Spring Boot 装配bean


本篇博客仅记录Spring Boot中一些需要特殊注意的点,更多详细的Bean装配相关内容,可见本人之前博客:Spring高级装配

@ComponentScan

  • Boolean lazyInit
    默认为false,此时在Spring IOC容器初始化时,Bean就会执行实例化和依赖注入。若设置为true,则不会,只有在

  • Filter[] includeFilters
    Filter[] excludeFilters
    上述两个属性用于限定当满足/不满足过滤器的条件时扫描

其限制条件设置依赖内部注解 @Filter 定义:

  • 过滤器类型,可以按注解类型或正则式等过滤 FilterType type
  • 定义过滤的类 Class<?>[] value/classes
  • 匹配方式 String[] pattrn

使用方式如下:

@ComponentScan(includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {"com.springboot.practice.demo.test.class"}, pat)})

@SpringBootApplication中包含@Component,其包含四个属性:

  • exclude:通过类型排除自动配置类
  • excludeName:通过命名排除自动配置类
  • scanBasePackages:定义扫描包
  • ScanBasePackageClasses:定义被扫描的类

@Autowired

  • 先通过类型去查找匹配bean;
  • 类型匹配不唯一会去根据名称匹配;

Bean生命周期

  • Bean定义
  • Bean初始化
  • Bean生存期
  • Bean销毁

Spring Boot 装配bean

整个Bean生命周期所涉及的流程:
Spring Boot 装配bean

下述样例可以测试Bean的生命周期:

  • 定义一个Bean,涉及到上图中setBeanName、setBeanFactory、setApplicationContext、自定义初始化、afterPropertiesSet、自定义销毁、destroy过程
@Component
public class MyBean implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanFactoryAware的setBeanFactory");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("【" + this.getClass().getSimpleName() + "】DisposableBean方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用InitializingBean的afterPropertiesSet");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用ApplicationContextAware的setApplicationContext");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("【" + this.getClass().getSimpleName() + "】调用BeanNameAware的setBeanName");
    }

    @PostConstruct
    public void init(){
        System.out.println("【" + this.getClass().getSimpleName() + "】注解@PostConstruct定义的自定义初始化方法");
    }

    @PreDestroy
    public void destroy1(){
        System.out.println("【" + this.getClass().getSimpleName() + "】注解@PreDestroy定义的自定义销毁方法");
    }
}
  • 定义一个处理类,涉及上图中postProcessBeforeInitialization(预初始化)、postProcessAfterInitialization(后初始化)过程。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor调用postProcessBeforeInitialization方法,参数【" + bean.getClass().getSimpleName() + "】【" + beanName + "】");
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor调用postProcessAfterInitialization方法,参数【" + bean.getClass().getSimpleName() + "】【" + beanName + "】");
        return bean;
    }
}
  • 运行结果如下
BeanPostProcessor调用postProcessAfterInitialization方法,参数【ServletWebServerFactoryConfiguration$EmbeddedTomcat$$EnhancerBySpringCGLIB$$4506f63e】【org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat】
BeanPostProcessor调用postProcessBeforeInitialization方法,参数【ServletWebServerFactoryConfiguration$EmbeddedTomcat$$EnhancerBySpringCGLIB$$4506f63e】【org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat】
BeanPostProcessor调用postProcessAfterInitialization方法,参数【TomcatServletWebServerFactory】【tomcatServletWebServerFactory】
...

省略部分其它Bean的预初始化和后初始化日志。

BeanPostProcessor调用postProcessAfterInitialization方法,参数【BaseConfig$$EnhancerBySpringCGLIB$$ec4dd4fe】【baseConfig】
BeanPostProcessor调用postProcessBeforeInitialization方法,参数【BaseConfig$$EnhancerBySpringCGLIB$$ec4dd4fe】【baseConfig】
【MyBean】调用BeanNameAware的setBeanName
【MyBean】调用BeanFactoryAware的setBeanFactory
【MyBean】调用ApplicationContextAware的setApplicationContext
BeanPostProcessor调用postProcessAfterInitialization方法,参数【MyBean】【myBean】
【MyBean】注解@PostConstruct定义的自定义初始化方法
【MyBean】调用InitializingBean的afterPropertiesSet
BeanPostProcessor调用postProcessBeforeInitialization方法,参数【MyBean】【myBean】
BeanPostProcessor调用postProcessAfterInitialization方法,参数【WebConfig$$EnhancerBySpringCGLIB$$b79cfd7d】【webConfig】
BeanPostProcessor调用postProcessBeforeInitialization方法,参数【WebConfig$$EnhancerBySpringCGLIB$$b79cfd7d】【webConfig】
【MyBean】注解@PreDestroy定义的自定义销毁方法
【MyBean】DisposableBean方法

从上述日志可以看出:
1)后置处理器(BeanPostProcess)对所有Bean生效;
2)在容器初始化过程中,会按照上面流程图中的顺序,依次调用各方法。

⚠️:若Bean的定义是第三方类,使用@Bean属性来自定义初始化和销毁方法:
@Bean(initMethod = "init", destroyMethod = "destroy")

使用属性文件

读取属性配置上下文所需的maven依赖

<dependency>                            <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration- processor</artifactId>
<optional>true</optional>
</dependency>

有了依赖,就可以引用application.properties文件中配置的属性了,有以下两种方式:

@Value

使用${}占位符读取配置在属性文件的内容

@ConfigurationProperties

此方式可以减少一些配置代码:

  • application.properties中配置
# custom properties
myproperties.name=yanzy
myproperties.text=testProperties
  • model类匹配属性配置
@Component
@ConfigurationProperties("myproperties")
public class MyProperties {
    private String name;
    private String text;
    
    省略setter/getter方法
}
  • 引用配置属性
@Autowired
    MyProperties myProperties;

@RestController
public class BaseController {
    @Autowired
    MyProperties myProperties;

    @GetMapping("/properties")
    public void getConfigProperties(){
        System.out.println(String.format("------------- Name: %s, Text: %s ---------------", myProperties.getName(), myProperties.getText()));
    }

}
  • 调用结果:
------------- Name: yanzy, Text: testProperties ---------------

上述样例可见,Spring将根据注解中配置属性和POJO属性名称组成全限定名去配置文件中查找,并读取到POJO中。

@PropertySource

使用@PropertySource可以指定要读取的属性文件。

  • 自定义配置文件myProperties.properties:
specified.myproperties.name=Syanzy
specified.myproperties.text=specifiedMyProperties
  • model类匹配属性配置
@PropertySource(value = "classpath:myProperties.properties", ignoreResourceNotFound = true)
@Component
@ConfigurationProperties("specified.myproperties")
public class SpecifiedMyProperties {
    private String name;
    private String text;

    省略setter/getter方法
}
  • 引用配置属性
@Autowired
    SpecifiedMyProperties specifiedMyProperties;
    
@GetMapping("/specified/properties")
    public void getSpecifiedConfigProperties(){
        System.out.println(String.format("------------- Name: %s, Text: %s ---------------", specifiedMyProperties.getName(), specifiedMyProperties.getText()));
    }
  • 调用结果
------------- Name: Syanzy, Text: specifiedMyProperties ---------------

上述样例可以看出,引用自定义的属性文件成功了。

条件装配Bean

在上面关于@ConfigurationProperties的样例中,将model类修改如下:

@Component
@ConfigurationProperties("myproperties")
@Conditional(MyPropertiesCondition.class)
public class MyProperties {
    private String name;
    private String text;
    
    省略setter/getter方法
}

添加@Conditional注解,并指定条件类MyPropertiesCondition,其内容如下:

public class MyPropertiesCondition implements Condition {
    /**
     *
     * @param context 条件上下文
     * @param metadata 注释类型的元数据
     * @return true装配bean,否则不装配
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 取出环境配置
        Environment env =  context.getEnvironment();
        // 判断属性文件是否存在对应的数据配置
        return env.containsProperty("myproperties.open");
    }
}

使用了此限定条件,在我们的myProperties.properties文件中未配置myproperties.open时,会出现如下错误:

Description:

Field myProperties in com.springboot.practice.demo.Controller.BaseController required a bean of type 'com.springboot.practice.demo.model.MyProperties' that could not be found.

说明此时MyProperties这个Bean未加载。

引入XML配置Bean(@ImportResource)

  • 要声明为Bean的类
public class XmlConfigClass {
    public void description(){
        System.out.println(String.format("----------------- %s------------------", "this is a xml config bean"));
    }
}
  • xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="xmlConfigClass" class="com.springboot.practice.demo.model.XmlConfigClass"/>
</beans>
  • 引入xml配置
@Configuration
@ComponentScan
@ImportResource(value = {"classpath:xmlProperties.xml"})
public class BaseConfig {
}

此处@ImportResource注解的value属性指定了要引入的xml配置文件,其位置在resource目录下。

  • 注入Bean
@Autowired
    XmlConfigClass xmlConfigClass;
    
@GetMapping("/xml")
    public void getXmlConfigBean(){
        xmlConfigClass.description();
    }
  • 调用结果
----------------- this is a xml config bean------------------

参考书籍:《深入浅出Spring Boot2.x》作者:杨开振