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(),scannerClassPathBeanDefinitionScanner的一个实例

/**
* 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()

这里的refreshXml的容器中调用的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是留给子类实现的,基于注解的AnnotationConfigApplicationContextClassPathXmlApplicationContext是不一样的。

具体就是调用refresh方法多次,AnnotationConfigApplicationContext类的BeanFactory始终都是同一个,不会重新创建,但是ClassPathXmlApplicationContext会重新创建

Spring IOC-基于注解配置的容器的更多相关文章

  1. Spring IoC — 基于注解的配置

    基于XML的配置,Bean定义信息和Bean实现类本身是分离的,而采用基于注解的配置方式时,Bean定义信息即通过在Bean实现类上标注注解实现. @Component:对类进行标注,Spring容器 ...

  2. spring mvc 基于注解 配置默认 handlermapping

    spring mvc 是类似于 Struts 的框架.他们都有一个最主要的功能就是URL路由.URL路由能将请求与响应请求处理逻辑的类(在Struts中即是action,在spring mvc 中即是 ...

  3. 基于注解配置spring

    1 对 bean 的标注基于注解方式有3个注解 @Component @Repository 对DAO类进行标注 @Service 对Service类进行标注 @Controller  对Contro ...

  4. Unit03: Spring Web MVC简介 、 基于XML配置的MVC应用 、 基于注解配置的MVC应用

    Unit03: Spring Web MVC简介 . 基于XML配置的MVC应用 . 基于注解配置的MVC应用 springmvc (1)springmvc是什么? 是一个mvc框架,用来简化基于mv ...

  5. 【Spring Framework】Spring入门教程(二)基于xml配置对象容器

    基于xml配置对象容器--xml 标签说明 alias标签 作用:为已配置的bean设置别名 --applicationContext.xml配置文件 <?xml version="1 ...

  6. Spring boot 基于注解方式配置datasource

    Spring boot 基于注解方式配置datasource 编辑 ​ Xml配置 我们先来回顾下,使用xml配置数据源. 步骤: 先加载数据库相关配置文件; 配置数据源; 配置sqlSessionF ...

  7. Spring:基于注解的Spring MVC

    什么是Spring MVC Spring MVC框架是一个MVC框架,通过实现Model-View-Controller模式来很好地将数据.业务与展现进行分离.从这样一个角度来说,Spring MVC ...

  8. Spring IOC源代码具体解释之容器初始化

    Spring IOC源代码具体解释之容器初始化 上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比較典型的代码 ClassPathResource res = new C ...

  9. Spring IOC源代码具体解释之容器依赖注入

    Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...

随机推荐

  1. Hadoop常用命令及基本概念

    HADOOP 是什么? 分布式计算开源框架,其核心组件为:HDFS.MAPREDUCE.YARN Hadoop各个功能模块的理解 1. HDFS模块 HDFS负责大数据的存储,通过将大文件分块后进行分 ...

  2. python 之 pip、pypdf2 安装与卸载

    pip是个啥? pip 是一个现代的,通用的 Python 包管理工具.提供了对 Python 包的查找.下载.安装.卸载的功能. 第一步:pip 下载:https://pypi.org/projec ...

  3. etcd的raft选取机制

    etcd 是一个分布式的k/V存储系统.核心使用了RAFT分布式一致性协议.一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服务器可能会崩溃或变得不可靠, ...

  4. Static 静态+this

    (一):静态 1.Static修饰的都是静态的,都是类相关的,不需要new对象,直接采用类名.的方式访问 2.当一个属性是类级别的,所有对象的这个属性都是一样的,直接定义为静态 类=属性+方法 属性描 ...

  5. 【Java常用类】BigDecimal

    BigDecimal 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中, 要求数字精度比较高,故用到java.math.BigDecimal类. BigDecimal类支 ...

  6. 【SpringCloud技术专题】「Gateway网关系列」(3)微服务网关服务的Gateway全流程开发实践指南(2.2.X)

    开发指南须知 本次实践主要在版本:2.2.0.BUILD-SNAPSHOT上进行构建,这个项目提供了构建在Spring生态系统之上API网关. Spring Cloud Gateway的介绍 Spri ...

  7. dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析

    问题描述&模拟 线上登录接口,通过监控查看,有类型转换异常,具体报错如下图 此报错信息是dubbo consumer端显示,且登录大部分是正常,有少量部分会报类型转换异常,同事通过更换方法名+ ...

  8. 《剑指offer》面试题11. 旋转数组的最小数字

    问题描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的 ...

  9. 【刷题-LeetCode】275. H-Index II

    H-Index II Given an array of citations sorted in ascending order (each citation is a non-negative in ...

  10. Sentry 开发者贡献指南 - 浏览器 SDK 集成测试

    Sentry 的浏览器 SDK 的集成测试在内部使用 Playwright.这些测试在 Chromium.Firefox 和 Webkit 的最新稳定版本上运行. https://playwright ...