java 的三种代理模式 (二)——子函数切面

java 的三种代理模式

结论:

1 jdk动态代理无法切面子函数,因为jdk动态代理是组合模式,this永远指向被代理origin类的对象,切子函数理论不可行

public class ProxyFactory implements InvocationHandler {
    //维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始事务jdk");
        Object returnValue = method.invoke(target, args);

除非用暴力(asm、javassist)运行期修改子函数 son(xxx)--->proxy.son(xxx)

2 cglib为继承模式,能够利用java语言的多态的特性,this指针本身指向proxy类对象,使切子函数理论可行

具体而言,可以切子函数,也可以不切,取决于:

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务cglib");

        //执行目标对象的方法
    //    Object returnValue = method.invoke(target, args);

        // 上面那个直接调用父类对象target的父方法,this指向父类——被代理origin类
        // obj为子类——proxy类对象,this指向proxy对象
        // 在这个地方拦截子函数
        Object returnValue = proxy.invokeSuper(obj, args);

    //    Object returnValue = method.invoke(obj, args); 无限循环

        System.out.println("提交事务cglib");

        return returnValue;
    }

3 spring aop

Cglib代理的时候target对象中的this就是Cglib子类  (你可能觉得我说的是废话,子类对象在父类的this指的不是自身吗? 你知道Spring Aop里this方法无法增强自身调用,这时候你就开始怀疑人生了)

spring自己阉割了,https://www.cnblogs.com/lvbinbin2yujie/p/10284316.html,AopUtils

 这也是spring aop 内部方法与aspectJ的原因

为什么,既然cglib有切子函数的功能,为何spring不用?猜测可能为了与jdk动态代理统一,毕竟后者无法切子函数?还是有更高远的考量?

一个经典应用:

@Transactional(propagation = Propagation.REQUIRED)
    public void insertUser1(){
        User user = new User("niu","男",19,"1000000");
        userDao.insertUser(user);

        this.insertUser2();
        //异常
        int a = 10/0;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insertUser2(){
        User user = new User("xing","女",19,"1111111");
        userDao.insertUser(user);
    }

  https://www.cnblogs.com/nxzblogs/p/10503889.html中

问题:“service层有两个事务方法insertUser1、insertUser2, 当在insertUser1中调用insertUser2是,如果前面 方法异常,后边方法不会保存

  期望:不管insertUser1是否报错,insertUser2 都要报存数据”

子方法的事务不起作用,继承母方法一起回滚

4 cglib解决无限递归

public final void mother() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}

if (var10000 != null) {
var10000.intercept(this, CGLIB$mother$0$Method, CGLIB$emptyArgs, CGLIB$mother$0$Proxy);
} else {
super.mother();
}
}
//    Object returnValue = method.invoke(obj, args); 无限循环

5 protected和public子函数可切,private不可切,因为private函数无法overide

当然,此外final function也是无法用cglib代理的,因为无法override

6 建议优先使用cglib增强,及protected子函数,极端情况使用private

在某些场景,具有优势

比如,所有方法的输入、输出统一全过程日志,cglib可以切到子函数,jdk proxy切不到,只能在子函数前后单独log,侵入较高,未利用代理中控的集中性

运用cglib增强配合 spring批量注入结合自定义注解的 spring 动态注入 ,构建自定义spring aop,弥补spring aop对切子函数的不友好的缺点(每个子函数getbean,侵入较高)

7 可以被切的隐藏方法(在java object常用方法罗列的方法),即时origin不改写这些方法,框架也会加上

object:clone finalize equals toString hashCode getClass wait notify notifyAll

jdk: equals toString hashcode

cglib:clone finalize equals toString hashCode

参考:

https://www.cnblogs.com/lvbinbin2yujie/p/10284316.html

https://blog.csdn.net/qq_28033719/article/details/108339751

原文地址:https://www.cnblogs.com/silyvin/p/13756883.html