SpringBoot实践-BeanPostProcessor的作用和妙用

BeanPostProcessor的用法 BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。BeanPostProcessor的源码如下:

public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }

其中postProcessBeforeInitialization方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization方法会在每个bean对象的初始化方法调用之后被回调。
提出的问题 在Spring开发过程中,存在同一个接口有多个实现类的情况,根据不同的应用场景,通常在具体调用的地方来选择不同的接口实现类,虽然可以在抽象出一个工厂方法,但是还是感觉不够优雅。如果通过注解的方式解决这个问题呢,Spring的BeanPostProcessor是个好的选择。
具体实现 声明接口
public interface HelloService{ public void sayHello(); }

接口实现类1
@Service public class HelloServiceImpl1 implements HelloService { @Override public void sayHello() { System.out.println("你好我是HelloServiceImpl1"); } }

接口实现类2
@Service public class HelloServiceImpl2 implements HelloService { @Override public void sayHello() { System.out.println("你好我是HelloServiceImpl2"); } }

自定义注解
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface RountingInjected { String value() default "helloServiceImpl1"; }

自定义的BeanPostProcessor类
@Component public class HelloServiceInjectProcessor implements BeanPostProcessor {@Autowired private ApplicationContext applicationContext; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Class targetCls = bean.getClass(); Field[] targetFld = targetCls.getDeclaredFields(); for (Field field : targetFld) { //找到制定目标的注解类 if (field.isAnnotationPresent(RountingInjected.class)) { if (!field.getType().isInterface()) { throw new BeanCreationException("RoutingInjected field must be declared as an interface:" + field.getName() + " @Class " + targetCls.getName()); } try { this.handleRoutingInjected(field, bean, field.getType()); } catch (IllegalAccessException e) { e.printStackTrace(); } } } return bean; }/** * @param field * @param bean * @param type * @throws IllegalAccessException */ private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException { Map candidates = this.applicationContext.getBeansOfType(type); field.setAccessible(true); if (candidates.size() == 1) { field.set(bean, candidates.values().iterator().next()); } else if (candidates.size() == 2) { String injectVal = field.getAnnotation(RountingInjected.class).value(); Object proxy = RoutingBeanProxyFactory.createProxy(injectVal, type, candidates); field.set(bean, proxy); } else { throw new IllegalArgumentException("Find more than 2 beans for type: " + type); } } }

代理实现类
public class RoutingBeanProxyFactory {private final static String DEFAULT_BEAN_NAME = "helloServiceImpl1"; public static Object createProxy(String name, Class type, Map candidates) { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(type); proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(name, candidates)); return proxyFactory.getProxy(); }static class VersionRoutingMethodInterceptor implements MethodInterceptor { private Object targetObject; public VersionRoutingMethodInterceptor(String name, Map beans) { this.targetObject = beans.get(name); if (this.targetObject == null) { this.targetObject = beans.get(DEFAULT_BEAN_NAME); } }@Override public Object invoke(MethodInvocation invocation) throws Throwable { return invocation.getMethod().invoke(this.targetObject, invocation.getArguments()); } } }

SpringBoot入口类
@SpringBootApplication @MapperScan("com.lx.mapper") public class MlxcApplication { public static void main(final String[] args) { try (ConfigurableApplicationContext applicationContext = SpringApplication.run(MlxcApplication.class, args)) { HelloServiceTest helloService = applicationContext.getBean(HelloServiceTest.class); helloService.testSayHello(); } } }

总结 【SpringBoot实践-BeanPostProcessor的作用和妙用】上述是整个解决方案的示例流程,其核心思想就是根据自定义注解拦截要注入的接口实现类,运用java反射和代理的知识点来进行有效的实现类注入。

    推荐阅读