sofastack / sofa-ark

SOFAArk is a light-weight,java based classloader isolation framework.
https://www.sofastack.tech/projects/sofa-boot/sofa-ark-readme/
Apache License 2.0
1.57k stars 500 forks source link

【bug】java.lang.IllegalArgumentException: Unable to instantiate org.springframework.boot.env.EnvironmentPostProcessor [com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor] #611

Closed zhengchangqing closed 1 year ago

zhengchangqing commented 1 year ago

Describe the bug

当宿主应用依赖多于业务应用且该依赖实现了SpringBoot的扩展点EnvironmentPostProcessor时,采用合并部署模式,业务应用启动报错。报错信息如下:

2023-02-17 17:57:19.288 ERROR 3364 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalArgumentException: Unable to instantiate org.springframework.boot.env.EnvironmentPostProcessor [com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor]
    at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:131) ~[spring-boot-2.6.6.jar:2.6.6]
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_73]
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_73]
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) ~[na:1.8.0_73]
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) ~[na:1.8.0_73]
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[na:1.8.0_73]
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_73]
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_73]
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) ~[na:1.8.0_73]
    at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:118) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.util.Instantiator.instantiate(Instantiator.java:103) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.env.ReflectionEnvironmentPostProcessorsFactory.getEnvironmentPostProcessors(ReflectionEnvironmentPostProcessorsFactory.java:72) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.getEnvironmentPostProcessors(EnvironmentPostProcessorApplicationListener.java:122) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:100) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) [spring-context-5.3.18.jar:5.3.18]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) [spring-context-5.3.18.jar:5.3.18]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) [spring-context-5.3.18.jar:5.3.18]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131) [spring-context-5.3.18.jar:5.3.18]
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:85) ~[spring-boot-2.6.6.jar:2.6.6]
    at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:66) ~[spring-boot-2.6.6.jar:2.6.6]

Steps to reproduce

宿主应用: sofa-ark-spring-guides 业务应用: spring-boot-ark-biz

① 宿主应用修改pom.xml:

<!-- 静态合并部署 -->
<dependency>
   <groupId>com.alipay.sofa</groupId>
   <artifactId>spring-boot-ark-biz</artifactId>
   <classifier>ark-biz</classifier>
   <version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.3.2</version>
</dependency>

② 宿主应用修改启动类: @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

③ 业务应用修改pom.xml:

<plugin>
   <groupId>com.alipay.sofa</groupId>
   <artifactId>sofa-ark-maven-plugin</artifactId>
   <version>2.1.1</version>
   <executions>
      <execution>
         <id>default-cli</id>
         <goals>
            <goal>repackage</goal>
         </goals>
      </execution>
   </executions>
   <configuration>
      <skipArkExecutable>true</skipArkExecutable>
      <outputDirectory>./target</outputDirectory>
      <bizName>spring-boot-ark-biz</bizName>
      <webContextPath>biz</webContextPath>
      <declaredMode>true</declaredMode>
      <attach>true</attach>
   </configuration>
</plugin>

Environment

penglinzhang commented 1 year ago

可以发一下更多的日志吗,instantiate这个后面应该有更具体的错误原因,我也出现过类似错误,已经可以自己手动解决了

yuanChina commented 1 year ago

我也出现了同样的问题,原因这个依赖是宿主的,但是biz本身没有这个依赖,但是biz是基于spring创建的,在初始化的时候会在EnvironmentPostProcessorApplicationListener把宿主对应的依赖类拿出来通过Class,forname进行加载.请问这个问题最后是怎么解决的? 难道要通过sofaboot进行spring上下文隔离? @penglinzhang @gaosaroma

lvjing2 commented 1 year ago

我也出现了同样的问题,原因这个依赖是宿主的,但是biz本身没有这个依赖,但是biz是基于spring创建的,在初始化的时候会在EnvironmentPostProcessorApplicationListener把宿主对应的依赖类拿出来通过Class,forname进行加载.请问这个问题最后是怎么解决的? 难道要通过sofaboot进行spring上下文隔离? @penglinzhang @gaosaroma

是这个问题导致的,因为 EnvironmentPostProcessorApplicationListener 的这段逻辑会从基座里搜索 EnvironpentPostProcessor,然后搜索出了 com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor,但是在初始化的时候会从模块里查找这个类,因为这个类在模块里没有声明过,所以模块是查找不到这个类的。

解决方法:模块里也声明下mybatis-plus-boot-starter这个依赖,pom 的scope 可以设置为 provided。这是启动的成功的截图

image

penglinzhang commented 1 year ago

我也出现了同样的问题,原因这个依赖是宿主的,但是biz本身没有这个依赖,但是biz是基于spring创建的,在初始化的时候会在EnvironmentPostProcessorApplicationListener把宿主对应的依赖类拿出来通过Class,forname进行加载.请问这个问题最后是怎么解决的? 难道要通过sofaboot进行spring上下文隔离? @penglinzhang @gaosaroma

是这个问题导致的,因为 EnvironmentPostProcessorApplicationListener 的这段逻辑导致 EnvironpentPostProcessor 只会从基座里搜索,然后搜索出了 com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor,但是在初始化的时候会从模块里查找这个类,因为这个类在模块里没有声明过,所以模块是查找不到这个类的。

解决方法:模块里也声明下mybatis-plus-boot-starter这个依赖,pom 的scope 可以设置为 provided。这是启动的成功的截图

image

这样修改是能解决现有的问题,但是master-biz里面的com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor,在非master的biz启动时也要进行处理,这个逻辑本身是不是有点问题呢?是否可以改成当启动非master的biz的时候,不必要的不处理

lvjing2 commented 1 year ago

如果有方案,可以提 proposal 或者 PR。

zhengchangqing commented 1 year ago

我也出现了同样的问题,原因这个依赖是宿主的,但是biz本身没有这个依赖,但是biz是基于spring创建的,在初始化的时候会在EnvironmentPostProcessorApplicationListener把宿主对应的依赖类拿出来通过Class,forname进行加载.请问这个问题最后是怎么解决的? 难道要通过sofaboot进行spring上下文隔离? @penglinzhang @gaosaroma

现在解决方案比较粗暴,就是把宿主应用的这个依赖在Biz应用中声明(虽然没啥用),然后scope为provided,可以暂时解决这个问题。

penglinzhang commented 1 year ago

我有一个方案,EnvironmentPostProcessorApplicationListener扫描EnvironmentPostProcessor是可以传入resourceLoader,而这个resourceLoader正是application.getResourceLoader(),我暂且改造启动类SpringApplication的resourceLoader为BizClassLoader(一般正是null)程序可以启动成功,至于有没有其他影响还不清楚,但初步看是能成的。

penglinzhang commented 1 year ago

我也出现了同样的问题,原因这个依赖是宿主的,但是biz本身没有这个依赖,但是biz是基于spring创建的,在初始化的时候会在EnvironmentPostProcessorApplicationListener把宿主对应的依赖类拿出来通过Class,forname进行加载.请问这个问题最后是怎么解决的? 难道要通过sofaboot进行spring上下文隔离? @penglinzhang @gaosaroma

现在解决方案比较粗暴,就是把宿主应用的这个依赖在Biz应用中声明(虽然没啥用),然后scope为provided,可以暂时解决这个问题。

我有一个方案,EnvironmentPostProcessorApplicationListener扫描EnvironmentPostProcessor是可以传入resourceLoader,而这个resourceLoader正是application.getResourceLoader(),我暂且改造启动类SpringApplication的resourceLoader为BizClassLoader(一般正是null)程序可以启动成功,至于有没有其他影响还不清楚,但初步看是能成的。

这样如何呢

lisu-barton commented 3 months ago

我的方法比较粗暴,直接自己给biz创建一个。 然后就不报错了~ 暂时没有发现什么影响!

image

lvjing2 commented 3 months ago

目前采用 @penglinzhang 提供的方法解决的: https://github.com/koupleless/samples/blob/main/springboot-samples/web/tomcat/biz2-web-single-host/src/main/java/com/alipay/sofa/web/biz2/Biz2Application.java

在模块main方法里指定 resourceLoader