Spring IOC-基于注解配置的容器

算法踏风鹤
• 阅读 797

Spring中提供了基于注解来配置bean的容器,即AnnotationConfigApplicationContext

1. 开始

先看看在Spring家族中,AnnotationConfigApplicationContext在一个什么样的地位,看看继承图

Spring IOC-基于注解配置的容器

可以看到Spring提供了基于Xml配置的容器之外,还提供了基于注解和Groovy的容器,今天我们来看看基于注解配置的容器

2. 方法窥探

看看AnnotationConfigApplicationContext中提供了哪些方法

Spring IOC-基于注解配置的容器

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会重新创建

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
spring源码解析
前言上篇我们介绍了spring容器加载的方式,并重点介绍了基于xml配置解析和注解扫描两种容器加载的方式,封装和注册beandefinition的过程。今天我们分享BeanDefinition注册后的另一个重要过程bean的实例化过程的源码。容器加载流程!spring源码解析spring容器加载源码(bean实
Easter79 Easter79
3年前
spring注解
随着越来越多地使用Springboot敏捷开发,更多地使用注解配置Spring,而不是Spring的applicationContext.xml文件。Configuration注解:Spring解析为配置类,相当于spring配置文件Bean注解:容器注册Bean组件,默认id为方法名@Configurat
Stella981 Stella981
3年前
Spring Security(3):配置与自动配置的介绍及源码分析
基于注解的配置(JavaConfiguration)从SpringSecurity3.2开始就已经支持,本篇基于Springboot注解的配置进行讲解,如果需要基于XML配置(SecurityNamespaceConfiguration),可查阅SpringSecurity官网:https://docs.spring.io/springse
Easter79 Easter79
3年前
Spring全解系列
本文基于Spring5.2.x@Import注解@Import是Spring基于Java注解配置的主要组成部分。@Import注解提供了@Bean注解的功能,同时还有原来Spring基于xml配置文件里的<import标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的
Easter79 Easter79
3年前
Spring的@Import 注解的作用与用法
@Import注解@Import(https://my.oschina.net/u/3201731)是Spring基于Java注解配置的主要组成部分。@Import(https://my.oschina.net/u/3201731)注解提供了@Bean(https://my.oschina.net/bean
Easter79 Easter79
3年前
Spring面试题总结
1、Spring是什么?Spring是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的配置、基于注解的配置、基于java的配置。主要由以下几个模块组成:SpringCore:核心类库,提供IOC服务;
Easter79 Easter79
3年前
Spring高级应用之注入嵌套Bean
在Spring中,如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。和普通的Bean一样,使用<bean元素来定义嵌套的Bean,嵌套Bean只对它的外部的Bean有效,Spring容器无法直接访问嵌套的Bean,因此定义嵌套Bean也无需指定id属性。如下配置片段是一个嵌套Bean的示例:<bean id
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
算法踏风鹤
算法踏风鹤
Lv1
时间会融化掉所有的尖锐。
文章
3
粉丝
0
获赞
0