Spring是如何判断那些类需要管理的?

1,041 阅读2分钟

前言

先看段代码.

AnnotationConfigApplicationContext annotationConfigApplicationContext =
      new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.scan("org.springframework.test");
for (String beanDefinitionName : annotationConfigApplicationContext.getBeanDefinitionNames()) {
   System.out.println(beanDefinitionName);
}

对于这段代码,是不是有这样一个疑问,spring是怎么获取带有@Configuration之类的注解的类的呢?

步骤很多,首先就是获取指定包下的所有class,这部分是由PathMatchingResourcePatternResolver完成的,他是通过路径匹配模式来获取的,所以他不仅可以扫描class,还可以扫描其他文件,最终会返回Resource[]

比如要获取com.sp.test下所有的类,也包括子包,就可以这样写。

PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath:com/sp/test/**/*.class");

上面是第一步,获取到待扫描的class文件列表,这时候这些class有的需要被Spring管理,有的不需要,所以第二步是剔除不需要被管理的,对需要管理的对象进行下一步操作。

需要被管理的对象,在类上一定会有某个注解,所以Spring要做的是,找到带有这些注解的class,而这个注解,在Spring中默认有三个,Component、jakarta.annotation.ManagedBean、jakarta.inject.Named,而我们常用的Configuration等注解都是集成Component的。判断这部分位于源码中ClassPathScanningCandidateComponentProvider#isCandidateComponent

但问题就是,怎么获取class上是不是带有这些注解?你可能会想到Class.forName(),但是压根不行,他会被执行static代码块,所以不能使用这个。

还有就是ClassLoader,这种虽然不会执行static代码块,但可能是由于性能的原因,或者说是不合理,Spring没有使用,因为会导致JVM装载过多class。

那么剩下的就是粗暴的解析class结构了,这种方式困难很大,但是效率很高,而Spring就是使用这种方式,这部分是由源码中ClassReader负责的。

先看一个ClassReader的例子.

ClassReader classReader = new ClassReader(Files.readAllBytes(Paths.get("/home/HouXinLin/project/Test.class")));
System.out.println(classReader.getClassName());
System.out.println(classReader.getItemCount());
System.out.println(classReader.getSuperName());

但是不幸的是,不能直接获取其他信息,想要获取只能通过accept方法,这个方法会解析class,并回调给ClassVisitor对象,包括他所有的注解。

当判断这个class带有Component、jakarta.annotation.ManagedBean、jakarta.inject.Named后,还会进入Conditional注解,根据其中的信息判断这个对象到底用不用管理,这部分是由ConditionEvaluator类管理。`

@ManagedBean
@Conditional(value = {TestCondition.class})
public class Config {
   public void print() {
      System.out.println("test");
   }
}
public class TestCondition implements Condition {
   @Override
   public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
      return false;
   }
}

在matches下返回true,则表示这个类不需要被管理。