SpringAOP的实现之代理模式

要想了解Spring的AOP的实现,首先必须要了解代理模式。

SpringAOP的实现之代理模式_第1张图片
如果只是简单代理一个对象,那么我们自己就可以很好的实现。
声明一个抽象主题,支付接口:

public interface ToCPayment {
    void pay();
}

被代理的实现类

public class ToCPaymentImpl implements ToCPayment {
    @Override
    public void pay() {
        System.out.println("以用户的名义进行支付");
    }
}

代理角色(持有被代理角色)

public class AliToC implements ToCPayment {
    private ToCPayment toCPayment;
    public AliToC(ToCPayment toCPayment){
        this.toCPayment = toCPayment;
    }
    @Override
    public void pay() {
        beforePay();
        toCPayment.pay();
        afterPay();
    }

    private void afterPay() {
        System.out.println("支付给收钱方");
    }

    private void beforePay() {
        System.out.println("从银行卡取款");
    }
}

这样在调用接口的时候,直接就可以调用代理对象。

public class ProxyDemo {
    public static void main(String[] args) {
        ToCPayment toCProxy = new AliToC( new ToCPaymentImpl());
        toCProxy.pay();
    }
}

SpringAOP的实现之代理模式_第2张图片
,但是这样的实现有一个弊端,以上只是针对ToC的代理,如果此时增加一个ToB岂不是又要自己手写一个被代理的类?这种静态代理的方式无疑在代理多的情况下维护成本指数级别增加。

JDK的动态代理

上面的思路是没有问题的。只是相关的实现方式需要改进。
我们知道,类是通过类加载器加载的。
1.通过findClass获取到二进制字节流。
2.根据读取到的字节流,将其代表的静态数据结构转换成方法区运行时的数据结构。有了数据结构,才能让外界通过class对象取访问。
3.生成一个代表该类的class对象,作为方法区该类的数据访问入口。

改进的切入点
根据一定的规则取改动或者生成新的字节流,将切面逻辑织入进去。
这就是动态代理,在程序运行时动态生成类的字节码,并加载到JVM中。
她要求被代理的类必须实现接口(唯一约束),否则不能使用JDK动态代理。

其中关键的有一个核心类和一个接口。

public class Proxy implements java.io.Serializable
public interface InvocationHandler

只有实现了InvocationHandler的类,才具有代理功能。相关的切面逻辑就存在于此接口的实现类里面。此接口只有一个方法。

 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

当我们铜鼓动态代理对象调用一个方法的时候 ,这个方法的调用就会被转发到实现了这个接口的本方法里面。
此方法接受三个参数。

//Object proxy 代理对象(不是被代理对象)
// Method method 调用的目标对象的方法示例
//方法的参数
 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

Proxy类

创建代理类。
里面有一个很重要的方法

//ClassLoader loader 声明哪个加载器来加载class\
//为代理对象要加上的接口
// InvocationHandler  用来 表示动态代理的方法会关联到哪个 InvocationHandler 的实现类
//返回的时一个被改造的类 但是 继承自这些接口 所以可以偷天换日
 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

JDK动态代理例子

下载我们将增强逻辑直接写在InvocationHandler的实现类里面。

public class AliPayInvocationHandler implements InvocationHandler {
    //用来保存被代理的对象
    private Object targetObject;
    public AliPayInvocationHandler(Object targetObject){
        this.targetObject = targetObject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforePay();
        //目标类 方法参
        Object result = method.invoke(targetObject, args);
        afterPay();
        return result;
    }
    //增强逻辑
    private void afterPay() {
        System.out.println("支付给收钱方");
    }
    //增强逻辑
    private void beforePay() {
        System.out.println("从银行卡取款");
    }
}
public class JdkUtil {
    public static <T> T newProxyInstance(T targetObject, InvocationHandler handler){
        ClassLoader classLoader = targetObject.getClass().getClassLoader();
        Class<?>[] interfaces = targetObject.getClass().getInterfaces();
        return (T) Proxy.newProxyInstance(classLoader,interfaces,handler);
    }
}
public class ProxyDemo {
    public static void main(String[] args) {
        //被代理的对象
        ToCPayment toCPayment = new ToCPaymentImpl();
        AliPayInvocationHandler handler = new AliPayInvocationHandler(toCPayment);
        ToCPayment payment = JdkUtil.newProxyInstance(toCPayment, handler);
        payment.pay();
    }
}

运行:
SpringAOP的实现之代理模式_第3张图片
通过动态代理,可以复用增强的逻辑 ,代理对象是自动生成的,不用显式的去写一个类。

CGLIB

JDK动态代理要求对象必须实现接口,否则不能使用动态代理。这样在于在接口调用方便偷天换日。
CGLIB全程 Code Generation Libary.不要求目标必须实现接口,内部封装了ASM字节码操作框架。
缺点在于:上手难度高 ,对JVM不熟悉直接修改字节码,会发生莫名其妙的问题。
简单来说CGLIB只是动态生成目标类的子类,覆盖非final的方法,绑定钩子回调自定义拦截器 。

使用cglib先导包。

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.9</version>
        </dependency>

主要由三个部分组成。
CallBack接口

```clike
public interface Callback {
}

`一个空接口,没有方法,仅仅是标志作用。

MethodInterceptor接口


public interface MethodInterceptor extends Callback {
//参数1 动态代理对象
//被代理的方法
// 方法参数 
//代理方法
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}
我们可以通过实现这个接口,构造出通用的逻辑。

Enhancer类
帮助我们创建出动态代理实例出来。

CgLib使用示例

定义通用切面

public class AliPayMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforePay();
        //执行被代理方法
        Object invoke = methodProxy.invokeSuper(o, objects);
        afterPay();
        return invoke;
    }
    private void afterPay() {
        System.out.println("支付给收钱方");
    }

    private void beforePay() {
        System.out.println("从银行卡取款");
    }
}

创建动态代理类

public class CgLibUtil {
    public static  <T> T  createProxy(T targetObject , MethodInterceptor interceptor){
       return (T) Enhancer.create(targetObject.getClass(), interceptor);
    }
}

创建被代理对象,(无接口)

public class CommonPay {
    public void pay(){
        System.out.println("一个通用的支付通道");
    }
}

Demo

public class CgLibDemo {
    public static void main(String[] args) {
    //无接口的类
        CommonPay commonPay = new CommonPay();
        AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
        CommonPay commonPayProxy = CgLibUtil.createProxy(commonPay,interceptor);
        commonPayProxy.pay();
    }
}

运行结果
SpringAOP的实现之代理模式_第4张图片
CGLIB方式 不仅仅支持无接口的对象代理,也支持有接口的对象代理。

public class CgLibDemo {
    public static void main(String[] args) {
       /* CommonPay commonPay = new CommonPay();
        AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
        CommonPay commonPayProxy = CgLibUtil.createProxy(commonPay,interceptor);
        commonPayProxy.pay();*/
        //tocPayMent接口的实现类
        ToCPayment toCPayment = new ToCPaymentImpl();
        AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
        ToCPayment proxy = CgLibUtil.createProxy(toCPayment, interceptor);
        proxy.pay();
    }
}

但是JDK动态代理模式,如果强行代理无接口的类,就会抛出异常。

public class CgLibDemo {
    public static void main(String[] args) {
       /* CommonPay commonPay = new CommonPay();
        AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
        CommonPay commonPayProxy = CgLibUtil.createProxy(commonPay,interceptor);
        commonPayProxy.pay();*/
        //tocPayMent的实现类
       /* ToCPayment toCPayment = new ToCPaymentImpl();
        AliPayMethodInterceptor interceptor = new AliPayMethodInterceptor();
        ToCPayment proxy = CgLibUtil.createProxy(toCPayment, interceptor);
        proxy.pay();*/
        //尝试使用JDK动态代理
        CommonPay commonPay = new CommonPay();
        AliPayInvocationHandler handler = new AliPayInvocationHandler(commonPay);
        CommonPay newProxyInstance = JdkUtil.newProxyInstance(commonPay, handler);
        newProxyInstance.pay();
    }
}

在这里插入图片描述

你可能感兴趣的:(SpringAOP的实现之代理模式)