Spring之可擴展點

一、SpringBean的生命週期

 

 

 

 

二、後置處理器postProcessor 

一個是針對BeanDefinition的容器級別的後處理器 - BeanFactoryPostProcessor
一個是針對getBean操作獲得的對象的後處理器 - BeanPostProcessor

兩者的不同:

觸發時機不同,前者BeanFactoryPostProcessor是在容器refresh方法中調用,而後者實際調用時機是在getBean方法獲取對象時調用;
1.因觸發時機不同導致二者處理的對象不同。
BeanFactoryPostProcessor處理的是解析完配置文件後註冊在容器中的BeanDefinition,
而BeanPostProcessor處理的是通過反射生成的實例Bean;
2.接口樣式不同,
BeanFactoryPostProcessor只有一個後處理方法,
而BeanPostProcessor有一個前置處理方法一個後置處理方法。

三、BeanPostProcessor

Spring主要提供了兩類擴展點BeanPostProcessor和BeanFactoryPostProcessor。前者是操作bean的實例,後者使對bean的元數據定義進行擴展。

BeanPostProcessor提供對bean實例的操作擴展,在spring容器對bean實例化和設置依賴之後,其回調開始執行。BeanPostProcessor接口定義的兩個方法,分別在bean的初始化方法(InitializingBean接口,或者init-method定義)執行的前後執行:

如果你想在Spring容器完成實例化,配置和初始化bean之後實現一些自定義邏輯,則可以插入一個或多個自定義BeanPostProcessor實現。這些實現成爲後置處理器。

BeanPostProcessor接口包含兩個回調方法。

當實現此接口類通過容器註冊爲後處理器時,由Spring容器實例的Bean,Spring容器會在bean 的init方法執行前回調postProcessBeforeInitialization方法,

然後會在bean初始化之後回調postProcessAfterInitialization方法。

後置處理器可以對這些Bean做任何自定義操作。一些Spring Aop 的基礎實現類就是通過實現BeanPostProcessor從而提供代理包裝邏輯 。

Spring容器能夠自動檢測任何實現了BeanPostProcessor接口的Bean。容器會自動將這些bean註冊成後置處理器以便後續調用。

另外我們可以定義多個BeanPostProcessor,他們執行的順序可以通過實現PriorityOrdered、Ordered接口來控制。
我們定義一個類實現了BeanPostProcessor,默認會對整個Spring容器中所有的bean進行處理,方法的參數:
1.每個Bean的實例
2.每個Bean的name 或者 id屬性
實現 PriorityOrdered、Ordered,可以定義順序

下面的示例演示如何在ApplicationContext中編寫,註冊和使用BeanPostProcessor實例(Spring AOP)的實現方式就是如下。

@Component
public interface Person {
 
    void sayHello();
 
}
@Component("student")
public class StudentImpl implements Person {
 
    private String name;
 
    @Override
    public void sayHello() {
        System.out.println("Hello World, " + this.name);
    }
 
    @PostConstruct
    public void init() {
        this.name = "student";
    }
 
    @Override
    public String toString() {
        return "HelloWorldImpl{" +
                "name='" + name + '\'' +
                '}';
    }
}
@Component("teacher")
public class TeacherImpl implements Person {
    private String name;
 
    @Override
    public void sayHello() {
        System.out.println("Hello World, "+this.name);
    }
 
    @PostConstruct
    public void init(){
        this.name="teacher";
    }
 
    @Override
    public String toString() {
        return "TeacherImpl{" +
                "name='" + name + '\'' +
                '}';
    }
}
@Configuration
@ComponentScan(value = "com.best")
public class AppConfig {
}
/**
 * 自定義HelloWorldBeanPostProcessor實現BeanPostProcessor接口
 */
@Component
public class HelloWorldBeanPostProcessor implements BeanPostProcessor {
 
    @Autowired
    private ApplicationContext applicationContext;
 
    // 直接返回實例化的bean,在bean初始化之前執行
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization: " + bean + ", " +  beanName + ", " + applicationContext.getApplicationName());
        return bean;
    }
 
    // 直接返回實例化的bean,在bean初始化之後執行
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization: " + bean + ", " +  beanName + ", " + applicationContext.getApplicationName());
        return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("BeanPostProcessor織入,Spring AOP實現原理");
                return method.invoke(bean, args);
            }
        });
    }
}

執行入口:

public class Main {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        Person student = (Person) applicationContext.getBean("student");
        System.out.println(student.getClass().getName());
        student.sayHello();
        Person teacher = (Person) applicationContext.getBean("teacher");
        System.out.println(teacher.getClass().getName());
        teacher.sayHello();
 
    }
}

上面程序執行的結果如下:

 

 

 

四、BeanFactoryPostProcessor

 

BeanFactory級別的處理,是針對整個Bean的工廠進行處理,這是Spring容器的另外一個擴展點,和BeanPostProcessor不同的地方在於,它是對beanDefiniton進行操作。
實現該接口,可以在spring的bean創建之前,修改bean的定義屬性。
當調用BeanFactoryPostProcess 方法時,這時候bean還沒有實例化,此時Bean剛被解析成 BeanDefinition對象。也就是說,Spring允許BeanFactoryPostProcessor在容器實例化任何其它bean之前讀取配置元數據,並可以根據需要進行修改,
例如可以把bean的scope從singleton改爲prototype,也可以把property的值給修改掉。可以同時配置多個BeanFactoryPostProcessor,並通過設置'order'屬性或實現ordered接口來控制各個BeanFactoryPostProcessor的執行次序,
這些和BeanPostProcessor很類似,並且其啓用方式和容器相關性也與之一致。
注意:BeanFactoryPostProcessor是在spring容器加載了bean的定義文件之後,在bean實例化之前執行的。
接口方法的入參是ConfigurrableListableBeanFactory,使用該參數,可以獲取到相關bean的定義信息: BeanDefinition obj = arg0.getBeanDefinition("sumBean");

Spring內置實現了很多的BeanFactoryPostProcessor實現,例如:

org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 
org.springframework.beans.factory.config.PropertyOverrideConfigurer 
org.springframework.beans.factory.config.CustomEditorConfigurer:用來註冊自定義的屬性編輯器。
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println(" IOC 容器調用了 YjBeanFactoryPostProcessor 的 postProcessBeanFactory方法");
        for(String name:beanFactory.getBeanDefinitionNames()) {
            if("yjLog".equals(name)) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(name);
                //beanDefinition.setLazyInit(true);
            }
        }
    }
}

下面示例,如何通過BeanFactoryPostProcessor動態註冊Bean進去。

@Component
public class User {
 
    private Integer id;
 
    private String name;
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
@Configuration
@ComponentScan(value = "com.best")
public class AppConfig {
}
@Component
public class HelloWorldBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    // 實現BeanFactoryPostProcessor,在spring的bean創建之前,對beanDefinition進行操作,修改bean的定義屬性
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("id", 1);
        beanDefinitionBuilder.addPropertyValue("name", "jak");
        defaultListableBeanFactory.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());
 
    }
}

 程序入口,獲取User並輸出。

public class Main { 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.toString());
    }
}

程序輸出結果,如下:

 

 

五、BeanDefinitionRegistryPostProcessor

這個接口繼承了BeanFactoryPostProcessor. 從名字上來看, 這個接口是BeanDefinitionRegistry的後處理器,
我們先介紹下BeanDefinitionRegistry.
BeanDefinitionRegistry是用來註冊BeanDefinition的.
BeanDefinition就是Bean的配置元數據或Bean的描述信息, 比如Bean的屬性值, 構造方法的參數值等. 上面的BeanFactory的BeanDefinition也是由它註冊的.
BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的擴展, 允許在BeanFactoryPostProcessor被調用之前對BeanDefinition做一些操作, 尤其是它可以註冊BeanFactoryPostProcessor的BeanDefinition.
它提供了一個方法postProcessBeanDefinitionRegistry(), 這個方法被調用的時候, 所有的BeanDefinition已經被加載了, 但是所有的Bean還沒被創建.

注意:
所有的Bean生成都有個順序: 定義 --> 創建 --> 初始化.
BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法在Bean被定義但還沒被創建的時候執行.
BeanFactoryPostProcessor的postProcessBeanFactory方法在Bean被創建但還沒被初始化的時候執行
@Component
public class BestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
 
    //該方法用來註冊更多的bean到spring容器中
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("BestBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法");
        //框架自己的  BeanDefiniton  Count
        System.out.println("bean定義的數據量:" + registry.getBeanDefinitionCount());
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(BestBeanDefinitionRegistryPostProcessor.class);
        registry.registerBeanDefinition("bestBeanDefinitionRegistryPostProcessor", rootBeanDefinition);
    }
 
    // 繼承自BeanFactoryPostProcessor的方法 主要用來對bean定義做一些改變
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BestBeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法");
        System.out.println("bean定義的數據量:" + beanFactory.getBeanDefinitionCount());
    }
}
public class Main {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        BestBeanDefinitionRegistryPostProcessor bestBeanDefinitionRegistryPostProcessor = (BestBeanDefinitionRegistryPostProcessor) applicationContext.getBean("bestBeanDefinitionRegistryPostProcessor");
        System.out.println(bestBeanDefinitionRegistryPostProcessor.getClass().getCanonicalName());
    }
}

運行結果:

 

 

六、InitializingBean和DisposableBean

Spring 中定義了 3 種自定義初始化和銷燬方法。

 

 

1.過@Bean指定init-method和destroy-method屬性
2.Bean實現InitializingBean(定義初始化邏輯),DisposableBean(定義銷燬邏輯);
3.@PostConstruct:在bean創建完成並且屬性賦值完成;來執行初始化方法
Spring bean 通過實現 InitializingBean ,DisposableBean 接口實現初始化方法和銷燬前操作

這個接口有一個方法:afterPropertiesSet, 該方法在所有的屬性都被賦值後調用. 屬性被賦值是在初始化的時候做的, 與BeanPostProcessor結合來看,

afterPropertiesSet方法將在postProcessBeforeInitialization和postProcessAfterInitialization之間被調用.
@Component
public class Student implements InitializingBean, DisposableBean {
 
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean destroy");
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean afterPropertiesSet");
    }
}
@Configuration
@ComponentScan(value = "com.best")
public class AppConfig {
 
}
public class Test {
    public static void main(String[] args) {
 
        /**
         * 1.把類掃描出來--掃描以後幹了什麼事情
         * 2.把bean實例化
         */
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        Student student = applicationContext.getBean(Student.class);
        System.out.println(student.getClass().getName());
        applicationContext.close();
    }
}

運行結果:

 

 

七、 Aware接口

Spring中提供了各種Aware接口,如果檢測到一個bean實現了Aware接口,則能在bean中獲取相應的Spring資源;
如果某個對象實現了某個Aware接口,比如需要依賴Spring的上下文容器(ApplicationContext),則可以實現ApplicationContextAware接口。
Spring在Bean進行初始化(注意與實例化的區別)之前,會將依賴的ApplicationContext對象通過調用ApplicationContextAware#setApplicationContext注入。

Spring 提供的Aware接口如下:

 

 

那麼這些Aware接口在源碼中是什麼時候調用的呢?

AbstractAutowireCapableBeanFactory#invokeAwareMethods

 

 

 

 其他的Aware接口呢?通過 ApplicationContextAwareProcessor

 

 

 

 

 

 應用示例

@Component
public class BestApplicationContextAware implements ApplicationContextAware {
 
    ApplicationContext applicationContext;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        String[] beanDefinitionNames = this.applicationContext.getBeanDefinitionNames();
        System.out.println(Arrays.toString(beanDefinitionNames));
    }
}
public class Main {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        BestApplicationContextAware bestApplicationContextAware = (BestApplicationContextAware) applicationContext.getBean("bestApplicationContextAware");
        System.out.println(bestApplicationContextAware.getClass().getCanonicalName());
    }
}

運行結果:

 

 

八、FactoryBean

一般情況下,Spring通過反射機制利用bean的class屬性指定實現類來實例化bean 。

在某些情況下,實例化bean過程比較複雜,如果按照傳統的方式,則需要在bean中提供大量的配置信息,配置方式的靈活性是受限的,這時採用編碼的方式可能會得到一個簡單的方案。

Spring爲此提供了一個org.Springframework.bean.factory.FactoryBean的工廠類接口,用戶可以通過實現該接口定製實例化bean的邏輯。

(後面Spring又提供了@Configration和@Bean這種方式,一定程度上可以替代FactoryBean)

  

 

 

public interface FactoryBean<T> {
 
    @Nullable
    T getObject() throws Exception;
 
    @Nullable
    Class<?> getObjectType();
 
    default boolean isSingleton() {
        return true;
    }
}

 

 FactoryBean和BeanFactory的區別

BeanFactory

 

 FactoryBean

 

 應用場景

 

 

@Component
public class BestFactoryBean implements FactoryBean<User> {
 
    private String userInfo;
 
    public User getObject() throws Exception {
        User User = new User();
        String[] infos = userInfo.split(",");
        User.setId(Integer.parseInt(infos[0]));
        User.setName(infos[1]);
        return User;
    }
 
    public Class<User> getObjectType() {
        return User.class;
    }
 
    public boolean isSingleton() {
        return false;
    }
 
    public String getUserInfo() {
        return this.userInfo;
    }
 
    // 接受逗號分割符設置屬性信息
    public void setUserInfo(String userInfo) {
        this.userInfo = userInfo;
    }
}

九、ApplicationListener

這跟Servlet中的監聽器一樣, 採用了觀察者模式. 監聽器往往都是監聽某些事件源,
下面是配合ApplicationContextAware一起使用的例子.
我們定義一個事件, 在實現了ApplicationContextAware的Bean中觸發事件, 在實現了ApplicationListener的類中對事件做出反應
// 自定義事件
public class RumenEvent extends ApplicationEvent {
    public RumenEvent(Object source) {
        super(source);
    }
}
// 自定義 Bean 實現 ApplicationContextAware 接口
@Component
public class RumenzBean implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    private String name;
    public void setApplicationContext(ApplicationContext context) {
        this.applicationContext = context;
    }
    // 當調用 setName 時, 觸發事件
    public void setName(String name) {
        this.name = name;
        applicationContext.publishEvent(new RumenEvent(this));  // 這行代碼執行完會立即被監聽到
    }
    public String getName() {
        return name;
    }
}
// 自定義監聽器, 監聽上面的事件
@Component
public class MyApplicationListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof RumenEvent) {
            System.out.println(((RumenzBean)event.getSource()).getName());
        }
    }
}

 

參考博客:Spring常用擴展點

參考視頻:視頻教程

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