使用BeanPostProcessor和Cglib Enhancer实现对spring bean的动态代理

603 阅读2分钟

有这么一个需求,需要对项目中的某个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;
    }
}