Spring常見擴充總結

weixin_34208283發表於2018-10-06

通常,應用程式開發人員不需要子類化ApplicationContext實現類。相反,可以通過插入特殊整合介面的實現來擴充套件Spring IoC容器。下圖展示了Spring Bean的生命週期。而在這其中,與一些擴充點是非常相關的。也就是說,這些擴充點作用了Bean的生命週期中。(在看下面的擴充點的時候,可以結合這個圖,觀察擴充點所處於的生命週期位置)


5281821-c531470bd33e84fe.png
image.png

1. FactoryBean(用於自定義例項化邏輯)

FactoryBean通常是用來建立比較複雜的bean,一般的bean 直接用xml配置即可,但如果一個bean的建立過程中涉及到很多其他的bean 和複雜的邏輯,用xml配置比較困難,這時可以考慮用FactoryBean。

  • 定義需要建立的Bean
public class Tool {
    private int id;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public Tool(int id) {
        this.id = id;
    }
}
  • 實現FactoryBean
public class ToolFactory implements FactoryBean<Tool> {

    private int factoryId;
    private int toolId;
    //獲取所建立的Bean
    @Override
    public Tool getObject() throws Exception {
        return new Tool(toolId);
    }
    //獲取物件型別
    @Override
    public Class<?> getObjectType() {
        return Tool.class;
    }
    public int getFactoryId() {
        return factoryId;
    }
    //設定是否單例
    @Override
    public boolean isSingleton() {
        return false;
    }
    public void setFactoryId(int factoryId) {
        this.factoryId = factoryId;
    }
    public int getToolId() {
        return toolId;
    }
    public void setToolId(int toolId) {
        this.toolId = toolId;
    }
}

建立Tool這個Bean的具體過程都交給了ToolFactory來管理,也就是建立的細節都有它控制.

  • FactoryBean建立Bean交給容器管理。
@Configuration
public class FactoryBeanAppConfig implements InitializingBean,BeanPostProcessor {
   //注入到容器裡面
    @Bean(name="tool")
    public ToolFactory toolFactory() {
        ToolFactory factory = new ToolFactory();
        factory.setFactoryId(7070);
        factory.setToolId(2);
        return factory;
    }
  • 通過name = "&tool"來獲取到ToolFactory,因為ToolFactory本身也是Bean,由Spring容器管理.
     //注入Tool
    @Autowired
    Tool tool;
   //注入ToolFactory
    @Resource(name = "&tool")
    ToolFactory toolFactory;

2. InitializingBean和DisposableBean

IInitializingBeanDisposableBean中前者是在容器對Bean必要的屬性做了初始化後,再做後續的初始化。後者是容器對Bean銷燬之前做一個回撥。

  • InitializingBean
    InitializingBean只有一個方法,允許你在初始化之後做個回撥。
void afterPropertiesSet() throws Exception;

通常來說,不推薦使用InitializingBean,因為它和Spring耦合太緊密了。推薦使用 @PostConstruct 註解 或者一個具體的 POJO 初始化方法。推薦使用的與InitializingBean效果是一樣的,但是前者就沒有和Spring耦合。

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

@PostConstruct 註解 則需要在支援Servlet2.5的容器使用。

 public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

這三種方法的效果是一樣的,但是當同時配置的時候,按照Spring Bean的生命週期,執行的順序如下:

  1. 使用@PostConstruct註解的方法。
  2. InitializingBean中的afterPropertiesSet()方法。
  3. 自定義的init()方法。

對應的,銷燬中@PreDestroy,DisposableBean,自定義的destroy()效果一樣,執行順序與初始化的順序對應一樣。這兩類在初始化之前和銷燬之前的回撥通常用於快取的初始載入和快取的清除這樣的場景。

3. BeanNameAware,BeanFactoryAware,ApplicationContextAware

  • BeanNameAware使得物件能夠感知到bean的名稱。通常用於把beanName記錄在日誌。
  • BeanFactoryAware使得我們可以獲取到BeanFactory,進而可以獲取到bean,或者判斷bean是否是單例。
  • ApplicationContextAware使得我們獲取到ApplicationContext的具體實現類這個容器。這個ApplicationContextBeanFactory功能更強大。

使用 BeanNameAware,BeanFactoryAware,ApplicationContextAware值需要實現介面裡面相應的方法即可。通常來說,定義一個類變數來分別紀錄下感知的的Name,BeanFactory,ApplicationContext即可。

    @Override
    public void setBeanName(String s) {
        System.out.println(s);
        System.out.println("setBeanName方法被呼叫");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println(beanFactory.containsBean("person"));
        System.out.println(beanFactory.isSingleton("person"));
        System.out.println("setBeanFactory被呼叫,beanFactory");
    }
   //功能比beanFactory功能更強大些
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println(applicationContext.getId());
        System.out.println(applicationContext.containsBean("person"));
        System.out.println("setApplicationContext被呼叫");
    }

在大多數情況下,我們應該避免使用這些Aware介面,因為它和Spring耦合太多。

4. BeanPostProcessor,BeanFactoryPostProcessor

  • BeanPostProcessor是在整個bean已經建立,並且設定好類成員屬性之後,即spring容器已經例項化一個bean後。通過回撥方法去實現自定義的例項邏輯.通常用於檢查bean屬性的有效性,每個bean在初始化(init-method,InitializingBean等初始化方法)前後,都會回撥BeanPostProcessor中的方法。
  • BeanFactoryPostProcessor 主要用於改變bean的Metadata。Spring容器允許BeanFactoryPostProcessor去讀取配置元屬性,並改變它。它在BeanPostProcessor之前執行。
  public class MyBeanPostProcessor implements BeanPostProcessor,BeanFactoryPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        if(bean instanceof Person) {
            ((Person) bean).setName("sb");
        }
        System.out.println("postProcessBeforeInitialization被呼叫");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessAfterInitialization被呼叫");
        return bean;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessor被呼叫");
    }
}

這樣,SpringBean的生命週期就被串聯起來了。如下

  • 首先容器啟動後,會對scope為singleton且非懶載入的bean進行例項化,

  • 按照Bean定義資訊配置資訊,注入所有的屬性,

  • 如果Bean實現了BeanNameAware介面,會回撥該介面的setBeanName()方法,傳入該Bean的id,此時該Bean就獲得了自己在配置檔案中的id,

  • 如果Bean實現了BeanFactoryAware介面,會回撥該介面的setBeanFactory()方法,傳入該Bean的BeanFactory,這樣該Bean就獲得了自己所在的BeanFactory,

  • 如果Bean實現了ApplicationContextAware介面,會回撥該介面的setApplicationContext()方法,傳入該Bean的ApplicationContext,這樣該Bean就獲得了自己所在的ApplicationContext,

  • 如果有Bean實現了BeanPostProcessor介面,則會回撥該介面的postProcessBeforeInitialzation()方法,

  • 如果Bean實現了InitializingBean介面,則會回撥該介面的afterPropertiesSet()方法,(初始化之一)

  • 如果Bean配置了init-method方法,則會執行init-method配置的方法,(初始化之一)

  • 如果有Bean實現了BeanPostProcessor介面,則會回撥該介面的postProcessAfterInitialization()方法,

  • 經過流程9之後,就可以正式使用該Bean了,對於scope為singleton的Bean,Spring的ioc容器中會快取一份該bean的例項,而對於scope為prototype的Bean,每次被呼叫都會new一個新的物件,期生命週期就交給呼叫方管理了,不再是Spring容器進行管理了

  • 容器關閉後,如果Bean實現了DisposableBean介面,則會回撥該介面的destroy()方法,

  • 如果Bean配置了destroy-method方法,則會執行destroy-method配置的方法,至此,整個Bean的生命週期結束。

參考文章:
spring官方文件
Spring Bean生命週期

相關文章