BeanPostProcessor实现AB测试

利用BeanPostProcessor的postProcessAfterInitialization方法在spring加载所有的类后,
过来实现接口的所有类,方法getBeansOfType获取接口的所有实现类
Map 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 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 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 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 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 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());
        }

        
    }
}

你可能感兴趣的:(BeanPostProcessor实现AB测试)