swiftR

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));

}

/**
* 生成bean的名字
* @param interfaceName
* @param isSwitchOn
* @return
*/
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();
}
//判断switchName是否为空
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();
}
}

运行结果如下,这里在不同实现类是使用自己想要的方法生成动态代理失实例

1
2
v1 say Hello
v2 say Hi