spring容器加载分析 一容器构造

目前的spring应用很少有使用XML进行配置的,springboot就是使用AnnotationConfigApplicationContext和AnnotationConfigEmbeddedWebApplicationContext作为非web应用和web应用的容器。所以就以AnnotationConfigApplicationContext为列来分析容器的加载过程。
构造方法:

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        // 构造函数
        this();
        // 向容器注册新的注解Bean
        register(annotatedClasses);
        // 刷新容器, 整个容器的加载都在这个方法中了
        refresh();
}

step 1、this()无参构造方法:

public AnnotationConfigApplicationContext() {
    // 初始化:基于注解的BeanDefinition读取器
    // this就是AnnotatedBeanDefinitionReader中的BeanDefinitionRegistry
    this.reader = new AnnotatedBeanDefinitionReader(this);
    // 初始化:基于路径的BeanDefinition扫描器
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

同时也调用了父类的无参构造方法:

public GenericApplicationContext() {
    // 实例化了默认的beanFactory
    this.beanFactory = new DefaultListableBeanFactory();
}

AnnotatedBeanDefinitionReader初始化方法:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)是一个关键的方法,在这个方法中向向容器中注册6个默认的BeanDefinition:
ConfigurationClassPostProcessor(实现自接口BeanFactoryPostProcessor)
AutowiredAnnotationBeanPostProcessor(实现自接口BeanPostProcessor)
RequiredAnnotationBeanPostProcessor(实现自接口BeanPostProcessor)
CommonAnnotationBeanPostProcessor(实现自接口BeanPostProcessor)
EventListenerMethodProcessor(实现自接口ApplicationContextAware)
DefaultEventListenerFactory(实现自接口EventListenerFactory)
这6个BeanDefinition对Spring至关重要,后面会有更详尽的阐述。

step 2、register(annotatedClasses)向容器注解新的annotatedClasses:
调用的是AnnotatedBeanDefinitionReader中的方法。

public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
    // 创建BeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    // 判断是否需要跳过(Condition注解)
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
        return;
    }
    // scopeMetadataResolver = AnnotationScopeMetadataResolver
    // 解析注解Bean定义的作用域,
    // 若@Scope("prototype"),则Bean为原型类型  
    // 若@Scope("singleton"),则Bean为单态类型  
    // 默认为singleton
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    // 注解Bean定义生成Bean名称
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    // 处理通用注解 如:Lazy DependsOn 等
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    // 解析限定符注解
    // 主要是配置的关于autowiring自动依赖注入装配的限定条件,即@Qualifier注解  
    // Spring的autowiring默认是按类型装配,如果使用@Qualifier则按名称 和@Resource一样
    if (qualifiers != null) {
        for (Class<? extends Annotation> qualifier : qualifiers) {
            // 如果配置了@Primary注解,设置该Bean为autowiring自动依赖注入装//配时的首选
            if (Primary.class == qualifier) {
                abd.setPrimary(true);
            }
            //如果配置了@Lazy注解,则设置该Bean为延迟初始化,否则则该Bean为预实例化  
            else if (Lazy.class == qualifier) {
                abd.setLazyInit(true);
            }
            else {
                // 如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一 个autowiring自动依赖注入装配限定符
                // 该Bean在进autowiring自动依赖注入装配时,根据名称装配限定符指定的Bean 
                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
            }
        }
    }
    // 创建bean持有类
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    // 根据注解Bean定义类中配置的作用域,创建BeanDefinition相应的代理对象  
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 注册BeanDefinition,这个方法中会注册所有的alias
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

annotatedClass已经注册到容器,但是还没有实例化。
this.conditionEvaluator.shouldSkip(abd.getMetadata())) 来判断是否满足Bean创建的条件,如果不满足则跳过。
一个Condition的示例:

/**
 * 条件:Linux操作系统
 */
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
         return context.getEnvironment().getProperty("os.name").contains("Linux");  
    }
}

使用方式:

@Configuration
@Conditional(LinuxCondition.class)
public class LinuxStartupConfig{
    ... ...
}

如果当前的操作系统不是Linux,那么LinuxStartupConfig这个Bean将不会创建。springboot提供的6个@ConditionalOnxxx就基于@Conditional注解和Condition接口实现的。

step 3、refresh() 容器刷新
所有Bean的加载和各种依赖的注入都在这个方法中实现的,之前先了解下两个接口BeanFactoryPostProcessor和BeanPostProcessor,它们都是Spring容器初始化时对外暴露的扩展点。

BeanFactoryPostProcesso接口:

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

只有一个方法postProcessBeanFactory(),它是在容器注册了Bean定义(BeanDefinition)之后,实例化之前执行的。通过这个接口可以获取Bean定义的元数据并且修改它们,如Bean的scope属性、property值等,也可以操作beanFactory。

BeanPostProcessor接口:

public interface BeanPostProcessor {
    // 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    // 实例化、依赖注入、初始化完毕时执行。 
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

BeanPostProcessor会在Bean实例化完毕后执行,所以任何BeanPostProcessor都是在BeanFactoryPostProcessor之后执行的。
BeanPostProcessor的作用可大了,比如自动注入、各种代理(AOP)等等,凡是对需要Bean进行增强操作的大都可以通过它来实现。

码字不易,转载请保留原文连接//www.greatytc.com/p/86b53e3f0264

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349