Pit 1: Dependent beans do not use BeanPostProcessor
Other URL
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 BoxWhen 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
Introduction
How to register BeanPostProcessor in Spring container? There are two main ways:
- 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.
- 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:
- 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.
- 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
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.