Get the App
SLTechnology News&Howtos  ›  Internet Technology  › 

The method of using BeanPostProcessor to realize AB Test in Spring

Shulou Source: shulou.com Published: 2022-06-02 09:24:14 09月11日 Update

This article mainly explains "Spring uses BeanPostProcessor to achieve AB testing". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "Spring uses BeanPostProcessor to achieve AB testing".

Step one:

Create the interface, implementation class, controller to implement the AB test

RoutingSwitch ("hello.switch") public interface HelloService {@ RoutingSwitch ("B") String sayHello (); @ RoutingSwitch ("A") String sayHi ();} @ Servicepublic class HelloServiceImplV1 implements HelloService {@ Override public String sayHello () {String helloV1= "hello from V1"; System.out.println ("hello from V1"); return helloV1;} @ Override public String sayHi () {String hiV1= "hi from V1" System.out.println ("hi from V1"); return hiV1;}} @ Servicepublic class HelloServiceImplV2 implements HelloService {@ Override public String sayHello () {String helloV2 = "hello from V2"; System.out.println ("hello from V2"); return helloV2;} @ Override public String sayHi () {String hiV2 = "hi from V2"; System.out.println ("hi from V2") Return hiV2; @ RestController@RequestMapping ("/ test") public class HelloControllerV2 {@ RoutingInject private HelloService helloService; @ GetMapping ("/ hello") public String sayHello () {return helloService.sayHello ();} @ GetMapping ("/ hi") public String sayHi () {return helloService.sayHi ();}}

Step 2:

Create the RoutingBeanPostProcessor class to implement the interface BeanPostProcessor. When registering controller's bean, create a dynamic proxy class implementation class for the interface annotated with @ RoutingInject. When using this interface, create the interface implementation class through the invoke method.

Set dynamic proxy class comments on the interface:

@ Target ({ElementType.FIELD}) @ Retention (RetentionPolicy.RUNTIME) @ Documented@Componentpublic @ interface RoutingInject {}

Switch setting notes:

Target ({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @ Retention (RetentionPolicy.RUNTIME) @ Documented@Componentpublic @ interface RoutingSwitch {String value ();}

After initialization of bean, post-processing is performed for annotation classes that use RoutingInject

@ Componentpublic 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 (RoutingInject.class)) {if (! f.getType (). IsInterface ()) {throw new BeanCreationException ("RoutingInject field must be declared as an interface:" + "@ 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 {Map candidates = applicationContext.getBeansOfType (type); field.setAccessible (true); if (candidates.size () = = 1) {field.set (bean, candidates.entrySet (). Iterator (). Next ());} else if (candidates.size () = = 2) {Object proxy = RoutingBeanProxyFactory.createProxy (type, candidates) Field.set (bean, proxy);} else {throw new IllegalAccessException ("Find more bean 2 bean for type:" + type);}

Agent engineering implementation class:

Public class RoutingBeanProxyFactory {public static Object createProxy (Class targetClass, Map beans) {ProxyFactory proxyFactory = new ProxyFactory (); proxyFactory.setInterfaces (new Class [] {targetClass}); proxyFactory.addAdvice (new VersionRoutingMethodInterceptor (targetClass, beans)); return proxyFactory.getProxy ();} static class VersionRoutingMethodInterceptor implements MethodInterceptor {private String classSwitch; private Object beanSwitchOn; private Object beanSwitchOff 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.beanSwitchOn = beans.get (this.buildBeanName (interfaceName, true)) This.beanSwitchOff = 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 (getTargetName (switchName), invocation.getArguments ());} public Object getTargetName (String switchName) {boolean switchOn If (RoutingVersion.A.name (). Equals (switchName)) {switchOn = false;} else {switchOn = true;} return switchOn? BeanSwitchOn: beanSwitchOff;}} enum RoutingVersion {A,B}}

Step 3:

The test interface is normal.

Thank you for your reading, the above is the content of "Spring uses BeanPostProcessor to achieve AB testing". After the study of this article, I believe you have a deeper understanding of the problem of Spring using BeanPostProcessor to achieve AB testing, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Tags: Testing interface method annotation agent learning content dynamic processing interface that is engineering ideas situations articles more knowledge knowledge points articles follow Apple Docker Huawei Linux macOS MariaDB Microsoft MySQL NVidia OPPO Reno MySQL Shulou Tech Info Linux Xiaomi Shulou Technology