Spring-Context

spring-context模块笔记,该模块是Spring容器的核心部分,继承关系和结构也很复杂,值得细细看

核心接口和类

ApplicationContext:核心接口定义.继承了环境信息,工厂信息,事件发布,资源加载接口
AbstractApplicationContext:提供ApplicationContext的基本实现,最主要定义了refresh方法,也就是刷新流程定义,让子类重写钩子即可完成拓展,非常的灵活
AbstractRefreshableApplicationContext:提供了refreshBeanFactory()抽象方法的实现,使用DefaultListableBeanFactory为默认的BeanFactory,提供
loadBeanDefinitions抽象方法,让子类专注于通过各种方式加载BeanDefinition
AbstractXmlApplicationContext和ClassPathXmlApplicationContext:loadBeanDefinitions实现从classpath路径下载入xml解析BeanDefinition方式
AnnotationConfigWebApplicationContext:目前使用较多的容器对象,loadBeanDefinitions实现为扫描注解加载BeanDefinition

上面列出的接口和类都是容器中极其核心的部分

整体概览

Context可以理解为对BeanFactory的封装.包含BeanFactory选择/配置/Bean来源加载/定义容器启动流程,为其提供上下文以及丰富的拓展,这是直面使用者的接口.
主要的refresh方法在AbstractApplicationContext中实现,它定义了核心流程,包括初始化BeanFactory,加载BeanDefinition,触发BeanDefinitionRegistry以及BeanFactory的PostProcessor后置方法,支持重复刷新以及Context继承关系,发布容器刷新成功消息等,细节非常多.
子类的核心实现就是实现loadBeanDefinitions抽象方法,提供不同渠道加载BeanDefinition能力就是子类最大的不同,整体流程上是完全一样的.


功能流程接口与组件说明

ConfigurationClassPostProcessor:通过BeanFactoryPostProcessor引入了注解部分的实现流程
ConfigurationClassParser:配置注解@Configuration类的核心实现.以及自定义注解的流程处理 内部较复杂,大量嵌套,循环,跨方法递归.
AnnotationBeanNameGenerator:注解类的命名规则实现.如果使用注解指定名字则选取(如:@Component@ManagedBean@Named),否则回退到使用短名称类名
ApplicationContextInitializer:在refresh()前的回调方法,用于调整BeanFactory以及自定义配置

BeanDefinition解析相关

XML方式

BeanDefinitionDocumentReader:将xml定义的整个文档对象Document进行注册,这是外部接入的顶层接口,默认DefaultBeanDefinitionDocumentReader类实现.主要方法是parseBeanDefinitions,他会遍历所有子节点,如果有beans元素则进行初始递归
BeanDefinitionParserDelegate:核心解析类.包含所有的默认节点解析,以及自定义节点的空间映射委托处理
NamespaceHandler:自定义命名空间处理器.相当于一个空间的处理者.主要实现是NamespaceHandlerSupport.将BeanDefinitionParser进行管理.比如典型AopNamespaceHandler,MvcNamespaceHandler等都会将自己定义的节点管理起来,很多标签名都很眼熟
BeanDefinitionParser:自定义标签解析成BeanDefinition的实现.这个就太多了,主要是将节点信息转换成实际的BeanDefinition供后续加载

注解方式

ConfigurationClassParser:注解配置和核心启动类,负责定义完整注解配置解析流程
AnnotatedBeanDefinitionReader:注解bean的BeanDefinition解析接入层
ScannedGenericBeanDefinition:使用扫描class文件产生的BeanDefinition信息,它是通过访问者模式将BeanDefinition信息填充完整,可以不加载Class对象
AnnotatedGenericBeanDefinition:直接使用Class对象将BeanDefinition信息填充完成

Context中的Event事件管理

ApplicationEventMulticaster:事件分发器.主要是管理事件监听器的,对应就是被监听者
ApplicationEvent:事件.比如应用创建完成之后会推送借助事件分发器推送应用创建事件ContextStartedEvent
ApplicationListener:事件监听器.一般来说用的比较多的都是自己实现接口,然后监听想要处理的事件.
EventListenerMethodProcessor:将EventListener注解的方法转为ApplicationListenerMethodAdapter进而注册事件监听器
EventListenerFactory:事件监听工厂-对@EventListener注解返回ApplicationListenerMethodAdapter对象进行监听适配
ApplicationListenerDetector:将容器中实现了ApplicationListener的监听器注册到Context中以支持Context进行发布事件

Spring的事件管理还是挺好的,不过仅限在单应用中,如果希望跨应用传播事件,可以参考Spring Cloud Bus的思路,通过mq传播到整个服务中

按照包功能依次展开

Expression

StandardBeanExpressionResolver:提供SPEL部分的引入支持
主要配置了PropertyAccessor,如BeanExpressionContextAccessor,EnvironmentAccessor等
引入BeanFactoryResolver对beanName获取bean的支持,引入ConversionService支持

Support

里面有多种ApplicationContext的实现类.这些实现类一部分是因加载配置的方式不同而略有区别,比如说在classpath目录/自定义文件路径.另一部分是为了提供不同的方式载入配置,比如说xml/注解的形式开启.他们主要是重写了loadBeanDefinitions这个方法,然后以不同的方式载入BeanDefinition.

类层级以及主要实现类

AbstractApplicationContext-定义了整个刷新流程
--AbstractRefreshableApplicationContext-支持刷新,主要是刷新时清理已经创建的bean,绝大多数的Context的基类
----AbstractXmlApplicationContext-xml配置启动容器基类
------ClassPathXmlApplicationContext-在classpath目录下加载xml配置启动容器
------FileSystemXmlApplicationContext-以资源的形式独立加载xml配置启动容器
--GenericApplicationContext-不支持刷新的通用的上下文,所有的配置需要手工处理
----AnnotationConfigApplicationContext-支持注册带注解的配置类注册,目前使用非常多
----GenericXmlApplicationContext-支持xml路径参数解析配置类注册
----StaticApplicationContext-不支持外部配置注册,必须手工注册,一般测试用这个,这个是比较干净的容器,跑起来快
--AbstractRefreshableWebApplicationContext-web模块context的基类
----AnnotationConfigWebApplicationContext-以注解的形式开启web容器,目前使用非常多
----XmlWebApplicationContext-以xml配置的形式启动容器

主要的容器类就这些,使用比较多的都加黑了,由于现在的主流开发方式都是注解开发,因此注解启动容器上下文使用较多.

MessageSource

国际化支持提供了一些加载方式和存储特征
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
StaticMessageSource

Schedule

@EnableScheduling开启调度任务.引入SchedulingConfiguration配置,进而加载ScheduledAnnotationBeanPostProcessor将注解@Scheduled进行解析
@EnableAsync开启异步执行任务.引入AsyncConfigurationSelector配置,选择加载ProxyAsyncConfiguration或AspectJAsyncConfiguration配置类,进一步加载AsyncAnnotationBeanPostProcessor,这个AsyncAnnotationBeanPostProcessor实际上会对标有@Async的类和方法进行过滤(AsyncAnnotationAdvisor类实现),匹配到的方法就会产生AOP代理,进而异步执行(实际方法拦截器为AnnotationAsyncExecutionInterceptor)

ReschedulingRunnable-通过Trigger获取下个执行时间点,不断的触发task执行.这样Trigger就可以专注于获取下次执行时间,如CronTrigger可以专注表达式
TaskScheduler-任务管理器接口,主要实现有ConcurrentTaskScheduler并发任务管理器,其主要依赖ScheduledExecutorService线程池
ScheduledTaskRegistrar-主要便于管理各种Task,最终还是执行TaskScheduler调度方法.

Validation

@Validated:验证注解,放在类,方法,参数级别均可生效,等效于@Valid
MethodValidationPostProcessor:类级别的@Validated注解AOP生成,这里的AOP生成还是很通用的,如果需要自己写切面的话,可以学习下,值得借鉴
DataBinder:绑定器.允许对目标对象的属性设值,类型转换,验证,返回错误信息等操作.http请求的参数很多都是依托于DataBinder进行数据绑定和验证处理


常用注解说明

@Configuration:配置类声明,
@Bean:标注方法声明一个Bean.需要在@Configuration声明的类中使用
@ComponentScan和@ComponentScans:包路径扫描加载Bean声明.需要在@Configuration声明的类中使用
@Conditional:加载Bean的条件限制.
@DependsOn:指出依赖关系,让容器让制先加载依赖的bean.一般情况下非显示的引用依赖但是又依赖别的Bean初始化信息会选择这个注解
@Import:加载指定的配置类Class对象到容器中.其中Class类如果是ImportSelector和ImportBeanDefinitionRegistrar接口的实现类则会让使用者自定义,动态的依据参数选择导入配置类
@ImportResource:指加载xml的配置
@Lazy:将容器初始化时加载延迟到使用时加载
@Primary:指多个Bean同类型引用时按照类型注入选择一个标注此注解的Bean进行注入
@Profile:对@Conditional的一个实际运用.抽象出配置文件的概念
@PropertySource和@PropertySources:指加载指定的配置文件到环境中便于直接使用
@Role:定义Bean的角色,有些类加载限制为"基础设施"配置,这是框架内部分辨.它的默认值是BeanDefinition#ROLE_APPLICATION
@Scope:作用域.比如单例ConfigurableBeanFactory#SCOPE_SINGLETON
@EventListener:事件监听器方法注解.方便的处理监听方法,支持SPEL表达式条件处理监听方法
@EnableScheduling开启调度任务.配合@Scheduled注解标注调度任务方法
@EnableAsync开启异步执行.配合@Async注解标注异步对应的类和方法
@Validated验证注解,放在类,方法,参数级别均可生效
@EnableAspectJAutoProxy:引入AspectJAutoProxyRegistrar进而将类AnnotationAwareAspectJAutoProxyCreator注册,它会解析@Aspect注解

@Configuration类的核心实现流程

实现类为ConfigurationClassParser
1判断是否含有@Conditional注解,需满足条件注解方可加载
2对配置类进行向父类循环解析配置
3解析配置流程
3.1处理嵌套的内部@Configuration注解配置类.递归到步骤1进行处理
3.2处理@PropertySource和@PropertySources注解.向环境中添加对应的配置信息
3.3处理@ComponentScan和@ComponentScans注解.如果扫到到的配置中含有@Configuration注解,递归到1进行处理.实现通过ClassPathBeanDefinitionScanner类,这里大部分方法都是core包和beans包提供的对象.
3.4处理@Import注解
3.4.1如果导入类为ImportSelector实现类.取出导入类,递归自己
3.4.2如果导入类为ImportBeanDefinitionRegistrar实现类.先放起来,后面处理
3.4.3其他情况递归到步骤1处理
3.5处理@ImportResource注解
3.6处理@Bean注解对应的方法
3.7处理接口上的@Bean注解对应的方法
3.8返回父类供步骤2判断是否到Object对象

从@Configuration的解析过程可以看出通过注解的方式将对象加入到容器中的所有方式有以下几种
1@Import配置类(ImportSelector选择性导入,ImportBeanDefinitionRegistrar自定义导入(一般会进行自定义扫描))自定义加载Bean
2@Bean注解直接声明Bean
3@ComponentScan扫描注解载入Bean
4@ImportResource加载xml解析载入Bean
5@Configuration解析上述所有规则

所有的注解载入流程都是这套,无出其右(上述加载Bean都是指加载BeanDefinition,因为还没到初始化Bean的时刻)


附:refresh方法的主要流程,看注释说明

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 准备上下文环境
      prepareRefresh();

      // 让子类创建beanFactory,以及让子类添加一些自定义的BeanDefinition.这里对于框架来说进行了大量的拓展
     // 在刷新的初始阶段就要子类将BeanDefinition加载进来,子类一般继承AbstractRefreshableApplicationContext类,实现抽象方法loadBeanDefinitions进行加载BeanDefinition,常用的子类实现有ClassPathXmlApplicationContext(XML加载)和AnnotationConfigWebApplicationContext(注解加载)两种
     // 这里还需要加载一些自定义的类以及注册核心功能的BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor用来实现框架基础
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 自己调整beanFactory的一些配置,比如忽略接口,添加Classloader,添加一些默认的BeanPostProcessor,再注册一些默认提供的BeanDefinition对象,如Environment,SystemProperties
      prepareBeanFactory(beanFactory);

      try {
         // 让子类调整beanFactory
         postProcessBeanFactory(beanFactory);

         // 触发BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的beanFactory后置方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 将注册的BeanPostProcessor都取出来放到容器中,供BeanFactory在后续bean初始化前后回调
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         initMessageSource();
         // Initialize event multicaster for this context.
         initApplicationEventMulticaster();

         // 让子类加载自定义的一些特殊bean,比如web容器相关的
         onRefresh();

         // 从容器中取出来ApplicationListener监听器供后续使用
         registerListeners();

         // 可以开始实例化非延迟的bean了
         finishBeanFactoryInitialization(beanFactory);

         // 发布完成刷新事件以及清理部分缓存等
         finishRefresh();
      }
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // 将已创建的Bean都销毁掉
         destroyBeans();

         // 设置false标志
         cancelRefresh(ex);

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

推荐阅读更多精彩内容

  • 2.1 我们的理念是:让别人为你服务 IoC是随着近年来轻量级容器(Lightweight Container)的...
    好好学习Sun阅读 2,707评论 0 11
  • 本来是准备看一看Spring源码的。然后在知乎上看到来一个帖子,说有一群**自己连Spring官方文档都没有完全读...
    此鱼不得水阅读 6,928评论 4 21
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan阅读 4,139评论 2 7
  • 题目(状态DP) 丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则...
    Jfeng666阅读 395评论 0 0
  • 把最深的曾经 忘在空气 把最爱的誓言 刻入回忆 把最后的不舍 埋进心底 然后,披荆斩棘
    盛夏狂放々阅读 107评论 0 0