SpringBoot-Configuration

前面提到,对于Spring项目来说,项目的功能取决于Spring容器中有哪些bean,每一个bean都是一个功能模块的实例化对象。所以问题就来到了如何向Spring容器中添加bean

XML 配置文件

XML配置文件是最原始的添加bean的方式。使用XML配置文件符合了配置文件分离的原则。可以通过修改XML配置文件直接修改程序的功能,而不需要将项目重新编译。

XML配置文件中通过<bean>标签来声明一个bean,并指定其类名、属性、依赖关系等信息,将该bean注册到Spring容器中。

1
2
3
4
5
<bean id="myBean" class="com.example.MyBean">
<!-- 设置属性值 -->
<property name="property1" value="value1" />
<property name="property2" ref="anotherBean" />
</bean>

此处定义了一个bean,为com.example.MyBean类的实例对象,其标识符通过id属性指定。通过<property>标签,可以对其成员变量进行属性注入。对于基础数据类型,可以直接使用value属性进行赋值;对于对象数据类型,可以使用ref属性进行引用,属性值为另一个容器中beanid。也可以通过设置beanautowire属性来指定是否采用自动注入。例如可以指定autowire="byType"来采用类型匹配自动注入。如果容器中符合成员变量类型的bean只有一个,那么就可以自动赋值。

启动时,通过指定XML配置文件,即可启动Spring容器

1
new ClassPathXmlApplicationContext("configuration.xml")

在定义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
<context:component-scan base-package="com.example"/>

使用注解@ComponentScan开启组件扫描:

1
@ComponentScan(basePackages="com.example")

组件扫描需要搭配@Component使用,这是一个类注解。只有扫描路径下带有@Component注解的类才会被添加到容器,没有的类则会被忽略。组件扫描@ComponentScan@Component组合使用可以灵活地控制路径下哪些类会被添加入容器。

除了@Component,还可以通过设置@ComponentScanincludeFiltersexcludeFilters属性,来灵活地自定义扫描路径下的过滤规则。

配置类@Configuration

Java的注解特性太过便捷,Spring项目逐渐从传统XML配置文件方式向注解方式迁移,这种大量依赖注解来配置和管理Spring容器中的Bean的方式叫注解驱动

注解驱动下,一个XML配置文件对应于一个带有@Configuration注解的Java类XML配置文件中所有的标签,都有对应的定义方式。其中最重要的<bean>标签,在配置类中对应一个方法。这个方法会被添加@bean注解,并且要求返回一个对象实例或对象的工厂bean

1
2
3
4
5
6
7
8
@Configuration
public class MyConfig {
@Bean
public MyBean myBean() {
// 返回Bean的实现
return new MyBean();
}
}

需要一提的是,配置类本身也是一个bean,存在于容器中。

基于单一职责原则,一般一个配置类往往只负责一类高度相关的功能。要开启某个功能往往就是把这个功能对应的配置类加入到容器中。配置类就会自动把功能相关的bean注册到容器中。

@ImportResource

实际项目开发中,可能会同时运用XML配置文件@Configuration配置类配置类作为主流的导入bean方式,那么就需要提供在其中引入XML配置文件的方法。@ImportResource就是这个作用。

1
2
3
4
5
@Configuration
@ImportResource("classpath:appCtx-dubbo.xml")
public class AppConfig {
// ...
}

在公司项目中,提供的对外Dubbo服务没有采用配置类的方式,而是将其写在了XML配置文件。通过在XML文件引入xmlns:dubbo,在文件内部可以使用dubbo提供的特定标签<dubbo:service>来注册服务。然后通过@ImportResource("classpath:appCtx-dubbo.xml")将生产者和消费者对应的bean加入到容器中。

@Import

在Java配置类上使用@Import注解,将其他Java配置类引入当前配置类中,从而将这些配置类中定义的Bean一同注册到Spring容器中。@Import也能导入非配置类。

1
2
3
4
5
@Configuration
@Import({AnotherConfig.class, YetAnotherConfig.class})
public class MyConfig {
// 引入其他配置类中的Bean
}

相较于@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
2
3
4
5
6
7
8
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 使用BeanDefinitionRegistry接口手动注册Bean
BeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);
beanFactory.registerBeanDefinition("myBean", beanDefinition);
}
}

实际上还有ImportBeanDefinitionRegistrar。由于这部分内容找到的资料不多且良莠不齐,在此不深入讨论。

总的来说,这类方式是通过实现接口中预先定义的回调函数,在Spring容器启动过程的早期阶段会调用这些函数。在这些函数中,可以获取到Spring容器,并通过Spring容器的注册方法向其中注册自定义bean

这部分内容令人感到困惑的原因是,Spring容器本身有多个接口和实现类,其中有许多都提供了注册方法。

总结

Spring应用的功能取决于其中的bean。向容器中添加/注册bean可以通过许多形式。其中最常用的是通过@Configuration的方式自定义配置类。结合SpringBootAutoConfiguration,可以很容易将各种应用集成到Spring中。由于AutoConfiguration内容太复杂,限于篇幅放到下一篇。


SpringBoot-Configuration
http://dracoyus.github.io/2023/04/17/SpringBoot-Configuration/
作者
DracoYu
发布于
2023年4月17日
许可协议