spring通过代理方式生成类,动态代理方式用到的包和框架是什么样的

JDK动态代理

  • 需要导入的包
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

  • 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理;JDK代理只能对实现接口的类生成代理

/**
 * JDK动态代理类
 */
public class JDKProxy implements InvocationHandler {    
    
    // 需要代理的目标对象
    private Object targetObject;    
    
    public Object newProxy(Object targetObject) {
        // 将目标对象传入进行代理    
        this.targetObject = targetObject;
        // 返回代理对象 
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }    
    
    // invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 进行逻辑处理的函数
        checkPopedom();
        Object ret = null;
        // 调用invoke方法
        ret = method.invoke(targetObject, args);
        return ret;
    }    
    
    private void checkPopedom() {
        // 模拟检查权限   
        System.out.println("检查权限:checkPopedom()!");    
    }    
} 

  • JDK代理使用的是反射机制实现aop的动态代理,jdk动态代理的方式创建代理对象效率较高,执行效率较低;JDK动态代理机制是委托机制,具体说就是动态实现接口类在动态生成的实现类里委托handler去调用原始实现类方法

CGLIB动态代理(ASM框架)

  • 利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
/**
 * CGlib动态代理类
 */
 public class CGLibProxy implements MethodInterceptor {    
    
    // CGlib需要代理的目标对象
    private Object targetObject;
    
    public Object createProxyObject(Object obj) {
    	//createProxyObject传入了要继承的实现类,通过setSuperClass继承
        this.targetObject = obj;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;
    }
    
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object obj = null;
        // 过滤方法
        if ("addUser".equals(method.getName())) {
            // 检查权限
            checkPopedom();
        }
        obj = method.invoke(targetObject, args);
        return obj;
    }    
    
    private void checkPopedom() {
        System.out.println("检查权限:checkPopedom()!");
    }
}

  • CGLIB代理使用字节码处理框架asm(ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能),通过修改字节码生成子类,所以cglib创建效率较低,执行效率高;CGLIB则使用的继承机制(createProxyObject传入了要继承的实现类,通过setSuperClass继承),具体说就是被代理类和代理类是继承关系,所以代理类可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口

面试必备技能:JDK动态代理给Spring事务埋下的坑!

两个注解为事务的方法,在A方法内调用B方法,B方法的事务失效;我们知道Spring事务管理通过JDK动态代理的方式进行实现的(另一种是使用CGLib动态代理实现的),也正是因为动态代理的特性造成了上述A()方法调用B()方法的时候造成了B()方法中的事务失效!简单的来说,在A()方法调用B()方法的时候,B()方法的事务是不起作用的,此时的child()方法像一个没有加事务的普通方法,代码的作用就好像把B()方法的代码粘贴到了A()方法中,并没有事务的加持。
于把B()的方法体放入到A()中,也就是内部方法,同样的不管你嵌套了多少层只有代理对象proxy 直接调用的那一个方法才是真正的走代理的

如何解决这个坑?

上文的分析中我们已经了解了为什么在特定场景下使用Spring事务的时候造成事务无法回滚的问题,下边我们谈一下几种解决的方法:

1、我们可以选择逃避这个问题

我们可以不使用以上这种事务嵌套的方式来解决问题,最简单的方法就是把问题提到Service或者是更靠前的逻辑中去解决,使用service.xxxtransaction是不会出现这种问题的。

2、通过AopProxy上下文获取代理对象:AopContext.currentProxy()

(1)SpringBoot配置方式注解开启 exposeProxy = true暴露代理对象 (否则AopContext.currentProxy() 会抛出异常)。

添加依赖:
在这里插入图片描述
添加注解:
在这里插入图片描述
修改原有代码的执行方式为:
在这里插入图片描述

  • 注意的是在parent调用child的时候是通过try/catch捕获了异常的!
3、通过ApplicationContext上下文进行解决:

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值