BeanPostProcessor实现AB测试

利用BeanPostProcessor的postProcessAfterInitialization方法在spring加载所有的类后,
过来实现接口的所有类,方法getBeansOfType获取接口的所有实现类
Map<String, Object> candidates = this.applicationContext.getBeansOfType(type);
通过设置代理类,多接口的多个实现可以通过注解来制定具体具体执行哪一类。

业务接口

@RoutingSwitch("hello.switch")
public interface HelloService{

    @RoutingSwitch("A")
    void sayHello();

    void sayHi();
}

业务实现类型V1

@Service
public class HelloServiceImplV1 implements HelloService{
     public void sayHello(){
          System.out.println("Hello from V1");
     }
     public void sayHi(){
          System.out.println("Hi from V1");
     }
}

业务实现类型V2

@Service
public class HelloServiceImplV2 implements HelloService{
     public void sayHello(){
          System.out.println("Hello from V2");
     }
     public void sayHi(){
          System.out.println("Hi from V2");
     }
}

自定义标识注解

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RoutingInjected{
    
}

@Retention(RetentionPolicy.SOURCE)
@Documented
@Component
public @interface RoutingSwitch{
    /**
     * 在配置系统中开关的属性名称,应用系统将会实时读取配置系统中对应开关的值来决定是调用哪个版本
     * @return
     */
     String value() default "";
}

利用BeanPostProcessor的postProcessAfterInitialization在spring系统加载bean后,
过滤处理有自定义注解的类型

@Component
public class RoutingBeanPostProcessor 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 clazz = bean.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            if (f.isAnnotationPresent(RoutingInjected.class)) {
                if (!f.getType().isInterface()) {
                    throw new BeanCreationException("RoutingInjected field must be declared as an interface:"
                            + f.getName() + " @Class " + clazz.getName());
                }
                try {
                    this.handleRoutingInjected(f, bean, f.getType());
                } catch (IllegalAccessException e) {
                    throw new BeanCreationException("Exception thrown when handleAutowiredRouting", e);
                }
            }
        }
        return bean;
    }

    private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException {
        //利用getBeansOfType方法获取,实现接口的所有类型
        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() > 1) {
            //有多个实现类时,创建一个代理来处理
            Object proxy = RoutingBeanProxyFactory.createProxy(type, candidates);
            field.set(bean, proxy);
        } else {
            throw new IllegalArgumentException("Find more than 2 beans for type: " + type);
        }
    }
}

RoutingBeanProxyFactory创建代理类,根据注解类型,获取对应的实现类,
所有实现接口的类型保存在candidates中,key为类的简单名称,值为对应实现类型。

public class RoutingBeanProxyFactory {

    public static Object createProxy(Class targetClass, Map<String, Object> beans) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(targetClass);
        proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(targetClass, beans));
        return proxyFactory.getProxy();
    }
    static class VersionRoutingMethodInterceptor implements MethodInterceptor {
        private String classSwitch;
        private Object beanOfSwitchOn;
        private Object beanOfSwitchOff;

        public VersionRoutingMethodInterceptor(Class targetClass, Map<String, Object> beans) {
            String interfaceName = StringUtils.uncapitalize(targetClass.getSimpleName());
            if(targetClass.isAnnotationPresent(RoutingSwitch.class)){
                this.classSwitch =((RoutingSwitch)targetClass.getAnnotation(RoutingSwitch.class)).value();
            }
            this.beanOfSwitchOn = beans.get(this.buildBeanName(interfaceName, true));
            this.beanOfSwitchOff = beans.get(this.buildBeanName(interfaceName, false));
        }
        
        private String buildBeanName(String interfaceName, boolean isSwitchOn) {
            return interfaceName + "Impl" + (isSwitchOn ? "V2" : "V1");
        }

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            Method method = invocation.getMethod();
            String switchName = this.classSwitch;
            if (method.isAnnotationPresent(RoutingSwitch.class)) {
                switchName = method.getAnnotation(RoutingSwitch.class).value();
            }
            if (StringUtils.isBlank(switchName)) {
                throw new IllegalStateException("RoutingSwitch's value is blank, method:" + method.getName());
            }
            return invocation.getMethod().invoke(getTargetBean(switchName), invocation.getArguments());
        }

        public Object getTargetBean(String switchName) {
            boolean switchOn;
            if ("A".equals(switchName)) {
                switchOn = false;
            } else if ("B".equals(switchName)) {
                switchOn = true;
            } else {
                //switchOn = FunctionSwitch.isSwitchOpened(switchName);
                switchOn = true;
            }
            return switchOn ? beanOfSwitchOn : beanOfSwitchOff;
        }
    }
}

直接注解具体实现类
this.targetObj = beans.get(this.classSwitch);//beans.get("HelloServiceImplV1")

public class RoutingBeanProxyFactory2 {

    public static Object createProxy(Class targetClass, Map<String, Object> beans) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setInterfaces(targetClass);
        proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(targetClass, beans));
        return proxyFactory.getProxy();
    }
    static class VersionRoutingMethodInterceptor implements MethodInterceptor {
        private String classSwitch;
        private Object targetObj;

        public VersionRoutingMethodInterceptor(Class targetClass, Map<String, Object> beans) {
            String interfaceName = StringUtils.uncapitalize(targetClass.getSimpleName());
            if(targetClass.isAnnotationPresent(RoutingSwitch.class)){
                this.classSwitch =((RoutingSwitch)targetClass.getAnnotation(RoutingSwitch.class)).value();
            }

            this.targetObj = beans.get(this.classSwitch);//beans.get("HelloServiceImplV1")
        }
        
        

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            Method method = invocation.getMethod();
            String switchName = this.classSwitch;
            if (method.isAnnotationPresent(RoutingSwitch.class)) {
                switchName = method.getAnnotation(RoutingSwitch.class).value();
            }
            
            if (StringUtils.isBlank(switchName)) {
                throw new IllegalStateException("RoutingSwitch's value is blank, method:" + method.getName());
            }
            
            return invocation.getMethod().invoke(targetObj, invocation.getArguments());
        }

        
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Java OOP 什么是面向对象思想? 把一组数据和处理他们的方法组成对象(object),把相同行为的对象归纳为...
    chonglingliu阅读 798评论 0 1
  • 大家看看作的诗怎么样,嘿嘿。能看出什么特别的吗?告诉大家一个秘密,秘密就在诗中哦! 一分难减亦难加, 萧艾转肥兰蕙...
    花小凡阅读 1,056评论 0 1
  • ⑴自《诗境浅说》后,唐诗的选本最好的可能就是由马茂元先生评注的《唐诗选》了。在修订版的序言里,赵昌平记起马茂元先生...
    书山寻路阅读 337评论 0 0