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的元注解。

  • 相关阅读:
    【转】【SEE】基于SSE指令集的程序设计简介
    【转】【Asp.Net】asp.net服务器控件创建
    ControlTemplate in WPF ——ScrollBar
    ControlTemplate in WPF —— Menu
    ControlTemplate in WPF —— Expander
    ControlTemplate in WPF —— TreeView
    ControlTemplate in WPF —— ListBox
    ControlTemplate in WPF —— ComboBox
    ControlTemplate in WPF —— TextBox
    ControlTemplate in WPF —— RadioButton
  • 原文地址:https://www.cnblogs.com/xwgblog/p/11885790.html
Copyright © 2011-2022 走看看