SpringBoot实践-BeanPostProcessor的作用和妙用
BeanPostProcessor的用法
BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中(具体为Bean初始化前后)会回调BeanPostProcessor中定义的两个方法。BeanPostProcessor的源码如下:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
其中postProcessBeforeInitialization方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization方法会在每个bean对象的初始化方法调用之后被回调。
提出的问题 在Spring开发过程中,存在同一个接口有多个实现类的情况,根据不同的应用场景,通常在具体调用的地方来选择不同的接口实现类,虽然可以在抽象出一个工厂方法,但是还是感觉不够优雅。如果通过注解的方式解决这个问题呢,Spring的BeanPostProcessor是个好的选择。
具体实现 声明接口
public interface HelloService{
public void sayHello();
}
接口实现类1
@Service
public class HelloServiceImpl1 implements HelloService {
@Override
public void sayHello() {
System.out.println("你好我是HelloServiceImpl1");
}
}
接口实现类2
@Service
public class HelloServiceImpl2 implements HelloService {
@Override
public void sayHello() {
System.out.println("你好我是HelloServiceImpl2");
}
}
自定义注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RountingInjected {
String value() default "helloServiceImpl1";
}
自定义的BeanPostProcessor类
@Component
public class HelloServiceInjectProcessor 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> targetCls = bean.getClass();
Field[] targetFld = targetCls.getDeclaredFields();
for (Field field : targetFld) {
//找到制定目标的注解类
if (field.isAnnotationPresent(RountingInjected.class)) {
if (!field.getType().isInterface()) {
throw new BeanCreationException("RoutingInjected field must be declared as an interface:" + field.getName()
+ " @Class " + targetCls.getName());
}
try {
this.handleRoutingInjected(field, bean, field.getType());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}/**
* @param field
* @param bean
* @param type
* @throws IllegalAccessException
*/
private void handleRoutingInjected(Field field, Object bean, Class type) throws IllegalAccessException {
Map candidates = this.applicationContext.getBeansOfType(type);
field.setAccessible(true);
if (candidates.size() == 1) {
field.set(bean, candidates.values().iterator().next());
} else if (candidates.size() == 2) {
String injectVal = field.getAnnotation(RountingInjected.class).value();
Object proxy = RoutingBeanProxyFactory.createProxy(injectVal, type, candidates);
field.set(bean, proxy);
} else {
throw new IllegalArgumentException("Find more than 2 beans for type: " + type);
}
}
}
代理实现类
public class RoutingBeanProxyFactory {private final static String DEFAULT_BEAN_NAME = "helloServiceImpl1";
public static Object createProxy(String name, Class type, Map candidates) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(type);
proxyFactory.addAdvice(new VersionRoutingMethodInterceptor(name, candidates));
return proxyFactory.getProxy();
}static class VersionRoutingMethodInterceptor implements MethodInterceptor {
private Object targetObject;
public VersionRoutingMethodInterceptor(String name, Map beans) {
this.targetObject = beans.get(name);
if (this.targetObject == null) {
this.targetObject = beans.get(DEFAULT_BEAN_NAME);
}
}@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return invocation.getMethod().invoke(this.targetObject, invocation.getArguments());
}
}
}
SpringBoot入口类
@SpringBootApplication
@MapperScan("com.lx.mapper")
public class MlxcApplication {
public static void main(final String[] args) {
try (ConfigurableApplicationContext applicationContext = SpringApplication.run(MlxcApplication.class, args)) {
HelloServiceTest helloService = applicationContext.getBean(HelloServiceTest.class);
helloService.testSayHello();
}
}
}
总结 【SpringBoot实践-BeanPostProcessor的作用和妙用】上述是整个解决方案的示例流程,其核心思想就是根据自定义注解拦截要注入的接口实现类,运用java反射和代理的知识点来进行有效的实现类注入。
推荐阅读
- Activiti(一)SpringBoot2集成Activiti6
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 不废话,代码实践带你掌握|不废话,代码实践带你掌握 强缓存、协商缓存!
- 六项精进20180530
- 数据库|SQL行转列方式优化查询性能实践
- 【Day31课后实践】
- springboot使用redis缓存
- springboot整合数据库连接池-->druid
- 2021—3—8日教练实践总结&呼吸练习&觉察日记