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定义文件的过程.这个过程 ...
随机推荐
- python 设计模式:单例模型
一.单例模型简介 代码的设计模式共有25种,不同的应用场景应用不同的设计模式,从而达到简化代码.利于扩展.提高性能等目的.本文简述Python实现的单例模式场景.简而言之,单例模式的应用场景是一个类对 ...
- 用一道题 来 复习 MySQL 的 复杂 sql 语句
1.前言 太久没有在数据库做一些复杂的sql了,基本上将数据库的查询逻辑全放在了Java里做, 一来呢,可以减轻数据库的负担,二来呢,在java写,逻辑感会更强,数据类型更丰富也容易操作. 然而... ...
- iframe 去除边框 背景透明等设置 待修改
<iframe name="file_frame" src="UploadFile.jsp" frameborder=no border=0 marg ...
- Windows 重装系统,配置 WSL,美化终端,部署 WebDAV 服务器,并备份系统分区
最新博客文章链接 最近发现我 Windows11 上的 WSL 打不开了,一直提示我虚拟化功能没有打开,但我看了下配置,发现虚拟化功能其实是开着的.然后试了各种方法,重装了好几次系统,我一个软件一个软 ...
- Docker的学习笔记(一)基础知识
概述 本人最近在学习docker相关的知识,既是工作本身的需要也是自己对技术的追求的必要,以后我也会推出容器相关的随笔,既可以增长自己的知识,也可以和读者广泛交流,岂不乐乎?话不多说.第一篇先介绍do ...
- 1121day-户别确认
1.Addinfor.jsp <%@ page language="java" contentType="text/html; charset=UTF-8" ...
- 【刷题-LeetCode】122 Best Time to Buy and Sell Stock II
Best Time to Buy and Sell Stock II Say you have an array for which the ith element is the price of a ...
- FHQtreap(我有个绝妙的理解方法,但课的时间不够[doge])
FHQtreap板子(P1486 [NOI2004] 郁闷的出纳员) 会了FHQ,treap什么的就忘了吧...... #include<bits/stdc++.h> using name ...
- iptables匹配条件总结1
源地址 -s选项除了指定单个IP,还可以一次指定多个,用"逗号"隔开即可 [root@web-1 ~]# iptables -I INPUT -s 172.16.0.116,172 ...
- 【数据结构】K-D Tree
K-D Tree 这东西是我入坑 ICPC 不久就听说过的数据结构,但是一直没去学 QAQ,终于在昨天去学了它.还是挺好理解的,而且也有用武之地. 目录 简介 建树过程 性质 操作 例题 简介 K-D ...