springboot jar包瘦身后启动提示 IllegalAccessError ?

问题描述

springboot jar包瘦身后,java -jar启动 报错

Caused by: java.lang.IllegalAccessError: class org.springframework.cloud.openfeign.HystrixTargeter$$EnhancerBySpringCGLIB$$7e887a8a cannot access its superclass org.springframework.cloud.openfeign.HystrixTargeter

at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_333]

at java.lang.ClassLoader.defineClass(ClassLoader.java:756) ~[na:1.8.0_333]

at sun.reflect.GeneratedMethodAccessor28.invoke(Unknown Source) ~[na:na]

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_333]

at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_333]

at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:535) ~[spring-core-5.2.8.RELEASE.jar:5.2.8.RELEASE]

... 130 common frames omitted

依赖版本

    <springboot.version>2.3.2.RELEASE</springboot.version>

<spring-cloud.version>Hoxton.SR9</spring-cloud.version>

<spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>

问题出现的环境背景及自己尝试过哪些方法

原先打包是通过下面spring-boot插件打包,打包后能正常java -jar运行

            <plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

<configuration>

<outputDirectory>${boot-jar-output}</outputDirectory>

</configuration>

<executions>

<execution>

<goals>

<goal>repackage</goal>

</goals>

</execution>

</executions>

</plugin>

后面需求变动,需要将打包的jar中的依赖库全部提出来,方便每次服务发布,减少传输的jar包大小。于是通过以下mvn插件来进行了配置

                <!-- Spring Boot模块jar构建 -->

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

<configuration>

<includes>

<include>

<groupId>null</groupId>

<artifactId>null</artifactId>

</include>

</includes>

<outputDirectory>${boot-jar-output}</outputDirectory>

<mainClass>com.bdip.cost.CostApplication</mainClass>

</configuration>

<executions>

<execution>

<goals>

<goal>repackage</goal>

</goals>

</execution>

</executions>

</plugin>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-jar-plugin</artifactId>

<configuration>

<archive>

<manifest>

<addClasspath>true</addClasspath>

<classpathPrefix>lib/</classpathPrefix>

<useUniqueVersions>false</useUniqueVersions>

</manifest>

</archive>

</configuration>

</plugin>

<!-- 拷贝项目所有依赖jar文件到构建lib目录下 -->

<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>

<outputDirectory>${boot-jar-output}/lib</outputDirectory>

<excludeTransitive>false</excludeTransitive>

<stripVersion>false</stripVersion>

<silent>false</silent>

</configuration>

</execution>

</executions>

</plugin>

插件的作用,主要就是将服务依赖的lib全部复制到jar包外面,并在打包的MANIFEST.MF文件中添加了Class-Path参数,指定lib的位置。

相关代码

现在通过 Java -ajr启动后报错,目前查到的原因是
在BeanPostProcessor#postProcessAfterInitialization的后置处理中,AspectJAwareAdvisorAutoProxyCreator创建代理类报错

    @Override

public Object postProcessAfterInitialization(Object bean, String beanName) {

if (this.advisor == null || bean instanceof AopInfrastructureBean) {

// Ignore AOP infrastructure such as scoped proxies.

return bean;

}

if (bean instanceof Advised) {

Advised advised = (Advised) bean;

if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {

// Add our local Advisor to the existing proxy's Advisor chain...

if (this.beforeExistingAdvisors) {

advised.addAdvisor(0, this.advisor);

}

else {

advised.addAdvisor(this.advisor);

}

return bean;

}

}

if (isEligible(bean, beanName)) {

ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);

if (!proxyFactory.isProxyTargetClass()) {

evaluateProxyInterfaces(bean.getClass(), proxyFactory);

}

proxyFactory.addAdvisor(this.advisor);

customizeProxyFactory(proxyFactory);

//报错就是这里,原因是 获取的classLoader和bean实际的classLoader不一致

//导致没有办法获取对应的父类

return proxyFactory.getProxy(getProxyClassLoader());

}

// No proxy needed.

return bean;

}

初步排查

初步排查是类加载器不一致导致proxy获取不到对应的类导致的,但是为什么导致不一致,目前还不清楚。
查询网络,有说是spring-boot-devtools导致的,可项目本身并没有依赖该jar,我个人怀疑是和
MANIFEST.MF的参数Class-Path导致的,但是我曾在别的项目上也这样配置过jar包瘦身,并没有什么问题。

临时解决

通过以下两个自定义在BeanPostProcessor,来还原classLoader解决,但是治标不治本呀。

@Component

//在AspectJAwareAdvisorAutoProxyCreator 之前

@Order(Ordered.LOWEST_PRECEDENCE+1)

public class FeignBeanPostProcessor implements BeanPostProcessor ,Ordered, ApplicationContextAware {

private ApplicationContext applicationContext;

// public static ThreadLocal<ClassLoader> originClassloader=new ThreadLocal<>();

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if ("feignTargeter".equals(beanName)){

ConfigurableApplicationContext ctx= (ConfigurableApplicationContext) applicationContext;

DefaultListableBeanFactory bf= (DefaultListableBeanFactory) ctx.getBeanFactory();

bf.getBeanPostProcessors().stream()

.filter(AspectJAwareAdvisorAutoProxyCreator.class::isInstance)

.forEach(item->

((AspectJAwareAdvisorAutoProxyCreator) item).setBeanClassLoader(bean.getClass().getClassLoader())

);

}

return bean;

}

@Override

public int getOrder() {

return Ordered.LOWEST_PRECEDENCE+1;

}

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext = applicationContext;

}

}

一个在AspectJAwareAdvisorAutoProxyCreator执行之前替换classLoader,一个在执行之后换回当前的classLoader

@Component

//在AspectJAwareAdvisorAutoProxyCreator 之后

public class FeignBeanPostProcessor2 implements BeanPostProcessor ,Ordered, ApplicationContextAware {

private ApplicationContext applicationContext;

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if ("feignTargeter".equals(beanName)){

ConfigurableApplicationContext ctx= (ConfigurableApplicationContext) applicationContext;

DefaultListableBeanFactory bf= (DefaultListableBeanFactory) ctx.getBeanFactory();

bf.getBeanPostProcessors().stream()

.filter(AspectJAwareAdvisorAutoProxyCreator.class::isInstance)

.forEach(item-> ((AspectJAwareAdvisorAutoProxyCreator) item).setBeanClassLoader(Thread.currentThread().getContextClassLoader()));

}

return bean;

}

@Override

public int getOrder() {

return Ordered.LOWEST_PRECEDENCE;

}

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext = applicationContext;

}

}

但是问题的根本原因还是不知道


回答:

去掉spring-boot-maven-plugin插件,在maven-dependency-plugin配置中添加main-class和outputdir配置。即可解决。

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-jar-plugin</artifactId>

<configuration>

<archive>

<manifest>

<addClasspath>true</addClasspath>

<classpathPrefix>lib/</classpathPrefix>

<useUniqueVersions>false</useUniqueVersions>

<mainClass>com.bdip.cost.CostApplication</mainClass>

</manifest>

</archive>

<!--指定输出jar目录-->

<outputDirectory>${boot-jar-output}</outputDirectory>

</configuration>

</plugin>

以上是 springboot jar包瘦身后启动提示 IllegalAccessError ? 的全部内容, 来源链接: utcz.com/p/945065.html

回到顶部