springboot 从jar包中分离出lib包,实现lib包与项目代码分离

问题背景:

服务依赖的.jar包文件会和代码打包在一起,会导致最终打包好的文件特别大,部署的时候每次都需要上传大文件很麻烦,为了解决这个问题,需要将打包文件里面的lib包文件和classes文件分离开。

异常方案:

在springboot 项目中,为了方便jar包替换,把 jar 包中 BOOT-INF\lib 下面的 .jar 文件拷贝出来放在外部文件中

 

 使用 java -jar 的方式启动jar 包时,出现了如下异常:

F:\TbpsServer>C:/Java/jdk1.8.0_251/bin/java -Xms256m -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=256M -XX:NewRatio=3 -Dhostname=tbps01  -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9110 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false  -Djava.ext.dirs=C:/Java/jdk1.8.0_251/jre/lib/ext;./lib -jar tbps.jar
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0
Logging system failed to initialize using configuration from 'null'
java.lang.IllegalStateException: Logback configuration error detected:
ERROR in ch.qos.logback.core.pattern.parser.Compiler@32c4e8b2 - Failed to instantiate converter class [com.cfcc.tbps.util.LogDesensitizationUtil] for keyword [msg] ch.qos.logback.core.util.DynamicClassLoadingException: Failed to instantiate type com.cfcc.tbps.util.LogDesensitizationUtil

注意: 抛出一个logback 异常,项目中为了做日志脱敏, 自定义一个 logback 的 MessageConverter

自定义MessageConverter类:

package com.cfcc.tbps.util;
import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
public class LogDesensitizationUtil extends MessageConverter {
    @Override
    public String convert(ILoggingEvent event) {
        String oriLog = event.getFormattedMessage();
        return filterSensitive(oriLog);
    }
    private String filterSensitive(String oriLog){
        String sensString = "";
        if(oriLog.contains("<PayAcct>")){
            String payAcct = oriLog.substring(oriLog.indexOf("<PayAcct>")+9,oriLog.indexOf("</PayAcct>"));
            for (int i=0;i<payAcct.length();i++){
                sensString += "*";
            }
            oriLog = oriLog.replace(payAcct,sensString);
        }
        if(oriLog.contains("<TaxPayName>")){
            sensString = "";
            String payAcct = oriLog.substring(oriLog.indexOf("<TaxPayName>")+12,oriLog.indexOf("</TaxPayName>"));
            for (int i=0;i<payAcct.length();i++){
                sensString += "*";
            }
            oriLog = oriLog.replace(payAcct,sensString);
        }
        if(oriLog.contains("<HandOrgName>")){
            sensString = "";
            String payAcct = oriLog.substring(oriLog.indexOf("<HandOrgName>")+13,oriLog.indexOf("</HandOrgName>"));
            for (int i=0;i<payAcct.length();i++){
                sensString += "*";
            }
            oriLog = oriLog.replace(payAcct,sensString);
        }
        return oriLog;
    }
}

spring logback 配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 日志存放路径 -->
    <!-- jar包日志设置 -->
    <property name="log.path" value="logs" />
    <!-- war包日志路径设置
<property name="log.path" value="../logs" />-->
    <!-- 日志输出格式 %d表示日期时间,%thread表示线程名,¥-5level表示级别从左显示5个字符宽度
    %logger{20}表示logger名字最长50个字符,否则按照句点分割。%msg:日志信息 %n:换行符-->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
    <!--日志脱敏配置, conversionRule标签不能放到appender标签下面,可以挨着configuration或者在property标签下面
    conversionWord对应脱敏的日志内容,即%msg%中脱敏的内容,converterClass属性指向自定义脱敏类-->
    <conversionRule conversionWord="msg" converterClass="com.cfcc.tbps.util.LogDesensitizationUtil" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
   <!-- <file>${log.path}/TbpsServer.log</file>-->
        <!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志文件名格式 滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
            %i:当文件大小超过maxFileSize时,按照i进行文件滚动-->
<fileNamePattern>${log.path}/%d{yyyyMMdd}/TbpsServer.log.%i</fileNamePattern>    
            <!-- 当日志文件超过maxFileSize指定的大小时,格局上面提到的%i进行日志文件滚动,注意此处配置SizeBasedTriggeringPolicy
            是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy-->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>

<!-- 级别过滤 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 过滤的级别 -->
            <level>INFO</level>
            <!-- 匹配时的操作:接收(记录) -->
            <onMatch>ACCEPT</onMatch>
            <!-- 不匹配时的操作:接收,info中打印所有日志信息 -->
            <onMismatch>ACCEPT</onMismatch>
        </filter>
</appender>
     <!--配置logger,指向异步appender-->
    <logger name="com.cybermax.manager.log.printer" additivity="false" level="info">
        <appender-ref ref="ACTION_LOG_APPENDER_ASYNC"/>
    </logger>
    
<!-- 用户访问日志输出  -->
    <!--<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!– 按天回滚 daily –>
            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!– 日志最大的历史 60天 –>
            <maxHistory>60</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
        </encoder>
    </appender>-->
<!-- 系统模块日志级别控制  日志级别:TRACE,DEBUG,INFO,WARN,ERROR-->
<logger name="com.cfcc.tbps" level="info" />
<!-- Spring日志级别控制  -->
<logger name="org.springframework" level="warn" />

<!--系统操作日志-->
    <root level="info">
        <appender-ref ref="console" />
        <appender-ref ref="file_info" />
        <appender-ref ref="file_error" />
    </root>

</configuration>

启动命令

title TBPS
cd ..
rem 设置java环境变量
set JAVA_HOME=C:/Java/jdk1.8.0_251
set JAVA_JRE=%JAVA_HOME%/jre/lib/ext
rem 设置java虚拟机参数信息
set JAVA_OPPTIONS=-Xms256m -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=256M -XX:NewRatio=3 -Dhostname=tbps01
rem 开启jconsole系统监测
set JCONSOLE=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9110 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
rem  加载外部jar包
set JAVA_EXT=-Djava.ext.dirs=%JAVA_JRE%;./lib
%JAVA_HOME%/bin/java %JAVA_OPPTIONS% %JCONSOLE% %JAVA_EXT% -jar tbps.jar

springboot 类加载过程

Springboot 的类加载过程是AppClassLoader加载org目录下的所有类文件,再由JarLauncher创建LauncherClassLoader(父类加载器是APPClassLoader)作为默认的类加载器去加载BOOT/classes/ 和BOOT-INF/lib/中的类和第三方库,并运行start-class中的main方法启动springboot应用。

问题分析:

因为通过 -Djava.ext.dirs  方式指定引导类加载器的路径,这时第三方包(包括Logback )会被引导类加载器加载,BOOT-INF\class 下的类(项目类)会被 LauncherClassLoader类加载器加载。这时,在Spring 解析的logback-spring.xml 文件,创建LogDesensitizationUtil对象时,会导致父类加载器加载的类调用子类加载器加载的类,因为父类加载器加载的类不能调用子类加载器加载的类,会导致对象初始化异常。

解决办法:

 将logback 转换器的自定义实现类打成jar包,放到lib目录下,和第三方包一起被Extension ClassLoader 加载,问题解决。

 上述方式很繁琐,通过查阅资料,可通过maven的方式实现项目代码与第三方jar包分离

参考资料【Maven】Maven打jar包分离lib包_maven打包lib分离_你怎么不笑了的博客-CSDN博客

Maven打包分离lib包

步骤一: 先修改pom.xml文件,打包的时候将lib和classes分离

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>package</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <!-- lib依赖包输出目录,打包的时候不打进jar包里 -->
        <outputDirectory>${project.build.directory}/lib</outputDirectory>
        <excludeTransitive>false</excludeTransitive>
        <stripVersion>false</stripVersion>
        <includeScope>runtime</includeScope>
      </configuration>
    </execution>
  </executions>
</plugin>
<!-- 压缩jar包,打出来的jar中没有了lib文件夹 -->
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <layout>ZIP</layout>
    <includes>
      <include>
        <groupId>nothing</groupId>
        <artifactId>nothing</artifactId>
      </include>
    </includes>
  </configuration>
</plugin>

步骤二:部署服务

将lib包脱离后的jar包是不可以直接通过java -jar xxx.jar的方式启动的,因为该jar包已经没有依赖包,没法去执行代码里面依赖的各种包的环境,

需要在启动的时候将lib包的路径也一并配置上。

nohup java -Dloader.path="lib/" -jar luan-account-web.jar &

注意:-Dloader.path 配置的就是打包后的整个lib文件夹上传到服务器的目录下所指的路径,通常lib不会经常改动,所以在第一次部署的时候,我们将lib包部署到服务器之后就可以不用动了,后期如果有新的依赖包,只需要将新的依赖包再单独上传到服务器的lib包里面就好了,然后改动业务的代码后,每次只需要将最新打包的服务jar包发布部署就好。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独行客-编码爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值