zoukankan      html  css  js  c++  java
  • Spring注解@Configuration是如何被处理的?

    从SpringApplication开始

    一般情况下启动SpringBoot都是新建一个类包含main方法,然后使用SpringApplication.run来启动程序:

    @SpringBootApplication
    public class AutoConfigApplication {
    
        public static void main(String[] args){
            ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(AutoConfigApplication.class,args);
        }
    }
    
    

    SpringApplication.run接收两个参数分别为:primarySource、运行参数(args),上面的代码使用AutoConfigApplication.class作为primarySource。SpringApplication还有一个实例方法也叫run,SpringBoot的大部分启动都由实例run方法来完成的,其中构造ApplicationContext由createApplicationContext方法完成:

    protected ConfigurableApplicationContext createApplicationContext() {
    		Class<?> contextClass = this.applicationContextClass;
    		if (contextClass == null) {
    			try {
    				switch (this.webApplicationType) {
    				case SERVLET:
    					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
    					break;
    				case REACTIVE:
    					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
    					break;
    				default:
    					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
    				}
    			}
    			catch (ClassNotFoundException ex) {
    				throw new IllegalStateException(
    						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
    			}
    		}
    		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    	}
    

    createApplicationContext根据this.webApplicationType来构造ApplicationContext,不同的环境都会使用不同的实例,但本文非web环境所有构造的时候会使用AnnotationConfigApplicationContext类。创建AnnotationConfigApplicationContext的时候会调用默认构造方法

    public AnnotationConfigApplicationContext() {
    	this.reader = new AnnotatedBeanDefinitionReader(this);
    	this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
    

    AnnotationConfigApplicationContext默认构造函数创建两个对象:

    • reader(AnnotatedBeanDefinitionReader):用于手动注册bean
    • scanner(ClassPathBeanDefinitionScanner): 用于扫描Component、Repository、Service等注解

    AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner会注册一些注解处理器,注册的方式都是使用AnnotationConfigUtils的registerAnnotationConfigProcessors方法

    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    		...
    		
    		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    		}
    		...
    		return beanDefs;
    	}
    

    最终AnnotationConfigApplicationContext构造方法执行完成后ApplicationContext会有以下BeanDefinition:

    构造完ApplicationContext后SpringApplicaiton紧接着会加载primarySource,上面提到 过primarySource是在运行的时候传递进来的(AutoConfigApplication.class),加载过程中不贴代码了,只要知道最终ApplicaitonContext中会多一个AutoConfigApplication的BeanDefinition:

    小结

    总的来说SpringApplicaiton主要干了这些事:

    • 创建AnnotationConfigApplicationContext
    • 加载一些处理注解的后处理器如:ConfigurationClassPostProcessor
    • primarySource加载进ApplicationContext

    最重要的一点是,现在是有一个AnnotationConfigApplicationContext里面包含了primarySource(AutoConfigApplication)以及ConfigurationClassPostProcessor。打个断点在ApplicaitonContext刷新之前打印下context中的bean的名称,可以确定这样说没毛病!

    @Configuration啥时候被解析?

    虽说有了primarySource和ConfigurationClassPostProcessor后处理器,还是需要有个执行的入口。ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,BeanDefinitionRegistryPostProcessor会在ApplicationContext的refresh操作时被处理:

    public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    		    ...
    			invokeBeanFactoryPostProcessors(beanFactory);
    			...
    		}
    }
    	
    public static void invokeBeanFactoryPostProcessors(
    			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
            
            ...
            //找出所有类型为BeanDefinitionRegistryPostProcessor的bean的名称
    	 	String[] postProcessorNames =
    					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    		for (String ppName : postProcessorNames) {
    			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    				processedBeans.add(ppName);
    			}
    		}
    		sortPostProcessors(currentRegistryProcessors, beanFactory);
    		registryProcessors.addAll(currentRegistryProcessors);
    		//执行BeanDefinitionRegistryPostProcessor
    		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    		...
    }
    
    private static void invokeBeanDefinitionRegistryPostProcessors(
    		Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
    
    	for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
    	    //调用postProcessBeanDefinitionRegistry方法
    		postProcessor.postProcessBeanDefinitionRegistry(registry);
    	}
    }	
    
    

    invokeBeanDefinitionRegistryPostProcessors会调用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,通过断点调试工具确认下ConfigurationClassPostProcessor有没有在这一步被处理:

    调试输出postProcessors集合里面有一个了ConfigurationClassPostProcessor元素,说明了ConfigurationClassPostProcessor的执行入口没有问题。

    ConfigurationClassPostProcessor处理器

    ConfigurationClassPostProcessor首先会判断在ApplicationContext中的bean是否被@Configuration注解标记,然后使用ConfigurationClassParser来解析@Configuration,ConfigurationClassPostProcessor的解析@Configuration的大致流程:

    1. 使用ConfigurationClassUtils.checkConfigurationClassCandidate检查BeanDefinition是否@Configuration注解标记
    2. 对@Configuration进行排序
    3. 使用ConfigurationClassParser解析@Configuration注解的信息
    4. 使用ConfigurationClassBeanDefinitionReader解析BeanDefinition
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    		
    		//获取所有BeanDefinitio名称
    		String[] candidateNames = registry.getBeanDefinitionNames();
            
    		for (String beanName : candidateNames) {
    			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
    			//如果是full、lite则说明已经处理过的类
    			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
    					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
    				if (logger.isDebugEnabled()) {
    					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
    				}
    			}
    			//检查BeanDefinition是否有@Configuration注解
    			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
    				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
    			}
    		}
    
    		//如果没有找到@Configuration标记的类,则返回不作处理也
    		if (configCandidates.isEmpty()) {
    			return;
    		}
    
    		//对@Configuration进行排序
    		configCandidates.sort((bd1, bd2) -> {
    			int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
    			int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
    			return Integer.compare(i1, i2);
    		});
    		
            ...
      
    		ConfigurationClassParser parser = new ConfigurationClassParser(
    				this.metadataReaderFactory, this.problemReporter, this.environment,
    				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
    		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    		do {
    		    //解析@Configuration class
    			parser.parse(candidates);
    			parser.validate();
    
    			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    			configClasses.removeAll(alreadyParsed);
    
    			//读取BeanDefinition
    			if (this.reader == null) {
    				this.reader = new ConfigurationClassBeanDefinitionReader(
    						registry, this.sourceExtractor, this.resourceLoader, this.environment,
    						this.importBeanNameGenerator, parser.getImportRegistry());
    			}
    			this.reader.loadBeanDefinitions(configClasses);
    			alreadyParsed.addAll(configClasses);
    
    			candidates.clear();
    		    ...
    		}
    		while (!candidates.isEmpty());
            ...
    	}
    

    最后还是通过调试工具看一下示例中的的启动类AutoConfigApplication没有被处理:

    图上显示configCandidates中有一个名称为autoConfigApplication的BeanDefinition的元素,说明AutoConfigApplication会被当作配置类解析,但是AutoConfigApplication并没有使用@Configuration注解,为什么还会被当做配置类呢?其实@Configuration在@SpringBootApplication注解中:

    红色背景列出来的就是@Configuration注解,它是@SpringBootConfiguration的元注解。

  • 相关阅读:
    go语言基本语法
    go语言学习
    Dockerfile简单实战
    Dockerfile构建命令
    docker安装prometheus grafana监控
    docker kali安装及复现永恒之蓝漏洞
    Windows系统实用快捷键
    python pip修改国内源
    网络基础——子网掩码及网络划分
    OSI七层模型简单概念及相关面试题
  • 原文地址:https://www.cnblogs.com/xwgblog/p/11885790.html
Copyright © 2011-2022 走看看