spring中的鉤子方法和鉤子接口

spring framework具備很高的擴展性,咱們能夠經過這些接口對spring作靈活的擴展。java

1.  Aware接口

Spring中提供了各類Aware接口,方便從上下文中獲取當前的運行環境,比較常見的幾個子接口有: ApplicationContextAware、BeanClassLoaderAware、BeanFactoryAware、BeanNameAware、EnvironmentAware、ImportAware、MessageSourceAware、ServletConfigAware、ServletContextAware等等。web

例如:要想經過應用上下文獲取bean對象,能夠經過以下方式。spring

@Component
public class ApplicationContextAssistor implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public Object getBeanDefinition(String name) {
        return this.applicationContext.getBean(name);
    }

    public <T> T getBeanDefinition(String name, Class<T> clazz) {
        return this.applicationContext.getBean(name, clazz);
    }

}

2.  InitializingBean和DisposableBean

InitializingBean接口只有一個方法afterPropertiesSet( ):當BeanFactory 設置完全部的Bean屬性以後纔會調用,能夠作一些資源初始化操做。apache

DisposableBean接口只有一個方法destroy( ):單例銷燬時由BeanFactory調用,能夠作一些資源釋放操做。編程

例子:tomcat

@Component
public class ConnectBean implements InitializingBean, DisposableBean {

    @Override
    public void destroy() throws Exception {
        System.out.println("單例銷燬時由BeanFactory調用");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("當BeanFactory 設置完全部的Bean屬性以後纔會調用");
    }

}

3. BeanPostProcessor

BeanPostProcessor能夠在bean初始化( 注意初始化不包括init方法 )以後修改bean實例中的屬性,對bean實例作一些特定操做 。springboot

例如:先定義一個bean,包含屬性count咱們將經過BeanPostProcessor修改該屬性的值。app

@Component
public class ConnectBean{

    private Integer count;

    public void setCount(int count) {
        this.count = count;
    }

    public Integer getCount() {
        return this.count;
    }
}

定義BeanPostProcessor接口實現類實現接口方法postProcessBeforeInitialization在bean實例初始化以後調用和postProcessAfterInitialization在bean初始化以後調用,先找出ConnectBean實例而後對調用bean實例的setCount( )方法,ide

@Order(value = 1)
@Component
public class ConnectBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ConnectBean) {
            System.out.println("給ConnectBean設置初始count");
            ((ConnectBean)bean).setCount(100);
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ConnectBean) {
            System.out.println("給ConnectBean設置count完成" + ((ConnectBean)bean).getCount());
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

調用啓動方法:post

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)
2020-03-24 08:47:43.417  INFO 187788 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 187788 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-24 08:47:43.419  INFO 187788 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-03-24 08:47:44.236  INFO 187788 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-03-24 08:47:44.244  INFO 187788 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-24 08:47:44.244  INFO 187788 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-24 08:47:44.316  INFO 187788 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-24 08:47:44.316  INFO 187788 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 860 ms
給ConnectBean設置初始count
給ConnectBean設置count完成100
2020-03-24 08:47:44.428  INFO 187788 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-24 08:47:44.537  INFO 187788 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-24 08:47:44.539  INFO 187788 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.395 seconds (JVM running for 1.724)

4.  BeanFactoryPostProcessor

Spring IoC容器容許BeanFactoryPostProcessor在容器實際實例化任何其它的bean以前讀取配置元數據,並有可能修改它。 換句話說BeanFactoryPostProcessor是bean工廠的bean屬性處理容器,咱們能夠管理bean工廠內全部的beandefinition(未實例化)數據,能夠爲所欲爲的修改屬性。

例如:咱們修改ConnectBean類,在其中新增init方法,而後咱們再BeanFactoryPostProcessor中把init方法設置爲ConnectBean的初始方法。

@Component
public class ConnectBean {

    private Integer count;

    public void setCount(int count) {
        this.count = count;
    }

    public Integer getCount() {
        return this.count;
    }

    public void init() {
        System.out.println("這是ConnectBean的初始化方法:init");
    }

}

定義BeanFactoryPostProcessor接口實現類,實現接口方法postProcessBeanFactory( )改方法參數ConfigurableListableBeanFactory對象,該對象能夠操做全部bean工廠中的BeanDefinition(bean元數據)。你也能夠經過設置@order設定beanFactoryPostProcessor的執行順序。

@Order(value = 1)
@Component
public class ConnectBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        System.out.println("調用自定義BeanFactoryPostProcessor" + beanFactory.getClass());
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("connectBean");
        beanDefinition.setInitMethodName("init");
        System.out.println("調用自定義BeanFactoryPostProcessor結束" + beanFactory.getClass());

    }

}

執行結果:能夠看出BeanFactoryPostProcessor的調用早於BeanPostProcessor的調用。

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)
2020-03-24 09:48:21.732  INFO 186536 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 186536 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-24 09:48:21.734  INFO 186536 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
調用自定義BeanFactoryPostProcessorclass org.springframework.beans.factory.support.DefaultListableBeanFactory
調用自定義BeanFactoryPostProcessor結束class org.springframework.beans.factory.support.DefaultListableBeanFactory
2020-03-24 09:48:22.540  INFO 186536 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-03-24 09:48:22.548  INFO 186536 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-24 09:48:22.548  INFO 186536 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-24 09:48:22.614  INFO 186536 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-24 09:48:22.614  INFO 186536 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 841 ms
給ConnectBean設置初始count
這是ConnectBean的初始化方法:init
給ConnectBean設置count完成100
2020-03-24 09:48:22.742  INFO 186536 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-24 09:48:22.865  INFO 186536 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-24 09:48:22.867  INFO 186536 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.417 seconds (JVM running for 1.76)

5. ImportBeanDefinitionRegistrar

@Import註解的參數能夠是一個類,也能夠是一個ImportSelector接口,也能夠是ImportBeanDefinitionRegistrar接口,若是是一個類就會建立該類對應的實例到IOC容器;若是是一個 ImportSelector接口就會根據接口邏輯篩選知足條件的類建立bean實例;若是是ImportBeanDefinitionRegistrar也會根據接口實現方法的邏輯建立bean實例。

官方解釋:

1. 當處理Java編程式配置類(使用了@Configuration的類)的時候,ImportBeanDefinitionRegistrar接口的實現類能夠註冊額外的bean definitions。

2. ImportBeanDefinitionRegistrar接口的實現類必須提供給@Import註解或者是ImportSelector接口返回值

3. ImportBeanDefinitionRegistrar接口的實現類可能還會實現下面org.springframework.beans.factory.Aware接口中的一個或者多個,它們各自的方法優先於ImportBeanDefinitionRegistrar的registerBeanDefinitions( )f方法被調用。

手動把類註冊成bean實例

首先定義一個類,後面該類會被註冊到bean容器裏面

public class CollectService {

    public void hello() {
        System.out.println("hello ! I am collect");
    }
}

自定義ImportBeanDefinitionRegistrar類,實現手動註冊bean

public class CollectImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * 
     * @author jiafeng
     * @date 2020年3月24日 下午3:43:46
     * @param importingClassMetadata 當前類的註解信息
     * @param registry 註冊類
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(CollectService.class);
        beanDefinition.setSynthetic(true);  
        registry.registerBeanDefinition("collectService", beanDefinition);
    }
}

最後定義一個配置類,用於發現上面的手動註冊類

@Configuration
@Import(CollectImportBeanDefinitionRegistrar.class)
public class CollectConfiguration {

}

springboot啓動類裏面調用被手動註冊的bean實例方法,驗證是否註冊成功

public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);

        System.out.println("啓動完成");
        CollectService collectService = (CollectService)applicationContext.getBean("collectService");
        collectService.hello();

    }

執行結果:調用被註冊類的hello( )方法。

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

2020-03-24 16:14:10.498  INFO 192624 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 192624 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-24 16:14:10.500  INFO 192624 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
org.springframework.beans.factory.support.DefaultListableBeanFactory
2020-03-24 16:14:11.304  INFO 192624 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-03-24 16:14:11.312  INFO 192624 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-24 16:14:11.312  INFO 192624 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-24 16:14:11.379  INFO 192624 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-24 16:14:11.379  INFO 192624 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 850 ms
2020-03-24 16:14:11.498  INFO 192624 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-24 16:14:11.607  INFO 192624 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-24 16:14:11.609  INFO 192624 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.374 seconds (JVM running for 1.71)
啓動完成
hello ! I am collect

自定義註解實現spring把類加載成bean實例

spring 官方也是經過ImportBeanDefinitionRegistrar實現@Component、@service等註解的動態注入機制。具體過程以下:

首先定義一個註解@Zyservice,配置該註解的類會被註冊到bean容器中。

/**
 * @description <描述>
 * @author jiafeng
 * @date 2020年3月24日 下午6:32:46
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Zyservice {

}

定義使用該註解的類,該類會被註冊到容器中

/**
 * @description <描述>
 * @author jiafeng
 * @date 2020年3月24日 下午6:34:58
 */
@Zyservice
public class ZycfcService {

    public void hello() {
        System.out.println("zycfcService 註冊成功");
    }
}

定義ImportBeanDefinitionRegistrar接口實現類,實現掃描將配置@Zyservice註解的類實例化到bean容器中

/**
 * @description <描述>
 * @author jiafeng
 * @date 2020年3月24日 下午6:35:35
 */
public class ZycfcImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        Map<String, Object> annotationAttributes =
            importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());

        String[] basePackages = (String[])annotationAttributes.get("basePackages");

        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(Zyservice.class));// 掃描對應註解的類
        scanner.scan(basePackages);

    }

}

定義Configuration配置類引入ZycfcImportBeanDefinitionRegistrar註冊器,配置掃描包路徑

@Configuration
@ComponentScan(basePackages = {"com.example.demo.bean"})
@Import(ZycfcImportBeanDefinitionRegistrar.class)
public class ZycfcServiceConfiguration {

}

啓動類,springboot啓動以後從bean容器中獲取ZycfcService類實例,調用方法驗證是否動態注入到bean容器中

public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);

        System.out.println("啓動完成");
        CollectService collectService = applicationContext.getBean(CollectService.class);
        collectService.hello();

        ZycfcService zycfcService = applicationContext.getBean(ZycfcService.class);
        zycfcService.hello();

    }

執行結果:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)
2020-03-24 19:03:27.374  INFO 190632 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 190632 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-24 19:03:27.375  INFO 190632 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
org.springframework.beans.factory.support.DefaultListableBeanFactory
2020-03-24 19:03:28.179  INFO 190632 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-03-24 19:03:28.187  INFO 190632 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-24 19:03:28.187  INFO 190632 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-24 19:03:28.259  INFO 190632 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-24 19:03:28.259  INFO 190632 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 856 ms
2020-03-24 19:03:28.377  INFO 190632 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-03-24 19:03:28.487  INFO 190632 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-24 19:03:28.490  INFO 190632 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.392 seconds (JVM running for 1.716)
啓動完成
hello ! I am collect
zycfcService 註冊成功

6. BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor類能夠當作是ImportBeanDefinitionRegistrar類和BeanFactoryPostProcessor類的綜合體,既能夠 實現BeanDefinition的註冊操做將類註冊到bean容器中,也能夠實現BeanDefinition的修改、移除操做。

例如:首先建立類,用於被手動註冊到容器中

/**
 * @description <描述>
 * @author jiafeng
 * @date 2020年3月24日 下午8:02:12
 */
public class GreatService {

    public void test() {
        System.out.println("這是GreatService的方法:test");
    }
}

定義BeanDefinitionRegistryPostProcessor的實現類,實現postProcessBeanDefinitionRegistry和postProcessBeanFactory方法實現手動註冊bean實例功能和修改bean元數據功能。

/**
 * @description <描述>
 * @author jiafeng
 * @date 2020年3月24日 下午7:46:22
 */
@Component
public class GreatBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //從beanFactory中取出bean元數據,進行修改或其餘操做
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("greatService");
        beanDefinition.setInitMethodName("test");
        System.out.println("調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanFactory( )方法"+beanDefinition.getAttribute("type"));
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        //建立bean元數據並註冊到容器中
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(GreatService.class);
        beanDefinition.setAttribute("type", "test");
        registry.registerBeanDefinition("greatService", beanDefinition);
        System.out.println("調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry( )方法");
    }

}

執行啓動類從執行結果能夠看到,先執行postProcessBeanDefinitionRegistry( )方法註冊bean實例到容器中,而後執行postProcessBeanFactory( )方法從beanFactory中能夠取出beanDefinition進行修改操做。

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

2020-03-24 20:16:36.621  INFO 194764 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 194764 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-24 20:16:36.623  INFO 194764 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry( )方法
調用GreatBeanDefinitionRegistryPostProcessor的postProcessBeanFactory( )方法test
org.springframework.beans.factory.support.DefaultListableBeanFactory
2020-03-24 20:16:37.424  INFO 194764 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-03-24 20:16:37.432  INFO 194764 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-03-24 20:16:37.432  INFO 194764 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.31]
2020-03-24 20:16:37.499  INFO 194764 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-03-24 20:16:37.499  INFO 194764 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 847 ms
2020-03-24 20:16:37.619  INFO 194764 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
這是GreatService的方法:test
2020-03-24 20:16:37.731  INFO 194764 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-24 20:16:37.733  INFO 194764 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.381 seconds (JVM running for 1.718)
啓動完成
相關文章
相關標籤/搜索