所有分类
  • 所有分类
  • 未分类

Spring-BeanPostProcessor-注意问题/坑

简介

本文用实例来介绍Spring的后置处理器(BeanPostProcessor)的一些坑,开发时要注意这些问题。

坑1:依赖的bean不走BeanPostProcessor

简介

在BeanPostProcessor的实现类中,依赖了其他的bean,那么被依赖的bean被创建时,将不会执行它所在的BeanPostProcessor实现类实现的方法。

因为由于BeanPostProcessor的实现类依赖于其他bean,所以这个bean需要在PostBean之前创建完成,这也就意味着在这个bean创建时,BeanPostProcessor的实现类还未初始化完成,所以不会调用它的方法。

以下用实例进行验证

BeanPostProcessor实现类

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
    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;
    }
}

被依赖的Bean

package com.example.processor;

import org.springframework.stereotype.Component;

@Component
public class MyBean {
}

测试

启动后,没有相关打印。

若将@Autowired  MyBean myBean删掉,则启动之后,会打印出:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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)

坑2:无法使用AOP

见:Spring-BeanPostProcessor无法使用AOP-原因/解决方案 – 自学精灵

坑3:注册方式及其限制 

简介

如何将BeanPostProcessor注册到Spring容器中?方式主要有如下两种:

  1. 将其声明在Spring的配置类或xml文件中,作为普通的bean,让ApplicationContext对象去加载它,这样它就被自动注册到容器中了。而且Spring容器会对BeanPostProcessor的实现类做特殊处理,即会将它们挑选出来,在加载其他bean前,优先加载BeanPostProcessor的实现类。
  2. 使用ConfigurableBeanFactory接口的addBeanPostProcessor方法手动添加。ApplicationContext对象中组合了一个ConfigurableBeanFactory的实现类对象。但这样添加BeanPostProcessor有一些缺点,如下:
    1. 一创建Spring容器,配置文件中的单例bean就会被加载,此时addBeanPostProcessor方法还没执行,那手动添加的BeanPostProcessor就无法作用于这些bean,所以手动添加的BeanPostProcessor只能作用于延迟加载的bean,或者非单例bean。
    2. Ordered接口的作用将失效,而是以注册的顺序执行。前面提过,Ordered接口用来指定多个BeanPostProcessor实现的方法的执行顺序。这是Spring官方文档中提到的: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.

坑4:使用@Bean配置的限制

简介

如果使用Java类的方式配置Spring,并使用@Bean声明一个工厂方法返回bean实例,那么返回值的类型必须是BeanPostProcessor类型,或者等级低于BeanPostProcessor的类型。 

演示 

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;
    }
}

在配置类中,声明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();
    }
}

 以上三种方式都可以让Spring容器创建MyProcessor实例对象,因为MyProcessor实现了BeanPostProcessor和Ordered接口,所以它的对象也是这两种类型的对象。但是需要注意,上面三种方式中,只有第一种和第二种方式,会让Spring容器将MyProcessor当作BeanPostProcessor处理;而第三种方式,则会被当作一个普通Bean处理,实现BeanPostProcessor的两个方法都不会被调用。因为在MyProcessor的继承体系中,Ordered和BeanPostProcessor是同级别的,Spring无法识别出这个Ordered对象,也是一个BeanPostProcessor对象;但是使用MyProcessor却可以,因为MyProcessor类型就是BeanPostProcessor的子类型。所以,在使用@Bean声明工厂方法返回BeanPostProcessor实现类对象时,返回值必须是BeanPostProcessor类型,或者更低级的类型。Spring官方文档中,这一部分的内容如下:

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. ​

0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录