在spring中關於對bean的擴張可以分爲兩種:
1 基於所有bean:可以使用beanfactorypostproccessor進行修改,但是這種修改是全局的,也就是所有的bean都會被進行修改
2 基於單個bean:只針對單個特定的bean的實例化修改,這種情況可以使用BeanPostProcessor,也是接下來要簡單介紹和使用的
從spring源碼的來看,主要可以包含下面的幾個流程
1. 初始化時,spring容器有特別處理,會直接調用beanFactory.addBeanPostProcessor進行註冊(例如AbstractApplicationContext類的prepareBeanFactory方法中就有);
2. 找出所有實現了BeanPostProcessor接口的bean,註冊到容器,註冊順序如下:
第一:實現了PriorityOrdered接口的,排序後;
第二:實現了Ordered接口的,排序後;
第三:既沒實現PriorityOrdered接口,也沒有實現Ordered接口的;
第四:實現了MergedBeanDefinitionPostProcessor接口的(這些也按照PriorityOrdered、Ordered等邏輯拍過續);
第五:實例化一個ApplicationListenerDetector對象;
3. 實例化bean的時候,對於每個bean,先用MergedBeanDefinitionPostProcessor實現類的postProcessMergedBeanDefinition方法處理每個bean的定義類;
4. 再用BeanPostProcessor的postProcessBeforeInitialization方法處理每個bean實例;
5. bean實例初始化;
6. 用BeanPostProcessor的postProcessAfterInitialization方法處理每個bean實例;
它的核心方法只有兩個
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
可以自定義個bean的後置處理器來控制bean
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("calculateService".equals(beanName)) {
CalculateService calculateService = (CalculateService)bean;
//修改calculateService實例的成員變量serviceDesc的值
calculateService.setServiceDesc("desc from " + this.getClass().getSimpleName());
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("calculateService".equals(beanName)) {
Utils.printTrack("do postProcess after initialization");
}
return bean;
}
}
下面再介紹一個實際開發中可能會用到的例子:就是一個接口有多個實現類
當然對於一個接口有多個實現類,我們可以直接使用spring自帶的註解,只不過需要加入@Primary,但是這樣看起來不友好,接下來通過自定義註解+後置處理器來實現動態切換
首先定義一個接口和兩個實現類
public interface HelloService{
public void sayHello();
}
@Service
public class HelloServiceImpl1 implements HelloService {
@Override
public void sayHello() {
System.out.println("你好我是HelloServiceImpl1");
}
}
@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";
}
定義bean的後置處理器
@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<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) {
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<String, Object> 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<String, Object> 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());
}
}
}
最後啓動一個application 測試一下效果
@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();
}
}
}