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)
启动完成
相关文章
相关标签/搜索