• Spring(十四)- Spring注解原理解析


    一、Spring注解原理解析

    1. 使用xml配置扫描组件的原理解析

    在这里插入图片描述

    使用@Component等注解配置完毕后,要配置组件扫描才能使注解生效

    ⚫ xml配置组件扫描:

    <context:component-scan base-package="com.itheima"/>
    
    • 1

    配置类配置组件扫描:

    @Configuration
    @ComponentScan("com.itheima")
    	public class AppConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    使用xml方式配置组件扫描,而component-scan是一个context命名空间下的自定义标签,所以要找到对应的命名空间处理器NamespaceHandler 和 解析器,查看spring-context包下的spring.handlers文件

    http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
    
    • 1

    查看 ContextNamespaceHandler类的 init方法

    public void init() {
    	this.registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
    }
    
    • 1
    • 2
    • 3

    关于自定义命名空间的原理在之前章节已经分析过 文章地址

    将ComponentScanBeanDefinitionParser进行了注册,对其源码进行跟踪,最终将标注的@Component的类,生成对应的BeanDefiition进行了注册

    public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
    	@Override
    	@Nullable
    	public BeanDefinition parse(Element element, ParserContext parserContext) {
    		String basePackage = element.getAttribute("base-package");
    		basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ",; \t\n");
    
    		// Actually scan for bean definitions and register them.
    		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    		// 重点:扫描"base-package"包下的所有加了@Component注解的bean,生成对应的BeanDefiition注册到BeanDefiitionMap中
    		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    
    		return null;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    核心:ClassPathBeanDefinitionScanner中的doScan方法

    public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    		for (String basePackage : basePackages) {
    			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    			for (BeanDefinition candidate : candidates) {
    				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    				candidate.setScope(scopeMetadata.getScopeName());
    				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    				if (candidate instanceof AbstractBeanDefinition) {
    					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    				}
    				if (candidate instanceof AnnotatedBeanDefinition) {
    					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    				}
    				if (checkCandidate(beanName, candidate)) {
    					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
    					definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    					beanDefinitions.add(definitionHolder);
    					// 重点:扫描"base-package"包下的所有加了@Component注解的bean,生成对应的BeanDefiition注册到BeanDefiitionMap中
    					registerBeanDefinition(definitionHolder, this.registry);
    				}
    			}
    		}
    		return beanDefinitions;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    在这里插入图片描述

    2. 使用配置类扫描组件的原理解析

    使用配置类配置组件扫描,AnnotationConfigApplicationContext容器在进行创建bean时,内部注册了几个Bean后处理器,从new AnnotationConfigApplicationContext()入手查看源码

    // 注解方式去加载Spring的核心配置类
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    
    • 1
    • 2

    AnnotationConfigApplicationContext源码:

    // 首先会执行AnnotationConfigApplicationContext构造方法
    public AnnotationConfigApplicationContext() {
    		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    		// 调用注解方式的BeanDefinitionReader
    		this.reader = new AnnotatedBeanDefinitionReader(this);
    		...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    		...
    		// 该工具类注册了各种关于注解配置的后处理器
    		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public abstract class AnnotationConfigUtils {
    	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
    		...
    		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    		// 向容器中注册ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、
    		// CommonAnnotationBeanPostProcessor、EventListenerMethodProcessor、DefaultEventListenerFactory后处理器
    		RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    		RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
    		RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
    		RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    		RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
    		...
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    这里主要分析ConfigurationClassPostProcessor后处理器,ConfigurationClassPostProcessor 实现了BeanDefinitionRegistryPostProcessor,而BeanDefinitionRegistryPostProcessor是一个bean工厂后处理器(BeanFactoryPostProcessor),可以向容器中注册BeanDefinition,所以ConfigurationClassPostProcessor 会重写postProcessBeanDefinitionRegistry方法向容器中注册BeanDefinition
    经过一系列源码调用,最终也指定到了ClassPathBeanDefinitionScanner 的doScan 方法(与xml方式终点一致)

    public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
    		@Override
    	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    		...
    		// 该方法内部最终会执行到ClassPathBeanDefinitionScanner的doScan方法
    		processConfigBeanDefinitions(registry);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    xml方式与注解方式扫描组件执行过程殊途同归,最终都是通过ClassPathBeanDefinitionScanner 的doScan方法,扫描"base-package"包下的所有加了@Component注解的bean,生成对应的BeanDefiition注册到BeanDefiitionMap中,后续就可以进入到Spring Bean的生命周期创建对应的Bean
    在这里插入图片描述

  • 相关阅读:
    18.5.4 分布式恢复
    K8S环境下研发如何本地调试?kt-connect使用详解
    华为云Stack的学习(四)
    基础SQL 函数
    初识 MySQL HeatWave
    【Proteus仿真】【STM32单片机】汽车尾灯控制设计
    linux java 环境变量配置
    Head First设计模式(阅读笔记)-04.工厂模式
    【华为机试真题 JAVA】输出指定字母在字符串的中的索引-100
    Redis源码解析-通信协议
  • 原文地址:https://blog.csdn.net/qq_36602071/article/details/127895277