SpringBoot-Configuration
前面提到,对于Spring项目来说,项目的功能取决于Spring容器中有哪些bean,每一个bean都是一个功能模块的实例化对象。所以问题就来到了如何向Spring容器中添加bean。
XML 配置文件
XML配置文件是最原始的添加bean的方式。使用XML配置文件符合了配置文件分离的原则。可以通过修改XML配置文件直接修改程序的功能,而不需要将项目重新编译。
在XML配置文件中通过<bean>标签来声明一个bean,并指定其类名、属性、依赖关系等信息,将该bean注册到Spring容器中。
1 | |
此处定义了一个bean,为com.example.MyBean类的实例对象,其标识符通过id属性指定。通过<property>标签,可以对其成员变量进行属性注入。对于基础数据类型,可以直接使用value属性进行赋值;对于对象数据类型,可以使用ref属性进行引用,属性值为另一个容器中bean的id。也可以通过设置bean的autowire属性来指定是否采用自动注入。例如可以指定autowire="byType"来采用类型匹配自动注入。如果容器中符合成员变量类型的bean只有一个,那么就可以自动赋值。
启动时,通过指定XML配置文件,即可启动Spring容器。
1 | |
在定义bean时,还有许多可配置属性。但作为便于理解的笔记,这部分内容此处不再深入,可以在需要的时候去查相关文档。
id:指定 bean 的唯一标识符,用于在容器中进行引用。class:指定 bean 的类名,用于实例化 bean 对象。scope:指定 bean 的作用域,包括 singleton(单例,默认值)、prototype(原型)、request(请求)、session(会话)等。init-method:指定 bean 初始化时调用的方法。destroy-method:指定 bean 销毁时调用的方法。property:用于设置 bean 的属性值。
其中可以指定bean的初始化init-method和销毁方法destroy-method,这也是上篇提到的管理bean生命周期的功能。
组件扫描
如果需要往容器中添加一百个bean,那么需要在XML配置文件中写一百个<bean>标签,过程十分繁琐。实际项目中,需要添加到容器中的bean往往是自己写的Java类,这些Java类在类路径上通常有特点,例如处于某个包之下。指定某个类路径/包下的所有Java类都加入到Spring容器中这个想法就十分自然。这种批量添加bean的方式叫组件扫描。
XML配置文件中开启组件扫描:
1 | |
使用注解@ComponentScan开启组件扫描:
1 | |
组件扫描需要搭配@Component使用,这是一个类注解。只有扫描路径下带有@Component注解的类才会被添加到容器,没有的类则会被忽略。组件扫描@ComponentScan和@Component组合使用可以灵活地控制路径下哪些类会被添加入容器。
除了@Component,还可以通过设置@ComponentScan的includeFilters和excludeFilters属性,来灵活地自定义扫描路径下的过滤规则。
配置类@Configuration
Java的注解特性太过便捷,Spring项目逐渐从传统XML配置文件方式向注解方式迁移,这种大量依赖注解来配置和管理Spring容器中的Bean的方式叫注解驱动。
在注解驱动下,一个XML配置文件对应于一个带有@Configuration注解的Java类。XML配置文件中所有的标签,都有对应的定义方式。其中最重要的<bean>标签,在配置类中对应一个方法。这个方法会被添加@bean注解,并且要求返回一个对象实例或对象的工厂bean。
1 | |
需要一提的是,配置类本身也是一个bean,存在于容器中。
基于单一职责原则,一般一个配置类往往只负责一类高度相关的功能。要开启某个功能往往就是把这个功能对应的配置类加入到容器中。配置类就会自动把功能相关的bean注册到容器中。
@ImportResource
实际项目开发中,可能会同时运用XML配置文件和@Configuration配置类。配置类作为主流的导入bean方式,那么就需要提供在其中引入XML配置文件的方法。@ImportResource就是这个作用。
1 | |
在公司项目中,提供的对外Dubbo服务没有采用配置类的方式,而是将其写在了XML配置文件。通过在XML文件引入xmlns:dubbo,在文件内部可以使用dubbo提供的特定标签<dubbo:service>来注册服务。然后通过@ImportResource("classpath:appCtx-dubbo.xml")将生产者和消费者对应的bean加入到容器中。
@Import
在Java配置类上使用@Import注解,将其他Java配置类引入当前配置类中,从而将这些配置类中定义的Bean一同注册到Spring容器中。@Import也能导入非配置类。
1 | |
相较于@bean的方式,@Import要简单很多。由于@bean注解在方法之上,可以通过编程式对每个bean进行细粒度的配置,而@Import就没法进行细粒度的控制。
@Import通常也用于注解之上,使其变得更容易理解。例如@Import(AspectJAutoProxyRegistrar.class)就是@EnableAspectJAutoProxy上的注解。开启自动代理功能,本质就是导入了AspectJAutoProxyRegistrar.class注册器。注册器中,通过重载了registerBeanDefinitions的回调方法,向容器中动态注册了AnnotationAwareAspectJAutoProxyCreator,从而开了AspectJ代理功能。相较于@Import(AspectJAutoProxyRegistrar.class),@EnableAspectJAutoProxy更具有语义化。实际上,大部分的@Enablexxx本质都是利用了@Import导入了一些特殊的bean。
实现BeanFactoryPostProcessor接口
这种方式作为用户不常用。一般作为开发者设计第三方库时的选择。
通过自定义一个实现了BeanFactoryPostProcessor接口的类,在其postProcessBeanFactory()方法中使用BeanDefinitionRegistry接口手动注册Bean到Spring容器中。例如:
1 | |
实际上还有ImportBeanDefinitionRegistrar。由于这部分内容找到的资料不多且良莠不齐,在此不深入讨论。
总的来说,这类方式是通过实现接口中预先定义的回调函数,在Spring容器启动过程的早期阶段会调用这些函数。在这些函数中,可以获取到Spring容器,并通过Spring容器的注册方法向其中注册自定义bean。
这部分内容令人感到困惑的原因是,Spring容器本身有多个接口和实现类,其中有许多都提供了注册方法。
总结
Spring应用的功能取决于其中的bean。向容器中添加/注册bean可以通过许多形式。其中最常用的是通过@Configuration的方式自定义配置类。结合SpringBoot的AutoConfiguration,可以很容易将各种应用集成到Spring中。由于AutoConfiguration内容太复杂,限于篇幅放到下一篇。