自Spring2.5以后引入的注解方法,为我们的开发带来了更多的便捷,至于使用,这里不打算说明,因为太多了……,本次主要想讲讲Spring提供给我们使用的注解(@Controller,@Component等等)其背后的细节,也讨论讨论中间贯穿的一些Spring知识。
开始讨论之前,你可能需要对Spring IOC有一些比较深的理解,我觉得,Spring IOC基本上是其它东西的基础,也是核心所在,有必要深入再深入。
Spring中的应用上下文ApplicationContext我们都不陌生,无论是Web还是非Web环境都需要使用到,只是使用的具体实现可能不同,在ApplicationContext的继承体系中,有一条分支是跟注解有关的,AnnotationConfigWebApplicationContext与AnnotationConfigApplicationContext,从名字就可以看出,前者用于Web应用,后者用于桌面应用,如果你只是一直在使用Spring,没有纠结与它是怎么做的,那么你可能不太注意到它们的存在,因为对于在Web中应用Spring注解的人,大抵是应用这两个注解,<context:annotation-config>与<context:component-scan>,很久以前论坛上还很多了问,配置了<context:component-scan>还需要配<context:annotation-config>吗?
在接下来的解析中,你会发现这两个标签加上前面的注解ApplicationContext背后的机制都是大抵一致的:
(1). 扫描配置的base-package下的所有bean(<context:annotation-config>没有做)
(2). 通过AnnotationConfigUtils注册相关的BeanPostProcessor。(核心,这些BeanPostProcessor负责对应的注解的解析与注入)
其中(2)将是注解实施的核心,先来分析各个应用方法都是如何进入到(2)的,ApplicationContext只以Web类型的AnnotationConfigWebApplicationContext为例,在解析Bean的过程中,系统会调用其loadBeanDefinitions()加载Bean定义信息(详见IOC的解析):
- protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
- AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(beanFactory); //负责注册bean
- reader.setEnvironment(this.getEnvironment());
- ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory); //扫描包中的bean
- scanner.setEnvironment(this.getEnvironment());
- if (!ObjectUtils.isEmpty(this.annotatedClasses)) {
- ……
- reader.register(this.annotatedClasses); //注册
- }
- if (!ObjectUtils.isEmpty(this.basePackages)) {
- ……
- scanner.scan(this.basePackages); //扫描包中的bean
- }
- String[] configLocations = getConfigLocations();
- if (configLocations != null) {
- for (String configLocation : configLocations) {
- try {
- Class<?> clazz = getClassLoader().loadClass(configLocation);
- if (logger.isInfoEnabled()) {
- logger.info("Successfully resolved class for [" + configLocation + "]");
- }
- reader.register(clazz); //注册
- }
- catch (ClassNotFoundException ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("Could not load class for config location [" + configLocation +
- "] - trying package scan. " + ex);
- }
- int count = scanner.scan(configLocation); //扫描
- ……
- }
- }
- }
- }
明显看到了(1)的影子,不断的扫描包中的Bean,注册Bean,但是明显不够,还需要第二部注册BeanPostProcessor来处理注解,实现的位置便是在scan中:
- public int scan(String... basePackages) {
- int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
- doScan(basePackages); //(1)扫描包中的类
- // Register annotation config processors, if necessary.
- if (this.includeAnnotationConfig) {
- AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); //注册各种注解类型对应的BeanPostProcessor
- }
- return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
- }
到了这里我们已经清楚了ApplicationContext中是如何处理的了,可惜在Web应用,我们一般使用的是XmlWebApplicationContext,之后再在配置文件中使用前面提到的两个标签配置注解,因此我们来看看其实现的原理,xml配置文件中<context>类型的配置由ContextNamespaceHandler处理:
- public class ContextNamespaceHandler extends NamespaceHandlerSupport {
- public void init() {
- registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
- registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
- registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
- registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
- registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
- registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
- registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
- registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
- }
- }
看到对应我们要分析的配置标签annotation-config与component-scan的Parser,首先看annotation-config的:
- public BeanDefinition parse(Element element, ParserContext parserContext) {
- Object source = parserContext.extractSource(element);
- //注册所有的相关BeanPostProcessor,(2)中的内容,后续分解
- Set processorDefinitions =
- AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
- //元素注册一个复合的Component,包含底下的PostProcessor Bean
- CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
- parserContext.pushContainingComponent(compDefinition);
- // 将processor都作为复合Component的嵌入Component
- for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
- parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
- }
- // 注册复合Component
- parserContext.popAndRegisterContainingComponent();
- return null;
- }
它把做任务都交给了BeanPostProcessor,具体每个BeanPostProcessor做了啥,后面分析,先继续来看看component-scan的Parser:
- public BeanDefinition parse(Element element, ParserContext parserContext) {
- String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
- ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
- // 扫描Bean定义并注册.
- ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
- Set beanDefinitions = scanner.doScan(basePackages);
- registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
- return null;
- }
看看我标注出来的三行代码,<1><2>便是创建scanner,通过它来扫描basepackage包下的Bean定义,这个在前面已经见识过了,<3>则是注册Bean,里面会有我们熟悉的注册BeanPostProcessor:
- protected void registerComponents(
- XmlReaderContext readerContext, Set beanDefinitions, Element element) {
- //老规矩,创建复合Component
- Object source = readerContext.extractSource(element);
- CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
- for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
- compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder)); //将前面扫描的Bean添加进入
- }
- // 如果必要,注册BeanPostProcessor.
- boolean annotationConfig = true;
- if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
- annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
- }
- if (annotationConfig) {
- //注册BeanPostProcessor
- Set processorDefinitions =
- AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
- for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
- compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
- }
- }
- readerContext.fireComponentRegistered(compositeDef);
- }
函数中先注册扫描的bean,之后根据需要决定是否添加BeanPostProcessor,这里就回到一开始的问题“配置了<context:component-scan>还需要配<context:annotation-config>吗? ”,在这里你会发现<context:component-scan>的Parser中会判断你是否配置了annotation-config属性,如果你配置了且设置为false,则它不会注册注解相关的BeanPostProcessor,相反则注册。之前说过BeanPostProcessor是具体处理Spring注解的地方,因此如果你使用了<context:component-scan>,只要你不主动将其annotation-config属性配置为false,则不需要再配置<context:annotation-config>了。
好了,最后的最后,我们终于来到了做实事的地方,看看Spring究竟注册了哪些BeanPostProcessor:
- public static Set registerAnnotationConfigProcessors(
- BeanDefinitionRegistry registry, Object source) {
- Set beanDefs = new LinkedHashSet(4);
- //处理@Configuration注解
- 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));
- }
- //处理自动注入的
- if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
- RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
- def.setSource(source);
- beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
- }
- if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
- RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
- def.setSource(source);
- beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
- }
- // 处理JSR-250相关注解的,如@Resource
- if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
- RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
- def.setSource(source);
- beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
- }
- //持久化相关,JPA支持
- if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
- RootBeanDefinition def = new RootBeanDefinition();
- try {
- ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
- def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
- }
- catch (ClassNotFoundException ex) {
- throw new IllegalStateException(
- "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
- }
- def.setSource(source);
- beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
- }
- return beanDefs;
- }
这里注册了好几个相关注解类型的BeanPostProcessor,其中可能最用的便是CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor了,有兴趣可以一个一个看看具体怎么操作。
本文深入探讨Spring框架中应用上下文ApplicationContext及其注解(如@Controller, @Component等)的工作原理,详细解析注解配置与上下文注册之间的关联,并深入分析Spring如何通过AnnotationConfigWebApplicationContext与AnnotationConfigApplicationContext实现注解解析与Bean注入。
1万+

被折叠的 条评论
为什么被折叠?



