BeanPostProcessor+Proxy动态代理+自定注解实现方法级增强和代理(类似AOP)

相关概念:

BeanPostProcessor接口是Bean后置处理器。接口实现类有postProcessBeforeInitialization和afterProcessBeforeInitialization两个方法,分别在bean创建前和创建后出发调用(bean创建/初始化的顺序,需要进一步深化看一下)。因此bean创建之前或者之后对对应bean进行一些处理。这两个方法最终的返回值还是bean。可以是本身,也可以是加工过之后的(比如这里的代理之后的bean)

一般来说,spring工程中编写一个实现了BeanPostProcessor接口的实现类,这个类会在Spring容器启动过程中在实例化每个bean的时候都出发一次响应的postProcessBeforeInitialization和afterProcessBeforeInitialization方法(个人觉得这样会造成很大的启动效率降低)。   利用这一特性,有一个思路:利用beanpostProcessor,捕获部分bean的加载,并对这些bean生成动态代理,并将代理类放到spring容器中,后续调用原本这个bean类的方法时,就会直接进代理类的invoke方法,实现对某些类的某些方法(利用自定义注解)的增强等。这里有以下几个点:

1.MyPostProcessor implements BeanPostProcessor实现类需要将自身也放入spring容器托管,直接@Component或者用@Configure注解类都可以,涉及(@Component和@Configure配置方式的区别)

2.动态代理:动态代理类是用下面的形式创建,实现InvocationHandler接口,实现必须的invoke方法。  这样,被代理类的所有方法调用都会转为调用这个代理类的invole方法,invoke方法内部,method。invoke是执行原来的方法本身,其余代码就可以做增强用。

代理类: invoke方法中,可以利用解析method方法上的注解,来实现仅对有无注解标注的方法进行区别处理。值的注意的是,因为这里Proxy是JDK自身的动态代理,代理的是接口,因此生成的代理类是接口的实现子类,不是原实现类的子类,这里的method也是接口的方法,因此只有注解加在接口上才能生效(加在实现类上不生效 )

public class TrainOuterServiceProxy implements InvocationHandler {
    private Object obj;
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        UseProxy useAnnotation = method.getAnnotation(UseProxy.class);
        if(useAnnotation != null){       //仅仅代理增强指定方法
            System.out.println("进入需要增强的方法:" + method.getName());
            return method.invoke(obj, args);
        }
        System.out.println("进入没有增强注解的方法:" + method.getName());
        return method.invoke(obj, args);
    }
}

创建代理类:利用Proxy.newProxyInstance()方法创建真正的代理类。在postProcessAfterInitialization方法中。  返回的代理类接口的实现子类,不是原实现类的子类。这里是对接口进行代理。

@Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//        MyProxy proxy = new MyProxy();
        TrainOuterServiceProxy proxy = new TrainOuterServiceProxy();
        if(beanName.contains("trainOuterService")){
           proxy.setObj(bean);
           Class[] iterClass = bean.getClass().getInterfaces();
           if(iterClass.length > 0){
               log.info("初始化时生成代理");
               Object proxyO = Proxy.newProxyInstance(bean.getClass().getClassLoader(),iterClass,proxy);
               return proxyO;
           }else {
               log.info(beanName + "没有接口不进行代理!");
               return bean;
           }
       }else{
           return bean;
       }
    }

以上述代码为例,此时在Spring容器中托管的原来的trainOuterSerivice实现类,已经由TrainOuterSServiceProxy代理实例取代 ,BeanPostProcessor+Proxy动态代理+自定注解实现方法级增强和代理(类似AOP)_第1张图片  因此在其他类中通过DI注入的trainOuterService对象就已经是一个代理对象。调用此Service的所有方法,都会被代理类的invoke方法取代,从而实现对方法的部分增强。这里是直接在spring容器启动中利用BeanPostProcessor将容器中托管的bean替换为代理类,如果想在实际需要的时候,才生成对应的代理类。需要像下面这样:

//测试自定义的代理
        TrainOuterServiceProxy trainProxy = new TrainOuterServiceProxy();
        TrainOuterService trainimpl = new TrainOuterServiceImpl();
        trainProxy.setObj(trainimpl);
        Class[] iterClass = trainimpl.getClass().getInterfaces();
        Object proxyO = Proxy.newProxyInstance(trainimpl.getClass().getClassLoader(),iterClass,trainProxy);
        TrainOuterService trainproxy = (TrainOuterService)proxyO;
        trainproxy.testYanfei("1234567");
        trainproxy.testYanfei2("1234455");

其中,由于Proxy.newProxyInstance生成的代理类是Object类,所以在倒数第三行的Object转实际对象的时候,建议想上面代码中那样转成接口类型。

有一个技术细节点:通过@Configuration类和@Component注解   将bean装配在Spring容器中的区别,下面这篇文章有较详细的解释:https://blog.csdn.net/long476964/article/details/80626930。    

 

你可能感兴趣的:(Spring高级技巧)