Spring Boot实现了很多有⽤的条件注⼊,其中的实现让⼈感到困惑,因为如果类不存在的话,加载就ConditionalOnClass会抛出错误,其实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("/projects/test/target/classes/com/test/User.class");
//元数据解析器⼯⼚
CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
//元数据解析器
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
//解析出来的类元数据
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println(classMetadata);
}