关于Spring使用代理模式进行AOP实现

AOP实现方式之利用Proxy代理

我们的aop可以说是Spring中的一个非常具有特色的东西了,非常流行的实现是利用proxy实现代理对象,但是这种操作其实也有两种不同的实现方法,一种是利用jdk的代理,另外一种是利用cglib这种第三方的包进行的。

jdk代理

jdk的代理是有一些限制的,它只能面向接口进行代理。我们来看一个例子


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MainTest{
    /**
     * 父接口
     */
    interface TheInterface{
        void foo();
    }

    /**
     * 我们需要代理的类
     */
    static class Target implements TheInterface{
        @Override
        public void foo() {
            System.out.println("foo实现!");
        }
    }

    public static void main(String[] args){
        // 生成一个被代理的对象
        Target target = new Target();

        // 获取一个类加载器用来加载动态代理生成的字节码文件
        ClassLoader classLoader = MainTest.class.getClassLoader();
        // 获取代理对象
        TheInterface proxyObject = (TheInterface) Proxy.newProxyInstance(classLoader, new Class[]{ TheInterface.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("代理增强的部分....");
                Object res = method.invoke(target, args);
                return res;
            }
        });
        proxyObject.foo();
    }
}

上面的代码我们可以看到我们需要代理的那个对象实现了一个接口,里面是我们需要增强代理的对象,这个时候我们利用Proxy这个类中的newProxyInstance方法来创建一个代理对象,然后我们需要一个类加载器,因为我们的动态代理对象是没有源代码的,所以我们是在运行的时候动态创建出来的所以需要用一个类加载器去手动的加载进内存,然后还需要一个数组里面存放我们需要代理的类的字节码文件,然后创建一个增强处理器实现增强逻辑InvocationHandler然后我们在里面就可以实现增强的逻辑,并且我们还要去invoke反射调用之前的方法,那么我们就需要把target实现类去放进去才可以(因为我们增强的是target)然后把该返回的返回,然后这个newProxyInstance这个方法返回的对象就是增强代理对象,所以我们强转为我们的接口类型,然后进行调用foo就可以了。
我们来看看结果
在这里插入图片描述
我们可以看到结果确实是正确的。
jdk这个动态代理我们发现最后生成的动态代理对象其实和我们的Target对象是兄弟关系,所以就算我的Target类用了final进行修饰(不能有子类)也不会有任何的问题。

Enhancer代理

Enhancer代理是Spring使用的那一种,是基于继承的,所以我们前面的那种如果有final修饰target的话,那么就不可以使用Enhancer进行代理

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MainTest{

    /**
     * 目标对象
     */
    static class Target{
        void foo(){
            System.out.println("调用方法...");
        }
    }

    public static void main(String[] args) {
        Target target = new Target();
        // 获取增强对象
        Target enhancer = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("增强....");
                // Object res = method.invoke(target, args); 这种使用反射,效率稍微有点低
                // Object res = methodProxy.invoke(target, args); 这种底层没有用反射,效率比较高,是Spring的实现
                Object res = methodProxy.invokeSuper(o,args);
                return res;
            }
        });
        // 调用增强对象
        enhancer.foo();
    }
}

注意一下,intercept方法内部参数中的o其实就是代理对象,Method是反射用的方法对象,然后还有一个值得注意的是MethodProxy这个参数是Method对象的代理,内部是没有用到反射的,没有用到反射所以它快,里面有三种方法都可以执行调用foo本来的逻辑,可以用method对象进行反射调用,还可以利用methodProxy方法代理对象进行调用,这两者都需要target对象,但是还有一种,是利用methodProxy.invokeSuper进行调用同样没有使用反射,只需要传入代理对象o和参数即可,这个连target都没有用到。

JDK实现的代理类源码仿写

因为动态代理的对象是没有源代码的,是直接通过Asm技术直接生成字节码文件的,所以只能通过字节码文件反编译才可以获取到一个代理对象的源码,这里模仿写一个类似的代理类


public class MainTest{
    /**
     * 父接口
     */
    interface TheInterface{
        void foo();
        int bar();
    }

    /**
     * 被代理的实现类
     */
    static class Target implements TheInterface{
        @Override
        public void foo(){
            System.out.println("foo...");
        }
        @Override
        public int bar() {
            System.out.println("bar...");
            return 0;
        }
    }

    /**
     * 模拟的代理对象的源代码
     */
    static class $Proxy0 implements TheInterface{
        @Override
        public void foo() {
            System.out.println("前增强...");
            System.out.println("foo...");
            System.out.println("后增强...");
        }

        @Override
        public int bar() {
            System.out.println("前增强...");
            System.out.println("bar...");
            System.out.println("后增强...");
            return 0;
        }
    }

    /**
     * 测试入口方法
     * @param args
     */
    public static void main(String[] args) {
        TheInterface proxy = new $Proxy0();
        proxy.foo();
        proxy.bar();
    }
}

首先定义了一个接口TheInterface然后里面有两个方法一个是foo还有一个是bar,现在有一个实现类Target我们的目的就要去代理它,于是我们写了一个代理类$Proxy0,里面对于foo和bar的实现都是写上了增强之后然后照抄Target中的方法,这个可以说是稀烂,因为我们的代码写的太过于固定了,那么最好的方法就是解耦,我们可以把这个增强的部分抽象成一个接口,以后就可以去执行这个接口实现类中的增强逻辑了。


public class MainTest{
    /**
     * 父接口
     */
    interface TheInterface{
        void foo();
        int bar();
    }

    /**
     * 增强逻辑抽象接口
     */
    interface InvocationHandler{
        /**
         * 逻辑方法
         * @return
         */
        Object invoke();
    }

    /**
     * 被代理的实现类
     */
    static class Target implements TheInterface{
        @Override
        public void foo(){
            System.out.println("foo...");
        }
        @Override
        public int bar() {
            System.out.println("bar...");
            return 0;
        }
    }


    /**
     * 模拟的代理对象的源代码
     */
    static class $Proxy0 implements TheInterface{
        InvocationHandler h;

        public $Proxy0(InvocationHandler handler){
            h = handler;
        }

        @Override
        public void foo() {
            h.invoke();
        }

        @Override
        public int bar() {
            return (int)h.invoke();
        }
    }

    /**
     * 测试入口方法
     * @param args
     */
    public static void main(String[] args) {
        TheInterface proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke() {
                System.out.println("前增强...");
                new Target().foo();
                System.out.println("后增强...");
                return null;
            }
        });
        proxy.foo();
        proxy.bar();
    }
}

然后修改了一次,我们现在来看,我们定义了一个接口InvocationHandler里面有一个invoke方法,我们在构造proxy的时候传入一个匿名内部类然后重写方法就可以进行代理了,并且我们在$Proxy0类中直接调用invoke方法就可以做到了,这个样子的话我们代理类中其实不需要照抄方法,直接调用被代理对象的方法就可以了,但是现在还是有问题,因为这里面调用的都是foo方法,没有调用bar的逻辑,简而言之就是我们的invoke方法不知道增强的是哪个方法,所以我们其实可以把Method对象和它的参数传入,这样就可以动态的代理各个方法了。我们继续修改代码

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainTest{
    /**
     * 父接口
     */
    interface TheInterface{
        void foo();
        int bar();
    }

    /**
     * 增强逻辑抽象接口
     */
    interface InvocationHandler{
        /**
         * 逻辑方法
         * @return
         */
        Object invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
    }

    /**
     * 被代理的实现类
     */
    static class Target implements TheInterface{
        @Override
        public void foo(){
            System.out.println("foo...");
        }
        @Override
        public int bar() {
            System.out.println("bar...");
            return 0;
        }
    }


    /**
     * 模拟的代理对象的源代码
     */
    static class $Proxy0 implements TheInterface{
        InvocationHandler h;

        public $Proxy0(InvocationHandler handler){
            h = handler;
        }

        @Override
        public void foo() {
            try {
                Method foo = TheInterface.class.getMethod("foo");
                h.invoke(foo,null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }

        @Override
        public int bar() {
            try {
                Method bar = TheInterface.class.getMethod("bar");
                return (int)h.invoke(bar,null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    /**
     * 测试入口方法
     * @param args
     */
    public static void main(String[] args) {
        TheInterface proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
                System.out.println("前增强...");
                Object res = method.invoke(new Target(), args);
                System.out.println("后增强...");
                return res;
            }
        });
        proxy.foo();
        proxy.bar();
    }
}

这样感觉就舒服多了,我们现在运行一下
在这里插入图片描述
可以看到实现了基本的增强功能了。其实现在和动态代理生成的类其实已经非常相似了,但是还是有一点不一样,比如这个InvocationHandler其实我们的代理类是继承了一个叫做Proxy的接口,我们来看一下这个接口
在这里插入图片描述
可以看到里面有一个protected修饰的成员变量InvocationHandler。所以我们现在不用自己的InvocationHandler我们就用JDK里面的InvocationHandler,并且让我们自己写的实现类去继承Proxy。还有一点不同,其实在真正的代理类中,方法对象并不是新鲜获取的,而是当作静态成员变量在类中。所以我们写一个static代码块来获取他们就好了,不用每次调用再来重新生成。最后的代码就像下面这样。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MainTest{
    /**
     * 父接口
     */
    interface TheInterface{
        void foo();
        int bar();
    }


    /**
     * 被代理的实现类
     */
    static class Target implements TheInterface{
        @Override
        public void foo(){
            System.out.println("foo...");
        }
        @Override
        public int bar() {
            System.out.println("bar...");
            return 0;
        }
    }


    /**
     * 模拟的代理对象的源代码
     */
    static class $Proxy0 extends Proxy implements TheInterface{

        private static Method foo;
        private static Method bar;
        static {
            try {
                foo = TheInterface.class.getMethod("foo");
                bar = TheInterface.class.getMethod("bar");
            }catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * 构造
         * @param handler
         */
        public $Proxy0(InvocationHandler handler){
            super(handler);
        }
        @Override
        public void foo() {
            try {
                Method foo = TheInterface.class.getMethod("foo");
                h.invoke(this, foo,null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }

        @Override
        public int bar() {
            try {
                return (int)h.invoke(this, bar,null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    /**
     * 测试入口方法
     * @param args
     */
    public static void main(String[] args) {
        TheInterface proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
                System.out.println("前增强...");
                Object res = method.invoke(new Target(), args);
                System.out.println("后增强...");
                return res;
            }
        });
        proxy.foo();
        proxy.bar();
    }
}

运行一下
在这里插入图片描述
我们把自己写的InvocationHandler删掉之后换成了JDK中的InvocationHandler我们也是无缝衔接。

Enhancer实现的Proxy增强代理

其实Enhancer实现的代理和JDK的代理很相似,但是有些许不同,它是有三种方式可以调用原本的方法,我们前面说了,有通过Method对象进行反射调用的,还有两个是不用进行反射调用的利用一个MethodProxy对象进行的调用。
先来看一个基本的利用反射调用的代理对象源码仿照案例

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MainTest{
    /**
     * 被代理的对象
     */
    static class Target{
        public void save(){
            System.out.println("save()");
        }
        public void save(int i){
            System.out.println("save(" + i + ")");
        }
        public void save(long il){
            System.out.println("save(" + il + "L)");
        }
    }

    static class Proxy extends Target{

        private MethodInterceptor methodInterceptor;
        public Proxy(MethodInterceptor m){ methodInterceptor = m; }

        /**
         * 方法对象
         */
        private static Method save0;
        private static Method save1;
        private static Method save2;
        static {
            try {
                save0 = Target.class.getMethod("save");
                save1 = Target.class.getMethod("save", int.class);
                save2 = Target.class.getMethod("save", long.class);
            }catch (NoSuchMethodException e){
                throw new RuntimeException(e.getMessage());
            }
        }


        @Override
        public void save() {
            try {
                methodInterceptor.intercept(this,save0,null,null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
        @Override
        public void save(int i) {
            try {
                methodInterceptor.intercept(this, save1, new Object[]{i},null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
        @Override
        public void save(long il) {
            try {
                methodInterceptor.intercept(this,save2, new Object[]{il},null);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        Proxy proxy = new Proxy(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("前置增强....");
                Object res = method.invoke(new Target(), objects);
                return res;
            }
        });
        proxy.save();
        proxy.save(1);
        proxy.save(2L);
    }
}

运行一下
在这里插入图片描述
结果没问题
可以看出来和我们之前的jdk的代理非常类似,但是我们知道我们的Enhancer的代理可以不用反射,那么重点就在于最后一个参数MethodProxy了,其实在底层里面,不只是这几个增强的方法,其实在底层我们的proxy代理对象其实还会生成几个原本的父类中的原生方法,并且没有通过反射进行调用,用的是super.方法名()进行调用,这就是为什么Enhancer是基于继承的了。并且我们需要用到MethodProxy所以需要给每一个方法都创建一个MethodProxy。并且把这个MethodProxy作为参数传入intercept调用的第四个参数

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MainTest{
    /**
     * 被代理的对象
     */
    static class Target{
        public void save(){
            System.out.println("save()");
        }
        public void save(int i){
            System.out.println("save(" + i + ")");
        }
        public void save(long il){
            System.out.println("save(" + il + "L)");
        }
    }

    /**
     * 模拟代理类
     */
    static class Proxy extends Target{

        private MethodInterceptor methodInterceptor;
        public Proxy(MethodInterceptor m){ methodInterceptor = m; }

        /**
         * 方法对象
         */
        private static Method save0;
        private static Method save1;
        private static Method save2;
        // 原生方法
        private static MethodProxy saveProxy0;
        private static MethodProxy saveProxy1;
        private static MethodProxy saveProxy2;
        static {
            try {
                save0 = Target.class.getMethod("save");
                save1 = Target.class.getMethod("save", int.class);
                save2 = Target.class.getMethod("save", long.class);

                saveProxy0 = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper0");
                saveProxy1 = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper1");
                saveProxy2 = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper2");
            }catch (NoSuchMethodException e){
                throw new RuntimeException(e.getMessage());
            }
        }


        // 父类原生方法
        public void saveSuper0(){
            super.save();
        }
        public void saveSuper1(int i){
            super.save(i);
        }
        public void saveSuper2(long j){
            super.save(j);
        }

        // 增强方法
        @Override
        public void save() {
            try {
                methodInterceptor.intercept(this,save0,null,saveProxy0);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
        @Override
        public void save(int i) {
            try {
                methodInterceptor.intercept(this, save1, new Object[]{i},saveProxy1);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
        @Override
        public void save(long il) {
            try {
                methodInterceptor.intercept(this,save2, new Object[]{il},saveProxy2);
            } catch (RuntimeException | Error e) {
                throw e;
            } catch (Throwable e){
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        Proxy proxy = new Proxy(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("前置增强....");
                Object res = methodProxy.invoke(new Target(), objects);
                return res;
            }
        });
        proxy.save();
        proxy.save(1);
        proxy.save(2L);
    }
}

我们看到代码中我们定义了四个父类的原生方法(调用super)然后调用MethodProxy.create方法创建了MethodProxy对象传入的参数分别是需要代理的对象的class和代理类的class,方法描述,需要代理的方法名称,代理方法名称
其中这个方法描述是一个字符串拿三个举例子 ()V 后面的V表示返回值是void,括号里面表示的是参数类型空的就表示没有参数,(I)V 表示参数是一个Integer,没有返回值,(J)V表示有一个long类型参数没有返回值。然后就创建出了MethodProxy然后在调用的时候也就是MethodInterceptor的方法的时候使用就可以了,运行一下看结果
在这里插入图片描述
结果还是一样没有问题。这个时候 我们就想到一个问题,这个MethodProxy是如何进行处理的呢?它怎么做到的呢?其实啊后面比较难,简单说一波,其实是又弄了两个代理类。这两个代理类都继承了一个抽象类,还是挨着挨着看吧。
我们知道我们刚刚运行的是一个invoke方法,那么我们点进MethodProxy的invoke看看源代码
在这里插入图片描述
这里看到里面核心是调用了一个invoke方法我们再次点进去看看
在这里插入图片描述
现在进入到一个FastClass的抽象类里面去了。这个FastClass就是主要的东西
在我们利用MethodProxy的时候就会又生成两个动态类,这两个动态类都继承了FastClass,一个是用于MethodProxy的invoke方法的,也就是带有target的,还有一个是用于MethodProxy的invokeSuper的也就是带有代理对象的。里面最重要的两个方法就是
在这里插入图片描述
getIndex和invoke方法了。
因为两个代理类也是动态生成的没有源码所以我们写一个模仿的,首先是Target的

class TargetFastClass{

        // 类里面的信息
        public static Signature s0;
        public static Signature s1;
        public static Signature s2;
        static {
            s0 = new Signature("save","()V");
            s1 = new Signature("save","(I)V");
            s2 = new Signature("save","(J)V");
        }

        /**
         * 根据信息签名获取index
         * @param signature
         * @return
         */
        public int getIndex(Signature signature){
            if(s0.equals(signature)) return 0;
            else if(s1.equals(signature)) return 1;
            else if(s2.equals(signature)) return 2;
            else return -1;
        }

        /**
         * 根据index来决定执行哪一个方法
         * @param index
         * @param target
         * @param args
         * @return
         * @throws InvocationTargetException
         */
        public Object invoke(int index, Object target, Object[] args){
            if (index == 0)((Target)target).save();
            if (index == 1)((Target)target).save(1);
            if (index == 2)((Target)target).save(2L);
            return null;
        }
    }

就像这样,先根据Signature把index计算出来然后我们的MethodProxy会记住自己的index,然后一旦需要调用的时候就执行target的里的特定方法。
invokeSuper的其实也差不多

class ProxyFastClass{

        // 类里面的信息
        public static Signature s0;
        public static Signature s1;
        public static Signature s2;
        static {
            s0 = new Signature("saveSuper0","()V");
            s1 = new Signature("saveSuper1","(I)V");
            s2 = new Signature("saveSuper2","(J)V");
        }

        /**
         * 根据信息签名获取index
         * @param signature
         * @return
         */
        public int getIndex(Signature signature){
            if(s0.equals(signature)) return 0;
            else if(s1.equals(signature)) return 1;
            else if(s2.equals(signature)) return 2;
            else return -1;
        }

        /**
         * 根据index来决定执行哪一个方法
         * @param index
         * @param proxy
         * @param args
         * @return
         * @throws InvocationTargetException
         */
        public Object invoke(int index, Object proxy, Object[] args){
            if (index == 0)((Proxy)proxy).saveSuper0();
            if (index == 1)((Proxy)proxy).saveSuper1(1);
            if (index == 2)((Proxy)proxy).saveSuper2(2L);
            return null;
        }
    }

这里就需要把Signature的name改成代理类中的原生父类方法(因为不管是我们的Method还是MethoProxy目的都是调用原生方法)然后invoke里面我们传入的target也不是target而是proxy了
其实在底层也就是生成了这两个代理类就做到了不用反射进行调用。

本次学习参考Blibli视频-黑马程序员Spring视频教程,全面深度讲解spring5底层原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值