有这么一个需求,需要对项目中的某个bean的某个方法进行重写,而该bean是引入别人写好的jar包自动注入到spring容器中,且是通过注解@Bean或@Component注入的,并且没有加入@ConditionalXXX等注解,导致无法简单的通过配置来替换,那么要完成该需求如何实现呢?
方式一:bean替换
系统中根据 @Bean或通过 @Component 定义的Bean对象在Spring中都会转换成一个个的BeanDefinition对象,如果我们在Spring创建这些对象加入到Spring容器之前,将不想要的BeanDefinition对象删除,而加入我们自己想要的BeanDefinition对象是不是就可以实现了?而Spring提供的BeanDefinitionRegistryPostProcessor接口正好可以帮助我们实现这个功能。
BeanDefinitionRegistryPostProcessor 是在系统加载完所有的BeanDefinition对象来进行回调。
/**
* 实现 Ordered接口,指定执行顺序
*/
@Component
class BeanReplace implements BeanDefinitionRegistryPostProcessor, Ordered {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
String beanName = "oldBeanName";
if (registry.containsBeanDefinition(beanName)) {
//移除原有bean
registry.removeBeanDefinition(beanName);
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(NewBeanService.class);
//注入新的bean
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
方式二:通过xml配置aop
方式三:代理机制
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;
}
CGLIB Enhancer
CGLIB是一个强大、高性能的字节码生成库,它用于在运行时扩展Java类和实现接口;本质上它是通过动态的生成一个子类去覆盖所要代理的类(非final修饰的类和方法)。Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
- net.sf.cglib.proxy.Callback接口:在cglib包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。
- net.sf.cglib.proxy.MethodInterceptor接口:是通用的回调(callback)类型,他经常被AOP用来实现拦截(intercept)方法的调用;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ConcreteClassInterceptor implements MethodInterceptor {
private final Object target;
public ConcreteClassInterceptor(Object target){
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println("=============Before:"+method);
//Object object=proxy.invokeSuper(obj, arg);
Object object= proxy.invoke(target, arg);
System.out.println("=============After:"+method);
return object;
}
}
import net.sf.cglib.proxy.Enhancer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements ApplicationContextAware, BeanPostProcessor {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("xxx")) {
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback(new ConcreteClassInterceptor(bean));
return enhancer.create();
}
return bean;
}
}