Hook method and hook interface in spring

spring framework has a high scalability. We can extend spring flexibly through these interfaces.

1. Aware interface

Spring provides a variety of Aware interfaces, which is convenient to get the current running environment from the context. Several common sub interfaces are: ApplicationContextAware, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, EnvironmentAware, ImportAware, MessageSourceAware, ServletConfigAware, ServletContextAware, etc.

For example: to get the bean object through the application context, you can do the following.

@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 and DisposableBean

The InitializingBean interface has only one method, afterpropertieset(): it will be called after BeanFactory has set all Bean properties. It can do some asset initialization operations.

The DisposableBean interface has only one method, destroy(): when a single instance is destroyed, it is called by BeanFactory, which can do some asset release operations.

Example:

@Component
public class ConnectBean implements InitializingBean, DisposableBean {

    @Override
    public void destroy() throws Exception {
        System.out.println("When a single case is destroyed by BeanFactory call");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("When BeanFactory Set all Bean Property is called after");
    }

}

3. BeanPostProcessor

BeanPostProcessor can modify the properties in the bean instance after the bean initialization (note that the init method is not included in the initialization), and do some specific operations on the bean instance.

For example: define a bean first, including the property count. We will modify the value of the property through the BeanPostProcessor.

@Component
public class ConnectBean{

    private Integer count;

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

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

Define BeanPostProcessor interface implementation class implementation interface method postProcessBeforeInitialization after bean instance initialization and postProcessAfterInitialization after bean initialization, first find ConnectBean instance and then setCount() method to call bean instance.

@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("to ConnectBean Set initial 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("to ConnectBean Set up count complete" + ((ConnectBean)bean).getCount());
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

Call start method:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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
Set initial count for ConnectBean
Set count for ConnectBean to complete 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

The Spring IoC container allows BeanFactoryPostProcessor to read configuration metadata and possibly modify it before the container actually instantiates any other bean. In other words, BeanFactoryPostProcessor is the bean property processing container of bean factory. We can manage all beandefinition (uninstantiated) data in bean factory and modify properties as we like.

For example, we modify the ConnectBean class, add the init method in it, and then set the init method to the initial method of ConnectBean in BeanFactoryPostProcessor.

@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("This is ConnectBean Initialization method for: init");
    }

}

Define the BeanFactoryPostProcessor interface implementation class, and implement the interface method postProcessBeanFactory() to change the method parameter ConfigurableListableBeanFactory object, which can operate on the BeanDefinition (bean metadata) in all bean factories. You can also set the execution order of beanfactorypostprocessor by setting @ order.

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

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

        System.out.println("Call custom BeanFactoryPostProcessor" + beanFactory.getClass());
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("connectBean");
        beanDefinition.setInitMethodName("init");
        System.out.println("Call custom BeanFactoryPostProcessor End" + beanFactory.getClass());

    }

}

Execution result: it can be seen that the call of BeanFactoryPostProcessor is earlier than the call of 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
Call the custom BeanFactoryPostProcessorclass org.springframework.beans.factory.support.DefaultListableBeanFactory
Call the custom BeanFactoryPostProcessor to end 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
Set initial count for ConnectBean
This is the initialization method of ConnectBean: init
Set count for ConnectBean to complete 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

@The parameter of the Import annotation can be a class, an ImportSelector interface, or an importbeandefinitionregister interface. If it is a class, the corresponding instance of the class will be created to the IOC container; if it is an ImportSelector interface, the bean instance will be created according to the class meeting the conditions filtered by the interface logic; if it is an importbeandefinitionregister, the bean instance will also be created Create a bean instance based on the logic of the interface implementation method.

Official explanation:

1. When processing Java programming Configuration classes (classes using @ Configuration), the implementation class of the importbeandefinitionregister interface can register additional bean definitions.

2. The implementation class of the importbeandefinitionregister interface must provide the @ Import annotation or the ImportSelector interface return value.

3. The implementation class of the importbeandefinitionregister interface may also implement one or more of the following org.springframework.beans.factory.Aware interfaces. Their respective methods take precedence over the registerbeandefinitions() F method of the importbeandefinitionregister.

Manually register the class as a bean instance

First, define a class, which will be registered in the bean container

public class CollectService {

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

Customize the importbeandefinitionregister class to register beans manually

public class CollectImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * 
     * @author jiafeng
     * @date 2020 March 24, 2004 3:43:46 PM
     * @param importingClassMetadata Annotation information for the current class
     * @param registry Registration class
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(CollectService.class);
        beanDefinition.setSynthetic(true);  
        registry.registerBeanDefinition("collectService", beanDefinition);
    }
}

Finally, a configuration class is defined to discover the above manual registration class

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

}

In the springboot startup class, the manually registered bean instance method is called to verify whether the registration is successful

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

        System.out.println("Start up");
        CollectService collectService = (CollectService)applicationContext.getBean("collectService");
        collectService.hello();

    }

Execution result: the hello() method of the registered class is called.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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)
//Start up
hello ! I am collect

Custom annotation implementation spring loads classes into bean instances

spring also implements the dynamic injection mechanism of @ Component, @ service and other annotations through importbeandefinitionregister. The specific process is as follows:

First, define an annotation @ Zyservice, and the class configured with the annotation will be registered in the bean container.

/**
 * @description <Description >
 * @author jiafeng
 * @date 2020 March 24, 2006 6:32:46 PM
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Zyservice {

}

Define the class using the annotation, which will be registered in the container

/**
 * @description <Description >
 * @author jiafeng
 * @date 2020 March 24, 2006 6:34:58 PM
 */
@Zyservice
public class ZycfcService {

    public void hello() {
        System.out.println("zycfcService login was successful");
    }
}

Define the importbeandefinitionregister interface implementation class, implement the scan and instantiate the class with @ Zyservice annotation into the bean container

/**
 * @description <Description >
 * @author jiafeng
 * @date 2020 March 24, 2006 6:35:35 PM
 */
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));// Scan the corresponding annotation class
        scanner.scan(basePackages);

    }

}

Define the Configuration configuration class, introduce the ZycfcImportBeanDefinitionRegistrar registrar, and configure the scan package path

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

}

After the springboot is started, get the instance of ZycfcService class from the bean container, and call the method to verify whether it is dynamically injected into the bean container

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

        System.out.println("Start up");
        CollectService collectService = applicationContext.getBean(CollectService.class);
        collectService.hello();

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

    }

Execution result:

 .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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)
//Start up
hello ! I am collect
zycfcService login was successful

Tags: Programming Tomcat Spring Apache jvm

Posted on Tue, 24 Mar 2020 07:59:11 -0400 by Verrou