我们发现:内部定义的class都是带internal的
- ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor处理器,BeanDefinitionRegistryPostProcessor的处理方法能处理@Configuration等注解。ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法内部处理@Configuration,@Import,@ImportResource和类内部的@Bean。
ConfigurationClassPostProcessor类继承了BeanDefinitionRegistryPostProcessor。BeanDefinitionRegistryPostProcessor类继承了BeanFactoryPostProcessor。
通过BeanDefinitionRegistryPostProcessor可以创建一个特别后置处理器来将BeanDefinition添加到BeanDefinitionRegistry中。它和BeanPostProcessor不同,BeanPostProcessor只是在Bean初始化的时候有个钩子让我们加入一些自定义操作;而BeanDefinitionRegistryPostProcessor可以让我们在BeanDefinition中添加一些自定义操作。在Mybatis与Spring的整合中,就利用到了BeanDefinitionRegistryPostProcessor来对Mapper的BeanDefinition进行了后置的自定义处理。
- AutowiredAnnotationBeanPostProcessor是用来处理@Autowired注解和@Value注解的
- RequiredAnnotationBeanPostProcessor这是用来处理@Required注解
- CommonAnnotationBeanPostProcessor提供对JSR-250规范注解的支持@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支持。
- EventListenerMethodProcessor提供@PersistenceContext的支持。
- EventListenerMethodProcessor提供@ EventListener 的支持。@ EventListener实在spring4.2之后出现的,可以在一个Bean的方法上使用@EventListener注解来自动注册一个ApplicationListener。
到此AnnotatedBeanDefinitionReader初始化完毕。
总结一下,AnnotatedBeanDefinitionReade读取器用来加载class类型的配置,在它初始化的时候,会预先注册一些BeanPostProcessor和BeanFactoryPostProcessor,这些处理器会在接下来的spring初始化流程中被调用
ClassPathBeanDefinitionScanner的初始化:
跟踪构造函数的重载,最终都到这里:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; //useDefaultFilters为true,所以此处一般都会执行 // 当然我们也可以设置为false,比如@ComponentScan里就可以设置为false,只扫描指定的注解/类等等 if (useDefaultFilters) { registerDefaultFilters(); } // 设置环境 setEnvironment(environment); // 详情如下: 这里resourceLoader传值,还是我们的工厂。否则为null setResourceLoader(resourceLoader); }
registerDefaultFilters()
protected void registerDefaultFilters() { // 这里需要注意,默认情况下都是添加了@Component这个注解的 //(相当于@Service @Controller @Respository等都会扫描,因为这些注解都属于@Component) 另外@Configuration也属于哦 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); //下面两个 是兼容JSR-250的@ManagedBean和330的@Named注解 try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } // 所以,如果你想Spring连你自定义的注解都扫描,自己实现一个AnnotationTypeFilter就可以啦 }
ClassPathBeanDefinitionScanner继承于ClassPathScanningCandidateComponentProvider,它内部维护有两个final类型的List:这两个对象在执行本类的scanCandidateComponents()方法时就会起作用。
//includeFilters中的就是满足过滤规则的 private final List<TypeFilter> includeFilters = new LinkedList<>(); //excludeFilters则是不满足过滤规则的 private final List<TypeFilter> excludeFilters = new LinkedList<>();
ClassPathScanningCandidateComponentProvider#setResourceLoader
为ResourcePatternResolver,MetadataReaderFactory和CandidateComponentsIndex
设定初始值
@Override public void setResourceLoader(@Nullable ResourceLoader resourceLoader) { this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader); this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); // Spring5以后才有这句,优化了bean扫描 this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader()); }
备注:若要使用Spring5 的这个功能,需要添加如下包。这样子当工程重新编译的时候(编译期),会在自动生成META-INF/spring-components。然后我们在启动用@ComponentScan扫描时,直接读取这个文件即可,极大的提高了Spring启动的速度。而这期间,可以使用Spring5.0最新提供的注解@Indexed来配合使用。
需要注意的是:这种方式也是存在缺陷的,具体缺陷请参考官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-index
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-indexer</artifactId> <version>5.0.7.RELEASE</version> <optional>true</optional> </dependency>
然后在运行后会生成一个 META-INF/spring.components 的文件,之后只要运行工程发现这个文件都会直接使用他。可以通过环境变量或工程根目录的spring.properties中设置spring.index.ignore=ture来禁用这个功能 (这个功能如果没有什么明确的需求,慎重使用,会提高工程的管理成本。)
- ResourcePatternResolver是一个接口,继承了ResourceLoader,可以用来获取Resource 实例。返回的实例为PathMatchingResourcePatternResolver类型
- MetadataReaderFactory用于解析资源信息对应的元数据,这里返回的实例为:CachingMetadataReaderFactory,带有缓存的
- CandidateComponentsIndexLoader.loadIndex () 方法是spring5.0以后加入的新特性,Spring Framework 5 改进了扫描和识别组件的方法,使大型项目的性能得到提升。(具体是通过编译器完成扫描,并且往本地写索引,然后启动的时候再去扫描索引即可的思路)
至此,ClassPathBeanDefinitionScanner初始化完毕,总结一下:
ClassPathBeanDefinitionScanner是一个扫描指定类路径中注解Bean定义的扫描器,在它初始化的时候,会初始化一些需要被扫描的注解,初始化用于加载包下的资源的Loader。
AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的初始化是spring上线文初始化的起点,很多预加载的类会在spring接下来的初始化中发挥重要作用
包扫描的启动方式
public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.fsx.config"); System.out.println(applicationContext.getBean(Parent.class)); //com.fsx.bean.Parent@639c2c1d }
启动函数为:
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); } public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); // 核心在这个scan方法里,见下面 this.scanner.scan(basePackages); }
下面就是重点看看doScan()方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); // 装载扫描到的Bean Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // 这个方法是最重点,把扫描到的Bean就放进来了(比如此处只有RootConfig一个Bean定义,是个配置类) // 这个是重点,会把该包下面所有的Bean都扫描进去。Spring5和一下的处理方式不一样哦~ Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // 拿到Scope元数据:此处为singleton ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // 生成Bean的名称,默认为首字母小写。此处为"rootConfig" String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 此处为扫描的Bean,为ScannedGenericBeanDefinition,所以肯定为true // 因此进来,执行postProcessBeanDefinition(对Bean定义信息做) 如下详解 // 注意:只是添加些默认的Bean定义信息,并不是执行后置处理器~~~ if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } // 显然,此处也是true 也是完善比如Bean上的一些注解信息:比如@Lazy、@Primary、@DependsOn、@Role、@Description @Role注解用于Bean的分类分组,没有太大的作用 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // 检查这个Bean 比如 //如果dao包(一般配置的basePakage是这个)下的类是符合mybaits要求的则向spring IOC容器中注册它的BeanDefinition 所以这步检查第三方Bean的时候有必要检查一下 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); //AnnotationConfigUtils类的applyScopedProxyMode方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // 注意 注意 注意:这里已经吧Bean注册进去工厂了,所有doScan()方法不接收返回值,也是没有任何问题的。。。。 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }