本节书摘来自华章出版社《springboot揭秘:快速构建微服务体系》一书中的第2章,第2.2节了解一点儿javaconfig,作者王福强,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.3 了解一点儿javaconfig
java 5的推出,加上当年基于纯java annotation的依赖注入框架guice的出现,使得spring框架及其社区也“顺应民意”,推出并持续完善了基于java代码和annotation元信息的依赖关系绑定描述方式,即javaconfig项目。
基于javaconfig方式的依赖关系绑定描述基本上映射了最早的基于xml的配置方式,比如:
(1)表达形式层面
基于xml的配置方式是这样的:

而基于javaconfig的配置方式是这样的:
@configuration
public class mockconfiguration{
}
任何一个标注了@configuration的java类定义都是一个javaconfig配置类。
(2)注册bean定义层面
基于xml的配置形式是这样的:
而基于javaconfig的配置形式是这样的:
任何一个标注了@bean的方法,其返回值将作为一个bean定义注册到spring的ioc容器,方法名将默认成为该bean定义的id。
(3)表达依赖注入关系层面
为了表达bean与bean之间的依赖关系,在xml形式中一般是这样的:
而在javaconfig中则是这样的:
如果一个bean的定义依赖其他bean,则直接调用对应javaconfig类中依赖bean的创建方法就可以了。
在javaconfig形式的依赖注入过程中,我们使用方法调用的形式注入依赖,如果这个方法返回的对象实例只被一个bean依赖注入,那也还好,如果多于一个bean需要依赖这个方法调用返回的对象实例,那是不是意味着我们就会创建多个同一类型的对象实例?
从代码表述的逻辑来看,直觉上应该是会创建多个同一类型的对象实例,但实际上最终结果却不是这样,依赖注入的都是同一个singleton的对象实例,那这是如何做到的?
笔者一开始以为spring框架会通过解析javaconfig的代码结构,然后通过解析器转换加上反射等方式完成这一目的,但实际上spring框架的设计和实现者采用了另一种更通用的方式,这在spring的参考文档中有说明,即通过拦截配置类的方法调用来避免多次初始化同一类型对象的问题,一旦拥有拦截逻辑的子类发现当前方法没有对应的类型实例时才会去请求父类的同一方法来初始化对象实例,否则直接返回之前的对象实例。
所以,原来spring ioc容器中有的特性(features)在javaconfig中都可以表述,只是换了一种形式而已,而且,通过声明相应的java annotation反而“内聚”一处,变得更加简洁明了了。
2.3.1 那些高曝光率的annotation
至于@configuration,我想前面已经提及过了,这里不再赘述,下面我们看几个其他比较常见的annotation,便于为后面更好地理解springboot框架的奥秘做准备。
@componentscan
@componentscan对应xml配置形式中的元素,用于配合一些元信息java annotation,比如@component和@repository等,将标注了这些元信息annotation的bean定义类批量采集到spring的ioc容器中。
我们可以通过basepackages等属性来细粒度地定制@componentscan自动扫描的范围,如果不指定,则默认spring框架实现会从声明@componentscan所在类的package进行扫描。
@componentscan是springboot框架魔法得以实现的一个关键组件,大家可以重点关注,我们后面还会遇到它。
@propertysource与@propertysources
@propertysource用于从某些地方加载*.properties文件内容,并将其中的属性加载到ioc容器中,便于填充一些bean定义属性的占位符(placeholder),当然,这需要propertysourcesplaceholderconfigurer的配合。
如果我们使用java 8或者更高版本开发(本书写作期间java 9还没发布),那么,我们可以并行声明多个@propertysource:
如果我们使用低于java 8版本的java开发spring应用,又想声明多个@propertysource,则需要借助@propertysources的帮助了:
@import与@importresource
在xml形式的配置中,我们通过的形式将多个分开的容器配置合到一个配置中,在javaconfig形式的配置中,我们则使用@import这个annotation完成同样目的:
@import只负责引入javaconfig形式定义的ioc容器配置,如果有一些遗留的配置或者遗留系统需要以xml形式来配置(比如dubbo框架),我们依然可以通过@importresource将它们一起合并到当前javaconfig配置的容器中: