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类