品Spring:注解之王@Configuration和它的一众“小弟们”

其实对Spring的了解达到一定程度后,你就会发现,无论是使用Spring框架开发的应用,还是Spring框架本身的开发都是围绕着注解构建起来的。

空口无凭,那就说个最普通的例子吧。

在Spring中要启用一项XXX功能,标准做法就是用@EnableXXX这种“启用”类型的注解。

那么这种类型的注解一般都做了什么呢?分析一下吧。

看过本号文章的人都知道,Spring的核心就是bean定义和bean,如果我想增加某方面的功能,只需写若干个类,并作为bean定义注册到容器中即可。

因此“启用”类型的注解表面上看起来是启用某项功能,背后其实就是注册了一系列相关的bean定义到容器中。

大家都非常熟悉的,启用事务管理功能的注解,@EnableTransactionManagement,如下图01:

还有启用定时任务的注解,@EnableScheduling,如下图02:

还有启动异步支持的注解,@EnableAsync,如下图03:

这类注解其实很多,就先看这三个吧。

相信大家已经发现了一个规律,这三个注解在定义时都使用@Import注解引入了某个类,最终目的当然是为了注册bean定义。

之前的文章中已经说过多遍,这种方法叫做通过编程的方式(或称写代码的方式)注册bean定义。

这种方式非常的灵活,因为@Import注解引入的类其实是两个著名接口的实现类。

这两个接口,之前也提到过很多次了,就是:

ImportSelector

ImportBeanDefinitionRegistrar

在实现接口时,可以按照自己关注的因素,来自主可控的(或称有选择性的)注册bean定义,这就是灵活性的原因。

@Import除了可以引入这两个接口外,还可以引入被@Configuration注解标注的类或没有被注解标注的普通类。

再次赘述一下这种方式的典型用法:

用于Spring框架自身的开发,以及第三方框架与Spring的整合开发。

熟悉Spring的朋友都应该知道,@EnableXXX注解和@Import注解都必须(直接或间接)和@Configuration注解放到一起才会起作用。

这是Spring规定的,主要是和Spring的源码实现有关。

所以整体就是,@Configuration类引入了@EnableXXX注解或@Import注解,@EnableXXX注解又引入了@Import注解。

而@Import注解又引入了那两个接口的实现类以编程方式注册bean定义,这里注册的bean定义同样还可以是@Configuration类。

@Import注解还可以再次引入@Configuration类。因此,我们成功的从@Configuration类出发后又回到了@Configuration类。

然后可以继续重复这个动作,一直进行下去。

这就像原子弹爆炸的链式反应一样,从最初的一个引爆点,然后开启逐个自我引爆的模式,越来越大,一发不可收拾。

所以Spring就是采用这种模式,最初的引爆点必须是一个@Configuration类,自我的引爆模式是@Import注解。

从最初的那一个基点,向容器中引入新的类,再以引入的新类作为基点,再次引入新的类,这种爆炸模型,非常强大。

除了@Import可以引入新类外,@ComponentScan注解也以扫描jar包的方式引入新的类。只不过扫描的方式通常用于用户程序而非框架开发。

可见Spring程序就是围绕着注解构建起来的,从最初的一个@Configuration类,配合使用@Import和@ComponentScan,逐渐壮大。

所以最初的这一个@Configuration类,是在应用启动前,显式的(或称特意的)注册到容器中的。

SpringBoot就是非常标准的这个样子的。它的主类就是一个@Configuration类,然后再使用@Import和@ComponentScan注解。

看看下面的注解源码,确实是这样子的,如下图0405:

终于可以下结论了:

@Configuration就是注解之王,是老大,其余的注解围绕在它周围,是小弟们。

按照江湖规矩,老大得罩着小弟们,所以负责处理@Configuration注解的类也要负责处理其它的注解。

编程新说注:究竟有哪些注解可以和@Configuration一起使用,在上一篇文章中已写明。

目前看来,主要是这样的:

在容器启动前使用编程的方式可以注册bean定义,如SpringBoot的主类。

在容器启动后,@Import和@ComponentScan都可以注册bean定义,@Bean方法也可以。

虽然它们功能相似,但侧重点其实是不同的,当然可以放到一起使用,但必须做到心中有数。

如果使用不当的话,可能会发生“重叠”,如一个类即被引入了,也被扫描了。

而且还可能被引入多次,扫描多次,当然,即使这样也不一定报错,因为Spring做了处理。

Spring采用了类似先入为主的做法:

首先是容器启动前通过编程方式注册的类。这些类在后面无论是被扫描还是被引入,都不会再被注册。

其次是如果先通过扫描方式注册的类,引入时不再注册。如果先通过引入方式注册的类,扫描时不再注册。

最后是如果先通过扫描方式注册的类,再次扫描时不再注册。如果先通过引入方式注册的类,再次引入时不再注册。

这就像幂等处理一样,给上层用户留有较宽的余地,在最终使用时没有那么严格。多了一些随意。

编程新说注:在显式注册bean定义时,如果beanName已存在,会抛出异常。

如果这些注解在一起出现的话,注册bean定义也是有先后顺序的:

第一,容器启动前,注册的@Configuration类的bean定义。

编程新说注:下面的这些内容都是依附于@Configuration类的。

第二,@ComponentScan注解扫描的bean定义。

第三,使用ImportSelector接口引入的bean定义(非ImportBeanDefinitionRegistrar接口的实现类)。

第四,使用@Import引入的普通类的bean定义。

第五,类的@Bean方法引入的bean定义。

第六,使用@ImportResource注解引入的XML文件里的bean定义。

第七,使用ImportBeanDefinitionRegistrar接口引入的bean定义。

第八,@Configuration类实现的接口里的@Bean方法(默认方法)。

第九,@Configuration类的父类

接着就是把父类当作新的@Configuration类,再次重复第二到第九。

整个bean定义的注册过程,就是以容器为依托,一遍一遍的执行,直到容器中的bean定义再无增多为止。

下面就描述其中的一遍:

第一,从容器中拿出尚未处理过的所有@Configuration类

第二,从这一批@Configuration类中解析出所有需要被注册的类

第三,将这一批@Configuration类标记为已处理过

第四,将刚刚解析出来的类进行bean定义注册,这些类里面可能还含有@Configuration类。

接下来进行循环执行即可。直到某一次执行完后,容器中的bean定义数量没有变化,表明已全部处理完毕。

再说一个外部类和内部类的事情,颇有意思:

@Configurationclass Outter {

    @Configuration    static class Inner {

    }}

先来看两个问题

当外部类上有注解时,分别使用@ComponentScan和@Import作用于外部类,此时注册的bean定义是谁?

当把外部类上的注解去掉时,分别使用@ComponentScan和@Import作用于外部类,此时注册的bean定义是谁?

来看看答案吧:

当外部类有注解时,@ComponentScan和@Import都会把Outter和Inner注册bean定义。

当外部类没有注解时,@ComponentScan会注册Inner类,@Import会注册Outter类。

下面来解释下:

JVM规范规定,每个字节码文件只能包含一个类,因此嵌套类会单独编译成一个字节码文件。与它的外部类是互相独立的。

而@ComponentScan注解是按字节码文件扫描的,所以此时内部类都会被注册,即使外部类上没有注解。

当@Import引入一个类时,不管该类上是否有注解,该类本身一定会被注册bean定义。所以外部类总是被注册。

但是,只有外部类上有注解时,才会去处理它的内部类。且内部类上必须也要有注解才行。因为源码就是这样写的。

最后看看SpringBoot应用注册的前几个bean定义,如下图06:

可见第一个注册的bean定义,就是用来处理标有@Configuration注解的类的。

第六个注册的是主类的bean定义。

>>> 热门文章集锦 <<<

毕业10年,我有话说

【面试】我是如何面试别人List相关知识的,深度有点长文

我是如何在毕业不久只用1年就升为开发组长的

爸爸又给Spring MVC生了个弟弟叫Spring WebFlux

【面试】我是如何在面试别人Spring事务时“套路”对方的

【面试】Spring事务面试考点吐血整理(建议珍藏)

【面试】我是如何在面试别人Redis相关知识时“软怼”他的

【面试】吃透了这些Redis知识点,面试官一定觉得你很NB(干货 | 建议珍藏)

【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看(建议珍藏)

【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)

【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生(深度好文,建议珍藏)

【面试】如果把线程当作一个人来对待,所有问题都瞬间明白了

Java多线程通关———基础知识挑战

品Spring:帝国的基石

>>> 品Spring系列文章 <<<

品Spring:帝国的基石

品Spring:bean定义上梁山

品Spring:实现bean定义时采用的“先进生产力”

品Spring:注解终于“成功上位”

品Spring:能工巧匠们对注解的“加持”

品Spring:SpringBoot和Spring到底有没有本质的不同?

品Spring:负责bean定义注册的两个“排头兵”

品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”

品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

作者是工作超过10年的码农,现在任架构师。喜欢研究技术,崇尚简单快乐。追求以通俗易懂的语言解说技术,希望所有的读者都能看懂并记住。下面是公众号和知识星球的二维码,欢迎关注!

       

原文地址:https://www.cnblogs.com/lixinjie/p/taste-spring-010.html

时间: 2024-11-07 23:08:32

品Spring:注解之王@Configuration和它的一众“小弟们”的相关文章

Spring注解中@Configuration和@Configurable的区别

@Configuration该注解是可以用来替代XML文件.以前我们配置bean时,都是写在applicationContext.xml文件中的.有了这个注解后,我们就可以编写一个类在其上面加上该注解.即配置类.在配置类中可以在方法上加@Bean注解定义其中的Bean @Configurable现在假设,我们想在非Spring管理的类中使用依赖注入:比如:手动new出来的对象,正常情况下,Spring是无法依赖注入的,这个时候可以使用@Configurable注解: 原文地址:https://w

品Spring:对@Autowired和@Value注解的处理方法

在Spring中能够完成依赖注入的注解有JavaSE提供的@Resource注解,就是上一篇文章介绍的. 还有JavaEE提供的@javax.inject.Inject注解,这个用的很少,因为一般都不会去引用JavaEE的jar包. 编程新说注:JavaEE早已经被Oracle抛弃了.JavaEE这个名字已经成为历史. 还有两个就是@Value和@Autowired注解,这可是Spring自己的亲孩子.所以这两个使用的最多. 虽然注解不一样,但是目的一样,都是用来进行依赖注入,而且Spring处

品Spring:注解终于“成功上位”

历史还是抛弃了XML,当它逐渐尝到注解的甜头之后. 尤其是在Spring帝国,到处充满着注解的气息. 注解从一个提供附属信息的“门客”,蜕变为颇具中流砥柱的“君侯”. 注解成功登上了帝国的舞台,定会像XML一样留下浓墨重彩的一笔. 重新认识一下注解 注解其实就是注释.批注的意思.就像看书时在旁边记笔记一样. 如果把书上印刷的内容看作是原始信息,那写上的笔记则是新添加的额外信息,且这个额外信息并不会对原始信息造成破坏. 所以注解其实是为原始数据信息添加额外附加数据信息的一种方式,且对原有数据信息没

品Spring:bean定义上梁山

认真阅读,收获满满,向智慧又迈进一步... 技术不枯燥,先来点闲聊 先说点好事高兴一下.前段时间看新闻说,我国正式的空间站建设已在进行当中.下半年,长征五号B运载火箭将在海南文昌航天发射场择机将空间站核心舱发射升空.预计用2到3年将空间站建好. 虽然到时你们不让我上去,不过我也为这件事出不了什么力,算扯平了.哈哈,但是我还是会衷心的祝福你. 长征五号火箭首次采用5米大直径的箭体结构,总加注量达到780吨,起飞时共有10台发动机产生1078吨的推力,具备近地轨道25吨.地球同步转移轨道14吨的运载

品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

上一篇文章整体非常轻松,因为在容器启动前,只注册了一个bean定义,就是SpringBoot的主类. OK,今天接着从容器的启动入手,找出剩余所有的bean定义的注册过程. 具体细节肯定会颇为复杂,同样,大家只需关注都干了什么,不用考虑如何干的. 来宏观的看下容器的启动过程,即refresh方法,如下图01: 只捡重要的来说,就是四大步: 第一,准备好bean工厂(BeanFactory). 第二,调用已经注册的bean工厂后处理器(BeanFactoryPostProcessor). 第三,注

Struts2+Spring3+MyBatis3整合以及Spring注解开发

 分类: Web(2)  版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在做一个SpringMVC+spring+MyBatis的项目,突然想起以前自己要搭建一个Struts2+Spring+IBatis的框架,但是没成功,正好看见培训时候老师给的千里之行的开源项目.于是将这个项目提供的SQL加入到了自己的数据库中(所以数据和项目名用的是qlzx),打算以后做练习的时候用这个数据库.那么接下来问题来了(不要说某翔或者不约,不是那个问题):我有了数据库和数据,想要搭建一个网站,该怎么做

Java面试--Spring技术要点--Spring注解

20  基于Java的Spring注解配置(给一些注解的例子) 基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件. 以@Configuration注解为例,它用来标记类可以当做一个bean的定义,被SpringIOC容器使用.另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文.(一般很少用java代码对元数据进行配置) 21  Spring基于注解的容器配置.和开启配置 相对于XML文件,

spring注解源码分析--how does autowired works?

1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行debug. 2. Autowired的定义及作用 作用:Marks a constructor, field, setter method or config method as to be autowired by Spring's dependency injection facilities.

spring注解预览

从Java5.0开始,Java开始支持注解.Spring做为Java生态中的领军框架,从2.5版本后也开始支持注解.相比起之前使用xml来配置Spring框架,使用注解提供了更多的控制Spring框架的方式. 现在越来越多的项目也都在使用注解做相关的配置,但Spring的注解非常多,相信很多注解大家都没有使用过.本文就尽量全面地概括介绍一下Spring中常用的注解. 一. 核心注解 @Required 此注解用于bean的setter方法上.表示此属性是必须的,必须在配置阶段注入,否则会抛出Be