背景
最近有一个业务使用了SpringCloud Config Gateway 源代码如下,由于公司监控系统使用的是Servlet规范来实现的,而基于Weblfux的 Config Gateway 自然是不能使用的。同时业务方也希望可能使用到公司的监控系统。看来还是有点作用的,嘎嘎嘎。
然后引发了一个类加载问题,出错信息如下所示.
处理问题
监控SpringCloud gw的时候,我会根据当前上下文是否存在GlobalFilter来判断,它是SpringCloud gw环境还是符合Servlet规范的环境,并因此写了一个SpringCloud相关的Conditional
public class SpringCloudCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
final boolean present = ClassUtils.isPresent("org.springframework.cloud.gateway.filter.GlobalFilter", this.getClass().getClassLoader());
return new ConditionOutcome(present, "none get properties");
}
}
当时这么写完以后,我就发布到线上了,然后业务方也验证通过了。
但是到了晚上发布的时候就出现了开始图中的堆栈。 而出现这个故障的原因也很简单,我原以为的预期是加了Conditonal以后,基于Servlet规范的SpringBoot项目就不会去加载这个Bean了,而实际上,在调用 xxx.class.getDeclaredMethods
的时候会触发一次 SpringCloudFilter
的类加载,而该 Filter
实现了 Config Gateway
的 GlobalFilter
,由此引发了 ClassNotFoundException
的案情。
基于此,我将这两个Bean移动到另外一个配置中,并且对另外配置的Class添加Conditonal,避免 getDeclaredMethods
的调用。
小结:
class加载发生在Spring#Conditional之后,而错误发生在class加载的时候。所以这种写法不能解决问题。
回顾
- new,设置或读取类静态字段 ,调用静态方法
- java.lang.reflect 进行反射会进行加载
- 常用反射的类加载
- 初始化类时父类会被初始化
- 启动执行并包含main方法的类
- 当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化。