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銷燬

在這裏插入圖片描述

整個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》作者:楊開振

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章