Spring是怎么解析配置类的?
1、解析时机分析
解析前Spring做了什么?
注册配置类
在分析扫描时机之前我们先回顾下之前的代码,整个程序的入口如下:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); }
其中在this()空参构造中Spring实例化了两个对象,一个是AnnotatedBeanDefinitionReader,在上篇文章中已经介绍过了,另外一个是ClassPathBeanDefinitionScanner,在前文中也进行了详细的分析。
在完成这两个对象的创建后,Spring紧接着就利用第一步中创建的AnnotatedBeanDefinitionReader去将配置类注册到了容器中。看到这里不知道大家有没有一个疑问,既然Spring是直接通过这种方式来注册配置类,为什么我们还非要在配置类上添加@Configuration注解呢?按照这个代码的话,我不在配置类上添加任何注解,也能将配置类注册到容器中,例如下面这样:
public class Config { } public class Main { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); System.out.println(ac.getBean("config")); // 程序输出:com.spring.study.springfx.aop.Config@7b69c6ba // 意味着Config被注册到了容器中 } }
大家仔细想想我这个问题,不妨带着这些疑问继续往下看。
调用refresh方法
在将配置类注册到容器中后,Spring紧接着又调用了refresh方法,其源码如下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 这个方法主要做了以下几件事 // 1.记录容器的启动时间,并将容器状态更改为激活 // 2.调用initPropertySources()方法,主要用于web环境下初始化封装相关的web资源,比如将servletContext封装成为ServletContextPropertySource // 3.校验环境中必要的属性是否存在 // 4.提供了一个扩展点可以提前放入一些事件,当applicationEventMulticaster这个bean被注册到容器中后就直接发布事件 prepareRefresh(); // 实际上获取的就是一个DefaultListableBeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 为bean工厂设置一些属性 prepareBeanFactory(beanFactory); try { // 提供给子类复写的方法,允许子类在这一步对beanFactory做一些后置处理 postProcessBeanFactory(beanFactory); // 执行已经注册在容器中的bean工厂的后置处理器,在这里完成的扫描 invokeBeanFactoryPostProcessors(beanFactory); // 后面的代码跟扫描无关,我们在之后的文章再介绍 } // ..... } }
大部分的代码都写了很详细的注释,对于其中两个比较复杂的方法我们单独分析
1.prepareBeanFactory
2.invokeBeanFactoryPostProcessors
prepareBeanFactory做了什么?
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 设置classLoader,一般就是appClassLoader beanFactory.setBeanClassLoader(getClassLoader()); // 设置el表达式解析器 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 容器中添加一个属性编辑器注册表,关于属性编辑在《Spring官网阅读(十四)Spring中的BeanWrapper及类型转换》有过详细介绍,这里就不再赘述了 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // 添加了一个bean的后置处理器,用于执行xxxAware方法 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 对以下类型的依赖,不进行依赖检查,不进行依赖检查也就不会进行自动注入 beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // 为什么我们能直接将ApplicationContext等一些对象直接注入到bean中呢?就是下面这段代码的作用啦! // Spring在进行属性注入时会从resolvableDependencies的map中查找是否有对应类型的bean存在,如果有的话就直接注入,下面这段代码就是将对应的bean放入到resolvableDependencies这个map中 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 添加一个后置处理器,用于处理ApplicationListener beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 是否配置了LTW,也就是在类加载时期进行织入,一般都不会配置 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // 加载时期织入会配置一个临时的类加载器 beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 配置一些默认的环境相关的bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
上面这段代码整体来说还是非常简单的,逻辑也很清晰,就是在为beanFactory做一些配置,我们需要注意的是跟后置处理器相关的内容,可以看到在这一步一共注册了两个后置处理器
- ApplicationContextAwareProcessor,用于执行xxxAware接口中的方法
- ApplicationListenerDetector,保证监听器被添加到容器中
关于ApplicationListenerDetector请参考Spring官网阅读(八)容器的扩展点(三)(BeanPostProcessor)
invokeBeanFactoryPostProcessors做了什么?
这个方法的执行流程在Spring官网阅读(六)容器的扩展点(一)BeanFactoryPostProcessor 已经做过非常详细的分析了,其执行流程如下
整的来说,它就是将容器中已经注册的bean工厂的后置处理器按照一定的顺序进行执行。
那么到这一步为止,容器中已经有哪些bean工厂的后置处理器呢?
还记得我们在上篇文章中提到的ConfigurationClassPostProcessor吗?在创建AnnotatedBeanDefinitionReader的过程中它对应的BeanDefinition就被注册到容器中了。接下来我们就来分析ConfigurationClassPostProcessor这个类的源码
ConfigurationClassPostProcessor源码分析
它实现了BeanDefinitionRegistryPostProcessor,所以首先执行它的postProcessBeanDefinitionRegistry方法,其源码如下
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 生成一个注册表ID int registryId = System.identityHashCode(registry); //..... // 表明这个工厂已经经过了后置处理器了 this.registriesPostProcessed.add(registryId); // 从名字来看这个方法是再对配置类的bd进行处理 processConfigBeanDefinitions(registry); }
processConfigBeanDefinitions方法的代码很长,我们拆分一段段分析,先看第一段
第一段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // ========第一段代码======== List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); // 大家可以思考一个问题,当前容器中有哪些BeanDefinition呢? // 这个地方应该能获取到哪些名字? String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { // 根据名称获取到对应BeanDefinition BeanDefinition beanDef = registry.getBeanDefinition(beanName); // 省略日志打印 // 检查是否是配置类,在这里会将对应的bd标记为FullConfigurationClass或者LiteConfigurationClass else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { // 是配置类的话,将这个bd添加到configCandidates中 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // 没有配置类,直接返回 if (configCandidates.isEmpty()) { return; } // 根据@Order注解进行排序 configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // .....
上面这段代码有这么几个问题:
1.当前容器中有哪些BeanDefinition
如果你看过上篇文章的话应该知道,在创建AnnotatedBeanDefinitionReader对象的时候Spring已经往容器中注册了5个BeanDefinition,再加上注册的配置类,那么此时容器中应该存在6个BeanDefinition,我们可以打个断点验证
不出所料,确实是6个
2.checkConfigurationClassCandidate
代码如下:
public static boolean checkConfigurationClassCandidate( BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { String className = beanDef.getBeanClassName(); if (className == null || beanDef.getFactoryMethodName() != null) { return false; } // 下面这一段都是为了获取一个AnnotationMetadata // AnnotationMetadata包含了对应class上的注解元信息以及class元信息 AnnotationMetadata metadata; if (beanDef instanceof AnnotatedBeanDefinition && className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) { // 已经解析过了,比如注册的配置类就属于这种,直接从bd中获取 metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata(); } else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { // 拿到字节码重新解析获取到一个AnnotationMetadata Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass(); metadata = new StandardAnnotationMetadata(beanClass, true); } else { try { // class属性都没有,就根据className利用ASM字节码技术获取到这个AnnotationMetadata MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className); metadata = metadataReader.getAnnotationMetadata(); } catch (IOException ex) { return false; } } // 如果被@Configuration注解标注了,说明是一个FullConfigurationCandidate if (isFullConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); } // 如果被这些注解标注了,@Component,@ComponentScan,@Import,@ImportResource // 或者方法上有@Bean注解,那么就是一个LiteConfigurationCandidate // 也就是说你想把这个类当配置类使用,但是没有添加@Configuration注解 else if (isLiteConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); } else { return false; } // 解析@Order注解,用于排序 Integer order = getOrder(metadata); if (order != null) { beanDef.setAttribute(ORDER_ATTRIBUTE, order); } return true; }
第二段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // 第一段 // ..... SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; // beanName的生成策略,不重要 if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // 核心目的就是创建这个ConfigurationClassParser对象 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 第三段 }
这段代码核心目的就是为了创建一个ConfigurationClassParser,这个类主要用于后续的配置类的解析。
第三段
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // 第一段,第二段 // ..... Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { // 在第二段代码中创建了一个ConfigurationClassParser,这里就是使用这个parser来解析配置类 // 我们知道扫描就是通过@ComponentScan,@ComponentScans来完成的,那么不出意外必定是在这里完成的扫描 parser.parse(candidates); // 校验在解析过程是中是否发生错误,同时会校验@Configuration注解的类中的@Bean方法能否被复写(被final修饰或者访问权限为private都不能被复写),如果不能被复写会抛出异常,因为cglib代理要通过复写父类的方法来完成代理,后文会做详细介绍 parser.validate(); // 已经解析过的配置类 Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); // 移除已经解析过的配置类,防止重复加载了配置类中的bd configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 将通过解析@Bean,@Import等注解得到相关信息解析成bd被注入到容器中 this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); // 如果大于,说明容器中新增了一些bd,那么需要重新判断新增的bd是否是配置类,如果是配置类,需要再次解析 if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // 注册ImportRegistry到容器中 // 当通过@Import注解导入一个全配置类A(被@Configuration注解修饰的类),A可以实现ImportAware接口 // 通过这个Aware可以感知到是哪个类导入的A if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }