BeanPostProcessor 简介 BeanPostProcessor 是spring ioc提供的一个接口,接口声明如下:
1 2 3 4 5 6 7 8 9 10 11 public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException { return bean; } }
该接口有两个回调方法。第一个是在bean初始化方法前调用,第二个是在bean初始化方法后调用。 当一个 BeanPostProcessor 的实现类注册到 Spring IOC 容器后,对于该 Spring IOC 容器所创建的每个 bean 实例在初始化方法,都会通过BeanPostProcessor。
BeanPostProcessor 示例
这里写一个示例来体会BeanPostProcessor的妙用
service接口 1 2 3 4 5 6 7 8 9 @RoutingSwitch ("hello" )public interface HelloService { @RoutingSwitch ("A" ) void sayHello () ; @RoutingSwitch ("B" ) void sayHi () ; }
这里@RoutingSwitch是我们自定义的一个接口,通过这个注解我们可以控制到底要不要使用BeanPostProcessor,而下面方法上的@RoutingSwitch可以选择方法的版本,这样实现该Service接口的类中的方法可以混合使用,这里以A和B为例,可以看做是AB计划 @RoutingSwitch实现如下
1 2 3 4 5 6 7 8 @Target ({ElementType.TYPE,ElementType.METHOD})@Retention (RetentionPolicy.RUNTIME)@Documented @Component public @interface RoutingSwitch { String value () default "" ; }
上面的@Target注解表示该注解可以定义到类和方法上
这里定义了以RoutingInjected的注解,该注解和@Autowired类似
1 2 3 4 5 6 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface RoutingInjected { }
service 接口的实现类 接下来编写Service的实现类,这里有V1和V2两个版本的
1 2 3 4 5 6 7 8 9 10 11 12 @Service public class HelloServiceV1 implements HelloService { @Override public void sayHello () { System.out.println("v1 say Hello" ); } @Override public void sayHi () { System.out.println("v1 sayHi" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 @Service public class HelloServiceV2 implements HelloService { @Override public void sayHello () { System.out.println("v2 say Hello" ); } @Override public void sayHi () { System.out.println("v2 say Hi" ); } }
实现BeanPostProcessor 该实现代码如下 在 RoutingBeanPostProcessor 类中,我们在 postProcessAfterInitialization 方法中通过检查 bean 中是否存在声明了 RoutingInjected 注解的属性,如果发现存在该注解则给该属性注入一个动态代理类实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @Component public class RoutingBeanPostProcessor implements BeanPostProcessor { @Autowired private ApplicationContext applicationContext; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * bean 注入之前的操作 * @param bean * @param beanName * @return * @throws BeansException */ @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()){ System.out.println("RoutingInjected field must be declared as an interface"); } try { this.handleRoutingInjected(f,bean,f.getType()); } catch (IllegalAccessException e) { e.printStackTrace(); } } } return bean; } 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){ System.out.println("lzl"); Object proxy = RoutingBeanProxyFactory.createProxy(type,candidates); field.set(bean,proxy); }else { throw new IllegalArgumentException("find more than 2 beans for type"+type); } } }
动态代理类 RoutingBeanProxyFactory 类功能就是生成一个代理类实例,代理类的逻辑也比较简单。版本路由支持到方法级别,即优先检查方法是否存在路由配置 RoutingSwitch,方法不存在配置时才默认使用类路由配置 我们注入的Bean都是通过这个代理类来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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 + (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 (switchName.equals("" )){ throw new IllegalStateException("value is blank" +method.getName()); } return invocation.getMethod().invoke(getTargetBean(switchName)); } public Object getTargetBean (String switchName) { boolean switchOn; if ("A" .equals(switchName)){ switchOn = false ; }else if ("B" .equals(switchName)){ switchOn = true ; }else { switchOn = false ; } return switchOn ? beanOfSwitchOn : beanOfSwitchOff; } } }
测试 这个测试类写的很简单,主要是测试不同实现类中的不同方法是否被注入到实例中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @SpringBootTest @RunWith (SpringRunner.class)public class BeanPostProcessorTest { @RoutingInjected HelloService helloService; @Test public void test () { helloService.sayHello(); helloService.sayHi(); } }
运行结果如下,这里在不同实现类是使用自己想要的方法生成动态代理失实例