美文网首页spring相关
SpringBoot实践-BeanPostProcessor的作

SpringBoot实践-BeanPostProcessor的作

作者: 飞狗未来 | 来源:发表于2018-12-03 14:46 被阅读0次

    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<String, Object> 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<String, Object> 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<String, Object> 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();
            }
        }
    }
    

    总结

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

    相关文章

      网友评论

        本文标题:SpringBoot实践-BeanPostProcessor的作

        本文链接:https://www.haomeiwen.com/subject/mzkycqtx.html