Spring Boot实现了很多有用的条件注入,其中
ConditionalOnClass
的实现让人感到困惑,因为如果类不存在的话,加载就会抛出错误NoClassDefFoundError
。其实Spring Boot使用的字节码技术来实现这一点的
实现原理
Spring在加载类之前,会提前使用字节码技术来读取这个类(并没有使用ClassLoader),然后解析里面的ConditionalOnClass
,再在classpath下找到对应的类,如果找到就注入,否则就不注入
最终是否满条件的逻辑
- 匹配的方法:
SpringBootCondition#matches(ConditionContext, AnnotatedTypeMetadata)
其中AnnotatedTypeMetadata
就会通过字节码技术解析得到的注解信息,可以通过断点的方式,可以跟踪注解信息是如何从字节码解析得到的
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
try {
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "
+ "that class. This can also happen if you are "
+ "@ComponentScanning a springframework package (e.g. if you "
+ "put a @ComponentScan in the default package by mistake)", ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}
关键路径
通过跟踪Spring解析流程,可以得到以下字节码解析类信息的路径如下。
public static void main(String[] args) throws Exception {
FileSystemResource resource = new FileSystemResource("/Users/tenmao/Workspace/boot/tenmao-cond/target/classes/com/tenmao/cond/UserManager.class");
//元数据解析器工厂
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
//元数据解析器
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
//解析出来的类元数据
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println(classMetadata);
}
注意:整个过程程序并没有使用ClassLoader加载UserManager
类
网友评论