Bean life cycle-the pit of BeanPostProcessor

Pit 1: Dependent beans do not use BeanPostProcessor

Other URL

Talk about the BeanPostProcessor interface in Spring

Introduction

        In the implementation class of BeanPostProcessor, if other beans are dependent, when the dependent bean is created, the method implemented by the implementation class of BeanPostProcessor will not be executed.

        Because the implementation class of BeanPostProcessor depends on other beans, this bean needs to be created before PostBean, which means that when this bean is created, the implementation class of BeanPostProcessor has not been initialized yet, so its methods will not be called.

verification

BeanPostProcessor implementation class

package com.example.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyProcessor implements BeanPostProcessor {
    @Autowired
    MyBean myBean;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("postProcessBeforeInitialization==> " + "This is MyBean");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("postProcessAfterInitialization==> " + "This is MyBean");
        }
        return bean;
    }
}

Dependent bean

package com.example.processor;

import org.springframework.stereotype.Component;

@Component
public class MyBean {
}

test

After startup, there is no related printing.

If @Autowired MyBean myBean is deleted, after startup, it will print out:

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

2021-03-05 21:39:47.861  INFO 16100 --- [           main] com.example.DemoApplication              : Starting DemoApplication on DESKTOP-QI6B9ME with PID 16100 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot)
2021-03-05 21:39:47.864  INFO 16100 --- [           main] com.example.DemoApplication              : No active profile set, falling back to default profiles: default
2021-03-05 21:39:48.645  INFO 16100 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-03-05 21:39:48.652  INFO 16100 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-03-05 21:39:48.652  INFO 16100 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.35]
2021-03-05 21:39:48.730  INFO 16100 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-03-05 21:39:48.731  INFO 16100 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 825 ms
postProcessBeforeInitialization==> This is MyBean
postProcessAfterInitialization==> This is MyBean
2021-03-05 21:39:48.865  INFO 16100 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-05 21:39:48.998  INFO 16100 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-03-05 21:39:49.007  INFO 16100 --- [           main] com.example.DemoApplication              : Started DemoApplication in 1.557 seconds (JVM running for 2.638)

Pit 2: Cannot use AOP

Other URL

Talk in Spring BeanPostProcessor interface - agents according to Ang - blog
BeanPostProcessor in autowired lead to failure AOP | Big Box

When BeanPostProcessor start reliance Bean of "friendly fire" trap (is not eligible for getting processed by all ...) _ YourBatman-CSDN blog
remember a Spring configuration accident - fly Aung Snow - blog Park
business class can not be AOP proxy issues

the reason

Brief description

BeanPostProcessor and dependent beans may not be able to use AOP.

At this time, there will be a print message like this: trationDelegate$BeanPostProcessorChecker: Bean'myBean' of type [com.example.processor.MyBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Source location

This is printed by the BeanPostProcessorChecker class. Searching for it in the source code, it is found that this class is a static internal class of PostProcessorRegistrationDelegate.

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
            this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
        if (logger.isInfoEnabled()) {
            logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() +
                    "] is not eligible for getting processed by all BeanPostProcessors " +
                    "(for example: not eligible for auto-proxying)");
        }
    }
    return bean;
}

Reason for printing this log

If the number of BeanPostProcessors registered to the beanFactory is less than the total number of BeanPostProcessors. That is: if there are other post processors that are not ready, this bean (here, myBean) is instantiated. The reason why this bean is instantiated is generally: the implementation class of BeanPostProcessor references this bean, which leads to the instantiation of this bean.

The original words of the Spring official document :

BeanPostProcessor instances and AOP auto-proxying

Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessor instances and beans that they directly reference are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessor instances are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor instances nor the beans they directly reference are eligible for auto-proxying and, thus, do not have aspects woven into them.

For any such bean, you should see an informational log message: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).

translation:

        The classes that implement the BeanPostProcessor interface are special, and they will be treated differently by the container. All BeanPostProcessor instances and their directly referenced beans are instantiated at startup, as part of the special startup phase of the ApplicationContext. Then, all BeanPostProcessor instances are registered in a sorted manner and applied to the next beans. Because Spring's AOP automatic proxy is done by implementing the BeanPostProcessor interface, the implementation classes of BeanPostProcessor and the beans they directly reference do not meet the conditions of AOP automatic proxy, so they cannot be woven into AOP .

        For these beans, you will see some INFO level information: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).

Instance

recurrent

 BeanPostProcessor implementation class

        Deliberately implement the Ordered interface to simulate the initialization of MyProcessor earlier than other BeanPostProcessor implementation classes. Otherwise, it will not be easy to reproduce. When I tested it here, I designated it as the highest priority or the lowest priority, and the effect was the same. For priority, see: Bean Life Cycle-Summary of BeanPostProcessor _feiying0canglang's blog -CSDN blog
        In addition, the Ordered interface must be implemented here to specify the order, if @Order is used, it is invalid. Guess: @Order is also done by implementing the BeanPostProcessor interface, so it is also invalid.

package com.example.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Component
public class MyProcessor implements BeanPostProcessor, Ordered {
    @Autowired
    private MyBean myBean;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("postProcessBeforeInitialization==> " + "This is MyBean");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("postProcessAfterInitialization==> " + "This is MyBean");
        }
        return bean;
    }

    // 此方法用来测试AOP,作为切点
    public void testAOP() {
        System.out.println("testAOP方法(MyProcessor)");
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

Bean

package com.example.processor;

import org.springframework.stereotype.Component;

@Component
public class MyBean {
    // 此方法用来测试AOP,用作切点
    public void testAOP() {
        System.out.println("testAOP方法(MyBean)");
    }
}

section

package com.example.processor;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class BeanPostProcessorAspect {
    
	// 此方法织入PostBean的testAOP方法
    @Before("execution(* com.example.processor.MyProcessor.testAOP(..))")
    public void before() {
        System.out.println("before MyProcessor#testAOP");
    }

    // 此方法织入MyBean的testAOP方法
    @Before("execution(* com.example.processor.MyBean.testAOP(..))")
    public void before2() {
        System.out.println("before MyBean#testAOP");
    }
}

ApplicationContextHolder tool class

package com.example.processor;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
 
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;
 
    public void setApplicationContext(ApplicationContext context)
            throws BeansException {
        ApplicationContextHolder.context = context;
    }
 
    public static ApplicationContext getContext() {
        return context;
    }
}

Test class

package com.example.controller;

import com.example.processor.MyBean;
import com.example.processor.MyProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Autowired
    MyProcessor myProcessor;

    @Autowired
    MyBean myBean;

    @GetMapping("/test1")
    public String test1() {
        myProcessor.testAOP();
        myBean.testAOP();

        return "test1 success";
    }

    // @GetMapping("/test1")
    // public String test1() {
    //     MyProcessor myProcessor = ApplicationContextHolder.getContext().getBean(MyProcessor.class);
    //     myProcessor.testAOP();
    //     MyBean myBean = ApplicationContextHolder.getContext().getBean(MyBean.class);
    //     myBean.testAOP();
    //     return "test1 success";
    // }
}

test

1. Reproduce the phenomenon of inability to AOP

start up:

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

2021-03-06 12:23:36.622  INFO 14368 --- [           main] com.example.DemoApplication              : Starting DemoApplication on DESKTOP-QI6B9ME with PID 14368 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot)
2021-03-06 12:23:36.625  INFO 14368 --- [           main] com.example.DemoApplication              : No active profile set, falling back to default profiles: default
2021-03-06 12:23:37.237  INFO 14368 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'myBean' of type [com.example.processor.MyBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-06 12:23:37.492  INFO 14368 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-03-06 12:23:37.500  INFO 14368 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-03-06 12:23:37.500  INFO 14368 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.35]
2021-03-06 12:23:37.590  INFO 14368 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-03-06 12:23:37.590  INFO 14368 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 927 ms
2021-03-06 12:23:37.718  INFO 14368 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-06 12:23:37.847  INFO 14368 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-03-06 12:23:37.855  INFO 14368 --- [           main] com.example.DemoApplication              : Started DemoApplication in 1.553 seconds (JVM running for 2.454)

Visit: http://localhost:8080/test1

Result: (Neither the ordinary bean injected by the BeanPostProcessor implementation class nor the BeanPostProcessor implementation class are AOP) Description: The second method in the Controller is also the same result. 

testAOP方法(MyProcessor)
testAOP方法(MyBean)

 

2. Remove BeanPostProcessor to AOP

Remove the implements BeanPostProcessor of the BeanPostProcessor implementation class and remove the overridden methods. (Others remain the same, such as: still implement the Ordered interface)

The result is: (The ordinary bean injected by the BeanPostProcessor implementation class and the BeanPostProcessor implementation class are both AOP)

before MyProcessor#testAOP
testAOP方法(MyProcessor)
before MyBean#testAOP
testAOP方法(MyBean)

track

This office traces the code above " 1. Reproduce the phenomenon that AOP cannot be reproduced ", entrance: the source code of MyBean being instantiated

The instantiation entry point of the bean is: AbstractBeanFactory#doGetBean. Make a conditional breakpoint at this place:

Start the project and find that this breakpoint has been reached three times, that is, there are three places where I want to instantiate MyBean.

The first time: MyBean is referenced by the BeanPostProcessor implementation class

Second time: HelloController refers to MyBean

The third time: MyBean itself joins the container, of course it must be instantiated

solve

Method 1: Use delayed initialization

Code

The implementation class of BeanPostProcessor introduces MyBean like this:

@Lazy
@Autowired
private MyBean myBean;

test

At startup:

postProcessBeforeInitialization==> This is MyBean
postProcessAfterInitialization==> This is MyBean

Visit: http://localhost:8080/test1

Result (Ordinary beans injected by the BeanPostProcessor implementation class can be AOP, but the BeanPostProcessor implementation class is not AOP)

testAOP方法(MyProcessor)
before MyBean#testAOP
testAOP方法(MyBean)

Method 2: Use ApplicationContext 

Of course, this method is not easy to apply here. It can be used to inject Service when configuring Shiro.

private UserService getUserService() {
    return (UserService) applicationContext.getBean(UserService.class);
}

Pit 3: Registration methods and restrictions 

Other URL

Talk about the BeanPostProcessor interface in Spring

Introduction

How to register BeanPostProcessor in Spring container? There are two main ways:

  1. Declare it in the Spring configuration class or xml file, as a normal bean, let the ApplicationContext object to load it, so that it is automatically registered in the container. And the Spring container will do special processing for the implementation classes of BeanPostProcessor, that is, they will be selected, and the implementation classes of BeanPostProcessor will be loaded first before loading other beans.
  2. Use the addBeanPostProcessor method of the ConfigurableBeanFactory interface to manually add. An implementation class object of ConfigurableBeanFactory is combined in the ApplicationContext object. But adding BeanPostProcessor like this has some disadvantages , as follows:
    1. Once the Spring container is created, the singleton bean in the configuration file will be loaded. At this time, the addBeanPostProcessor method has not been executed, and the manually added BeanPostProcessor cannot act on these beans, so the manually added BeanPostProcessor can only act on delayed-loaded beans. , Or non-singleton bean.
    2. The role of the Ordered interface will be invalid, but executed in the order of registration. As mentioned earlier, the Ordered interface is used to specify the execution order of methods implemented by multiple BeanPostProcessors. This is mentioned in the official Spring documentation: While the recommended approach for BeanPostProcessor registration is through ApplicationContext auto-detection (as described above), it is also possible to register them programmatically against a ConfigurableBeanFactory using the addBeanPostProcessor method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however that BeanPostProcessor s added programmatically do not respect the Ordered interface. Here it is the order of registration that dictates the order of execution. Note also that BeanPostProcessor s registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.

Pit 4: Limitations of using @Bean configuration

Other URL

Talk about the BeanPostProcessor interface in Spring

Introduction

If you configure Spring in a Java class and use @Bean to declare a factory method to return a bean instance, the return value must be of type BeanPostProcessor, or a type lower than BeanPostProcessor. 

Demo 

package com.example.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

@Component
public class MyProcessor implements BeanPostProcessor, Ordered {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization==> " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization==> " + beanName);
        return bean;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

In the configuration class, there are several ways to declare MyProcessor 

@Configuration
public class BeanConfig {

	// 方式1:MyProcessor
    @Bean
    public MyProcessor myProcessor() {
        return new MyProcessor();
    }
    
    // 方式2:返回值为BeanPostProcessor
    @Bean
    public BeanPostProcessor myProcessor() {
        return new MyProcessor();
    }
    
    // 方式3:返回值为Ordered
    @Bean
    public Ordered postBean() {
        return new MyProcessor();
    }
}

        The above three methods can allow the Spring container to create MyProcessor instance objects. Because MyProcessor implements the BeanPostProcessor and Ordered interfaces, its objects are also objects of these two types. However, it should be noted that among the above three methods, only the first and second methods will make the Spring container treat MyProcessor as a BeanPostProcessor; and the third method will be treated as an ordinary Bean to implement the BeanPostProcessor Neither method will be called. Because in the inheritance system of MyProcessor, Ordered and BeanPostProcessor are at the same level, Spring cannot recognize this Ordered object, which is also a BeanPostProcessor object; but it is possible to use MyProcessor, because the MyProcessor type is a subtype of BeanPostProcessor. Therefore, when using the @Bean declaration factory method to return the BeanPostProcessor implementation class object, the return value must be of the BeanPostProcessor type, or a lower-level type. In the official Spring document, the content of this part is as follows:

Note that when declaring a BeanPostProcessor using an @Bean factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext won’t be able to autodetect it by type before fully creating it. Since a BeanPostProcessor needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.

 

Guess you like

Origin blog.csdn.net/feiying0canglang/article/details/114297964