首页>行业>正文
环球快报:【Spring源码】- 01 Spring IoC容器启动之this方法
2023-03-28 13:21:26    来源:腾讯云

开始案例

1、定义两个Service Bean


【资料图】

package org.source.ioc.basic.demo02;public class TestService01 {}
import org.springframework.stereotype.Component;@Componentpublic class TestService02 {}
package org.source.ioc.basic.demo02;public class TestService03 {}

2、定义Configuration

package org.source.ioc.basic.demo02;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@Configuration@ComponentScan(basePackageClasses = {TestConfig.class})@Import(TestService03.class)public class TestConfig { @Bean public TestService01 testService01(){  return new TestService01(); }}

3、测试代码

public class MainDemo { @Test public void test01(){  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);  Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);  //Arrays.stream(context.getBeanDefinitionNames()).forEach(x -> System.out.println(x+"->"+context.getBean(x))); }}

输出结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.context.event.internalEventListenerProcessororg.springframework.context.event.internalEventListenerFactorytestConfigtestService02org.source.ioc.basic.demo02.TestService03testService01

通过上面的输出结果可以看出,三个TestService都被纳入了Spring IoC容器中,但却是通过三种不同方式实现的,@ComponentScan@Import@Bean,从输入结果来看,IoC容器中除了自定义的类外,还有几个非我们自定义的Bean,它们又是从哪里引入的、引入进来又有什么用呢?下面我们就通过源码方式分析下IoC的启动流程,看看IoC容器启动的背后到底隐藏了哪些玄机。

Reader

new AnnotationConfigApplicationContext(TestConfig.class)这一句测试代码就可以驱动IoC启动,非常的简单,下面我们就来研究下隐藏在这一句代码的背后Spring到底做了哪些工作。

public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh();}

这个构造方法代码非常简单,主要逻辑封装在三个方法中,首先我们来看下this()这个方法。

public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this);}

AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScannerSpring中两个非常重要的类。

首先,我们来看下AnnotatedBeanDefinitionReader这个类,@Configuration@Import@Autowired@Bean等等这些Spring中常用注解可以很神奇的为我们实现各种功能,注解本身是没有任何意义的,核心在于隐藏在这些注解背后的处理逻辑,AnnotatedBeanDefinitionReader就是这个隐藏在注解背后的处理逻辑,可以实现对Spring中常用注解的解析处理。

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; //条件比较器,用于处理@Condition注解 this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}

AnnotatedBeanDefinitionReader构造方法中最关键的是在最后一句代码,其源码核心见下:

public static Set registerAnnotationConfigProcessors(   BeanDefinitionRegistry registry, @Nullable Object source) {        ...//省略  Set beanDefs = new LinkedHashSet<>(8);  if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {   RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);   def.setSource(source);   beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));  }  if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {   RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);   def.setSource(source);   beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));  }  if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {   RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);   def.setSource(source);   beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));  }  if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {   RootBeanDefinition def = new RootBeanDefinition();   try {    def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,      AnnotationConfigUtils.class.getClassLoader()));   }   catch (ClassNotFoundException ex) {    throw new IllegalStateException(      "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);   }   def.setSource(source);   beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));  }  if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {   RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);   def.setSource(source);   beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));  }  if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {   RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);   def.setSource(source);   beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));  }  return beanDefs; }

其逻辑很简单,就是向IoC中注册一个BeanFactoryPostProcessor和5个BeanPostProcessorSpring就是通过这些PostProcessor扩展点实现对各种注解的解析、处理,让开发只需要简单的几个注解就可以实现很多复杂功能,屏蔽了注解背后处理的复杂逻辑,这也是目前Spring开发趋势:注解驱动开发。

这几个PostProcessor大致作用:

ConfigurationClassPostProcessor:完成@Configuration@Import@ComponentScan@Component@Bean等注解支持,该类主要完成完成BeanDefinition的采集工作,就是解析各种注解,把需要纳入Spring管理的Bean都采集到一起生成BeanDefinition存储起来,供后续生成对象提供所需的材料;AutowiredAnnotationBeanPostProcessor:完成@Autowired@Value注解支持,实现Spring中依赖注入的核心逻辑;CommonAnnotationBeanPostProcessor:支持JSR-250的一些注解,如:@Resource@PostConstruct@PreDestroy等;PersistenceAnnotationBeanPostProcessor:支持JPA中相关注解的支持;EventListenerMethodProcessorDefaultEventListenerFactory:这两个类主要完成对Spring4.2之后引入的@EventListener等事件注解支持;

上面六个PostProcessor中,最重要的是前两个,一个负责完成从各个地方把需要纳入IoC管理的Bean都收集到一起;另一个则完成对这些收集的Bean进行依赖注入。Spring IoC基本工作就是管理Bean以及依赖注入,所以IoC启动流程分析中,这两个类占有很大的比重。

Scanner

下面再来看下另一个非常重要的类:ClassPathBeanDefinitionScanner

Spring项目中配置@ComponentScan(basePackages="a.b.c"),这背后的工作就是靠ClassPathBeanDefinitionScanner完成,其主要就是完成对指定包路径下的Bean进行扫描,把含有特定注解的Bean生成BeanDefinition注册到IoC容器中。

下面通过一个Demo了解下ClassPathBeanDefinitionScanner基本使用:

@Testpublic void classPathBeanDefinitionScannerTest(){ String BASE_PACKAGE = "org.source.ioc.basic.demo02.scanner"; //1.创建一个IoC容器,用于装载ClassPathBeanDefinitionScanner扫描出的BeanDefinition SimpleBeanDefinitionRegistry registry= new SimpleBeanDefinitionRegistry(); /** * 2.创建一个Scanner扫描器,useDefaultFilters:是否使用默认过滤器,默认该值为true, * 即会把@Component注解的Bean都扫描出来,这里我们不需要这个功能,只需要扫描我们自定义注解的Bean */ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false); //3.这里注册一个注解类型过滤器,完成对自定义注解Bean过滤 scanner.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class)); /** * 4.是否向IoC中注册用于用于处理核心注解的6个PostProcessor,默认true * AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry) */ scanner.setIncludeAnnotationConfig(false); //5.上面工作都准备完成,调用scan(String... basePackages)即可对指定的包路径下的Bean扫描过滤,返回值是扫描出的Bean数量 int beanCount = scanner.scan(BASE_PACKAGE); //6.scan()方法会把符合要求的Bean生成BeanDefinition并注册到IoC容器中,我们就可以从IoC容器中获取到这些BeanDefinition String[] beanDefinitionNames = registry.getBeanDefinitionNames(); System.out.println("bean count:"+beanCount); Arrays.stream(beanDefinitionNames).forEach(System.out::println);}

上面案例就可以完成扫描org.source.ioc.basic.demo02.scanner包下的所有Bean,将含有@MyComponent注解的Bean生成对应的BeanDefinition,并注册到IoC容器中。

ClassPathBeanDefinitionScannerSpring中非常重要的一个类,决定了哪些类需要被纳入IoC容器。我们可以继承ClassPathBeanDefinitionScanner实现框架定制化功能,比如MyBatisMapper扫描就是一个典型应用案例,MyBatisMapperScannerConfigurer的内部就使用到一个ClassPathBeanDefinitionScanner的子类,实现将Mapper接口文件注入到IoC容器中。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) {      processPropertyPlaceHolders();    }    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);    scanner.setAddToConfig(this.addToConfig);    scanner.setAnnotationClass(this.annotationClass);    scanner.setMarkerInterface(this.markerInterface);    scanner.setSqlSessionFactory(this.sqlSessionFactory);    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);    scanner.setResourceLoader(this.applicationContext);    scanner.setBeanNameGenerator(this.nameGenerator);    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);    if (StringUtils.hasText(lazyInitialization)) {      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));    }    scanner.registerFilters();    scanner.scan(        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}

ClassPathMapperScanner继承ClassPathBeanDefinitionScanner,完成@MapperScan注解支持,将特定的Mapper类生成BeanDefinition注册到IoC容器中,这样我们才能通过@Autowired依赖注入Service类中。

MyBatisMapper类是一个接口,而依赖注入获取到的是一个对象,这是如何做到的?这里主要运用了动态代理功能,具体可以参见后续MyBatis Mapper实现原理分析。

注意:AnnotationConfigApplicationContext中定义的:this.scanner = new ClassPathBeanDefinitionScanner(this);,这个其实一般情况下是没有使用的,只有手工调用AnnotationConfigApplicationContext#scan()才会使用到scanner。大部分Bean的采集工作是AnnotatedBeanDefinitionReader中向IoC注册的ConfigurationClassPostProcessor这个BeanFactory后置处理器完成的,它在处理@ComponentScan注解时会重新创建一个ClassPathBeanDefinitionScanner实例,而不是使用AnnotationConfigApplicationContext.scanner这个,这里要特别注意下,避免修改却发现没有起作用的尴尬。

关键词:

环球快报:【Spring源码】- 01 Spring IoC容器启动之this方法

通过上面的输出结果可以看出,三个TestService都被纳入了SpringIoC容器中,但却是通过三种不同方式实现的,@ComponentScan、@Imp2023-03-28

当前头条:东方电热:接受中泰证券等机构调研

东方电热(SZ300217,最新价:6 08元)发布公告称,2023年3月24日14:00-16:30,东方电热接受中泰证券等机构调研,公司董事兼东方九天总经理朱晓2023-03-28

每日速读!开辟新领域制胜新赛道 长宁两民企跑出发展加速度

新时代的企业如何高质量发展?记者近日在长宁区采访时,不同领域的两家民营企业用相似的路径给出了答案:开辟新领域,制胜新赛道。2023-03-28

当前热议!和讯个股快报:2023年03月28日 三德科技 (300515),股价出现向上跳空缺口

受板块利好消息影响,该股快速拉升,出现向上跳空缺口,说明多方力量聚集,主力资金做多意愿明显。截至发稿,该股报16 92元 股,成交量0 000手2023-03-28

速读:3月27日基金净值:南方中证500ETF最新净值6.3297,涨0.01%

3月27日,南方中证500ETF最新单位净值为6 3297元,累计净值为2 0323元,较前一交易日上涨0 01%。历史数据显示该基金近1个月上涨0 05%,近3个月2023-03-28

环球动态:票价补贴最多30%,上海·静安现代戏剧谷将于4月20日开幕

记者杨舒鸿吉界面新闻2023年3月27日从上海市静安区获悉,2023上海·静安现代戏剧谷将于4月20日开幕。今年上海·静安现代戏剧谷的主题是“汇”2023-03-28

焦点简讯:电脑版gta3中文版下载资源_gta3下载电脑版

1、一般的飞机是进不去的只有少数飞机和任务里才能开飞机加速爬升倒退的键为都是在小数字键盘上的具体的分配也记不得了2023-03-28

环球今亮点!《第五人格》象牙塔系列「独特时装」“囚徒”

【三视图爆料】象牙塔系列「独特时装」“囚徒”-怪咖三视图在愚人的乐园里,天才总是格格不入象牙塔系列【独特时装】“囚徒”-怪咖将于4月6日2023-03-27

全球看点:辽宁校讯通家园_辽宁校讯通

1、因为很多的老师以及学生啊,家长啊都会进入校讯通,所以导致网络繁忙。2、进不去很正常,我也有很多次,老师让我发短信可进2023-03-27

当前速看:广湛高铁湛江湾海底隧道“穿海进城”

(梁盛 张朋飞)广湛高铁湛江湾海底隧道掘进工程完成“穿海”任务2023-03-27

全球通讯!科美诊断:获巨细胞病毒IgM抗体检测试剂盒医疗器械注册证

科美诊断3月27日公告,公司获得国家药品监督管理局颁发的巨细胞病毒IgM抗体(CMVIgM)检测试剂盒(光激化学发光法2023-03-27

环球速看:宠妃宠上天_宠妃上天冷绝相公娃娃妃

1、《宠妃上天》哇哩咧,她堂堂慕容集团第一顺位继承人睡个觉竟然都能玩儿穿越?这也未免太扯了吧…真是原是千金娇娇女,一2023-03-27

环球微头条丨上海去年常住人口为2475.89万人,60岁及以上占比25%

2022年人口变动情况抽样调查数据结果显示,2022年上海常住人口为2475 89万人。2023-03-27

今日讯!陕西省富县发布大雾黄色预警

富县气象台2023年03月27日07时28分发布大雾黄色预警信号:预计下述地区未来12小时内将出现能见度小于500米的雾:直罗镇、寺仙镇、张村驿镇、羊2023-03-27

环球快看:拒绝勇士逆转!4.3亿最贵双塔爆发,唐斯关键三分扎心库里

拒绝勇士逆转!4 3亿最贵双塔爆发,唐斯关键三分扎心库里,勇士,双塔,戈伯特,汤普森,卡尔唐斯,美国篮球,斯蒂芬·库里,明尼苏达森林狼队,卡尔-安2023-03-27

【世界时快讯】法治社会不容“按键伤人”

从社交媒体盗取照片,P出一张张不雅照,编造出子虚乌有的故事,肆意传播甚至倒卖……近日,网络平台出现恶意P图造“黄谣”事件。网络谣言给被2023-03-27

当前热点-补短板、学技术……推进乡村产业振兴各地这样做

2022年,我国农产品加工转化率达72%,网络零售额同比增长9 2%挖掘产业优势推进乡村振兴核心阅读建设冷链物流等基础设施,拓展农村电商等新兴业2023-03-27

【全球速看料】沧州城区三轮车限行吗?

【更新时间:2023年3月27日】沧州中心城区电动三轮车限行!限行安排如下:违规电动三、四轮车种类:违规电动三、四轮车是指未经工信部许可生产2023-03-27

天天播报:京西王者,中海长安誉用实力说话!

这个周末中海长安誉开盘非常火爆,大厅座无虚席,就连摆在墙边的备用座位都坐满了前来选房的客户,延续着中海在石景山的开盘即热销。现场每轮2023-03-27

今日热闻!调值公式法怎么使用_调值公式法

1、调值公式法(动态结算公式法)调值公式法是利用调值公式来调整价差。2、它首先将总费用分为固定部分、人工部分和材料部分2023-03-27

当前信息:越南撤侨事件始末(越南撤侨)

一、题文鉴于越南反华暴乱进一步扩散,我国政府迅速采取有效措施,派出飞机、舰船开展从越南的“撤侨”行动,至5月18日已撤出超过3000名中国人。2023-03-26

全球讯息:暴风连续涨停(期货配资雷曼小景)

跟着3月29日晚间中石化年报发表,国内三大石油巨子2015年的“成果单”悉数露脸。劳累于世界油价近乎腰斩,“三桶油”挣钱才能大不如前,净赢利2023-03-26

全球资讯:多地高校举办校园招聘会 促进大学生顺利就业、尽早就业

央视网消息:眼下正是春季招聘黄金期,多地高校举办多场招聘会,全力促进高校毕业生顺利就业、尽早就业。日前,江苏各地高校举办毕业生春季招2023-03-26

天天热消息:红楼梦每章概括20字_红楼梦每章概括200字

1、第三回贾雨村夤缘复旧职林黛玉抛父进京都黛玉母逝;贾母要接外孙女黛玉;林如海写信给贾政为雨村谋求复职。2、2023-03-26

当前关注:势如破竹的意思(成语“势如破竹”的意思是什么?)

本文目录一览:1、成语“势如破竹”的意思是什么?2、势如破竹的意思是什么3、势如破竹的意思4、势如破2023-03-26