Spring IOC-基于注解配置的容器
Spring中提供了基于注解来配置bean的容器,即AnnotationConfigApplicationContext
1. 开始
先看看在Spring家族中,AnnotationConfigApplicationContext在一个什么样的地位,看看继承图

可以看到Spring提供了基于Xml配置的容器之外,还提供了基于注解和Groovy的容器,今天我们来看看基于注解配置的容器
2. 方法窥探
看看AnnotationConfigApplicationContext中提供了哪些方法

3. 从构造方法开始
我们从构造方法开始,分析基于注解的容器,是如何获取BeanDefinition并注册beanDefinitionMap中的
public AnnotationConfigApplicationContext(String... basePackages) {
    this();
    scan(basePackages);
    refresh();
}
接下来一步一步分析下去
this()
调用了本类中的一个无参构造函数
public AnnotationConfigApplicationContext() {
    //注解bean读取器
    this.reader = new AnnotatedBeanDefinitionReader(this);
    //注解bean扫描器
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}
而AnnotationConfigApplicationContext继承自GenericApplicationContext,所以GenericApplicationContext的无参构造方法也会被调用
/**
	 * Create a new GenericApplicationContext.
	 * @see #registerBeanDefinition
	 * @see #refresh
	 */
public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}
可以看到父类拆功能键
scan(basePackages)
public void scan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    this.scanner.scan(basePackages);
}
调用了scanner.scan(),scanner是ClassPathBeanDefinitionScanner的一个实例
/**
	 * Perform a scan within the specified base packages.
	 * @param basePackages the packages to check for annotated classes
	 * @return number of beans registered
	 */
public int scan(String... basePackages) {
    // 原来的beanDefinition数量
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    doScan(basePackages);
	// 下面是注册配置处理器
    // 这个是啥呢,就是以前在xml中配置的<context:annotation-config>
    // 这里会注册四个注解处理器,分别是
    // AutowiredAnnotationBeanPostProcessor,
	// CommonAnnotationBeanPostProcessor
	// PersistenceAnnotationBeanPostProcessor
	// RequiredAnnotationBeanPostProcessor
    // 这四个都是BeanPostProccessor,在每个Bean创建的时候都会调用它们
    // 既然是注解处理器,他们处理什么注解呢?
    // AutowiredAnnotationBeanPostProcessor 处理@AutoWired注解
    // CommonAnnotationBeanPostProcessor 处理@ Resource 、@ PostConstruct、@ PreDestroy
    // PersistenceAnnotationBeanPostProcessor 处理@PersistenceContext
    // RequiredAnnotationBeanPostProcessor 处理@Required
    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
	// 返回本次扫描注册的beanDefinition数量
    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
这个ClassPathBeanDefinitionScanner是干什么的呢,通过查看源码注释
/ * A bean definition scanner that detects bean candidates on the classpath,
 * registering corresponding bean definitions with a given registry ({@code BeanFactory}
 * or {@code ApplicationContext}).
 *
 * <p>Candidate classes are detected through configurable type filters. The
 * default filters include classes that are annotated with Spring's
 * {@link org.springframework.stereotype.Component @Component},
 * {@link org.springframework.stereotype.Repository @Repository},
 * {@link org.springframework.stereotype.Service @Service}, or
 * {@link org.springframework.stereotype.Controller @Controller} stereotype.
 */
意思就是扫描类路径下的被@Component,@Repository,@Service,@Controller注解的的类,然后注册BeanDefinition到给定的BeanFactory
重点戏就在doScan()方法中
/**
	 * Perform a scan within the specified base packages,
	 * returning the registered bean definitions.
	 * 扫描指定的包,反正注册后的Bean Definition
	 * <p>This method does <i>not</i> register an annotation config processor
	 * but rather leaves this up to the caller.
	 * 这个方法不会注册注解处理器,而是留给调用者去做这件事
	 * @param basePackages the packages to check for annotated classes
	 * @return set of beans registered if any for tooling registration purposes (never {@code null})
	 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 遍历给定的packages
    for (String basePackage : basePackages) {
        // findCandidateComponents是获取一个包下的满足条件的类,下面会介绍
        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);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}
findCandidateComponents(String basePackage)
这个方法可以获取一个包下的满足条件的BeanDefinition
/**
	 * Scan the class path for candidate components.
	 * @param basePackage the package to check for annotated classes
	 * @return a corresponding Set of autodetected bean definitions
	 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    // 是否使用Filter,不扫描指定的包
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        // 扫描包
        return scanCandidateComponents(basePackage);
    }
}
这个scanCandidateComponents()里面就是获取资源判断是否满足条件,但是Spring判断的条件比较复杂,就先不看了
再回到doScan()方法里面:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
        // 遍历给定的packages
		for (String basePackage : basePackages) {
            // findCandidateComponents是获取一个包下的满足条件的类,下面会介绍
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
                // 绑定scope(解析@Scope)
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
                // 设置beanName
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
                ////检查beanName否存在
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
                    // 正式将BeanDefinition注入
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
registerBeanDefinition(definitionHolder,registry)
/**
	 * Register the specified bean with the given registry.
	 * <p>Can be overridden in subclasses, e.g. to adapt the registration
	 * process or to register further bean definitions for each scanned bean.
	 * @param definitionHolder the bean definition plus bean name for the bean
	 * @param registry the BeanDefinitionRegistry to register the bean with
	 */
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
/**
	 * Register the given bean definition with the given bean factory.
	 * @param definitionHolder the bean definition including name and aliases
	 * @param registry the bean factory to register with
	 * @throws BeanDefinitionStoreException if registration failed
	 */
public static void registerBeanDefinition(
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    throws BeanDefinitionStoreException {
    // Register bean definition under primary name.
    // 以主要名称
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    // Register aliases for bean name, if any.
    // 如果有别名,遍历别名注册到容器的aliasMap
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}
上面的registry.registerBeanDefinition()就是DefaultListableBeanFactory中的方法了
现在scan()方法已经走完了,回到构造方法中,还剩最后一个refresh()
refresh()
这里的refresh和Xml的容器中调用的refresh是同一个方法,都来自AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 记录启动时间,标记状态,检查变量
      prepareRefresh();
      // 初始化BeanFactory容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 添加BeanPostProcessor,手动注册几个特殊的 bean
      prepareBeanFactory(beanFactory);
      try {
         // 子类扩展点
         postProcessBeanFactory(beanFactory);
         // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);
         // 注册 BeanPostProcessor 的实现类
         registerBeanPostProcessors(beanFactory);
         // 初始化MessageSource
         initMessageSource();
         // 初始化事件广播器
         initApplicationEventMulticaster();
         // 子类扩展点
         onRefresh();
         // 注册事件监听器
         registerListeners();
         // 初始化所有的 singleton beans
         finishBeanFactoryInitialization(beanFactory);
         // 完成refresh(),发布广播事件
         finishRefresh();
      }
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         // 销毁已经初始化的的Bean
         destroyBeans();
         // 设置 active为false
         cancelRefresh(ex);
         throw ex;
      }
      finally {
         // 清除缓存
         resetCommonCaches();
      }
   }
}
这里也有一点不同就是第二步obtainFreshBeanFactory(),这个方法里面的调用getBeanFactory是留给子类实现的,基于注解的AnnotationConfigApplicationContext和ClassPathXmlApplicationContext是不一样的。
具体就是调用refresh方法多次,AnnotationConfigApplicationContext类的BeanFactory始终都是同一个,不会重新创建,但是ClassPathXmlApplicationContext会重新创建
Spring IOC-基于注解配置的容器的更多相关文章
- Spring IoC — 基于注解的配置
		
基于XML的配置,Bean定义信息和Bean实现类本身是分离的,而采用基于注解的配置方式时,Bean定义信息即通过在Bean实现类上标注注解实现. @Component:对类进行标注,Spring容器 ...
 - spring mvc 基于注解 配置默认 handlermapping
		
spring mvc 是类似于 Struts 的框架.他们都有一个最主要的功能就是URL路由.URL路由能将请求与响应请求处理逻辑的类(在Struts中即是action,在spring mvc 中即是 ...
 - 基于注解配置spring
		
1 对 bean 的标注基于注解方式有3个注解 @Component @Repository 对DAO类进行标注 @Service 对Service类进行标注 @Controller 对Contro ...
 - Unit03: Spring Web MVC简介 、 基于XML配置的MVC应用 、 基于注解配置的MVC应用
		
Unit03: Spring Web MVC简介 . 基于XML配置的MVC应用 . 基于注解配置的MVC应用 springmvc (1)springmvc是什么? 是一个mvc框架,用来简化基于mv ...
 - 【Spring Framework】Spring入门教程(二)基于xml配置对象容器
		
基于xml配置对象容器--xml 标签说明 alias标签 作用:为已配置的bean设置别名 --applicationContext.xml配置文件 <?xml version="1 ...
 - Spring boot 基于注解方式配置datasource
		
Spring boot 基于注解方式配置datasource 编辑  Xml配置 我们先来回顾下,使用xml配置数据源. 步骤: 先加载数据库相关配置文件; 配置数据源; 配置sqlSessionF ...
 - Spring:基于注解的Spring MVC
		
什么是Spring MVC Spring MVC框架是一个MVC框架,通过实现Model-View-Controller模式来很好地将数据.业务与展现进行分离.从这样一个角度来说,Spring MVC ...
 - Spring IOC源代码具体解释之容器初始化
		
Spring IOC源代码具体解释之容器初始化 上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比較典型的代码 ClassPathResource res = new C ...
 - Spring IOC源代码具体解释之容器依赖注入
		
Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...
 
随机推荐
- Docker_创建自定义镜像(5)
			
生成docker镜像有两种方式 使用已有容器生成镜像 使用dockerfile生成镜像 一.使用已有容器生成镜像 1.下载centos镜像,并创建容器 2.进入容器部署python环境 centos镜 ...
 - Python_使用smtplib+email完成邮件发送
			
本文以第三方QQ邮箱服务器演示如何使用python的smtplib+email完成邮箱发送功能 一.设置开启SMTP服务并获取授权码 开启QQ邮箱SMTP服务 开启的最后一步是发送短信验证,获取 au ...
 - centos7-collabora-office(在线文档编辑)
			
1.wget https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-centos7/repodata/repomd.xml.key &a ...
 - js- float类型相减 出现无限小数的问题
			
6.3 -1.1 是不是应该等于5.2? 但是js 会导致得出 5.19999999999的结果 怎么办?可以先先乘100 后相减,然是用方法 舍入为最接近的整数,然后再除于100, Math.rou ...
 - SQL高级优化系列
			
目录 SQL高级优化系列(一)之MySQL优化 SQL高级优化系列(二)之MySQL架构 SQL高级优化系列(三)之存储引擎 SQL高级优化系列(四)之SQL优化 SQL高级优化系列(五)之执行计划 ...
 - HDU 2044 一只小蜜蜂... (斐波那契数列)
			
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=2044 题目分析:其实仔细读题就会发现其中的规律, 其中:这是一个典型的斐波那契数列. 代码如下: #i ...
 - UVA 156 Ananagrams (STL multimap & set)
			
原题链接: http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=98&p ...
 - Rust 实现一个简单的区块链
			
一.背景 近期用 Rust 实现了 Jeiwan/blockchain_go,与原项目相比没有加入新的功能,只是换了一个编程语言实现了一遍,源码放在 Github 上. 开发这个项目,花费了好几个周末 ...
 - 今天太开心了,因为我知道了seastar框架
			
今天听说了一个新的C++语言开发的网络框架,叫做seastar. seastar有何特别之处呢?先看看官网提供的性能数据: 性能 HTTPD benchmark: cpu # request/sec ...
 - Java Selenide 介绍&使用
			
目录 Selenide 介绍 官方快速入门 元素定位 元素操作 浏览器操作 断言 常用配置 Selenide 和 Webdriver 对比 Selenide 介绍 Selenide github Se ...