• 细说Spring——AOP详解(动态代理实现AOP)


    前言

    嗯,我应该是有一段实现没有写过博客了,在写完了细说Spring——AOP详解(AOP概览)之后,我发现我不知道该怎么写AOP这一部分,所以就把写博客这件事给放下了,但是这件事情又不想就这么放弃,所以今天我仔细思考了一下,决定还是要克服困难,我仔细的想了一下怎么讲解AOP实现这一部分,然后我决定由浅入深的讲解动态代理,然后用动态代理实现一个简单的AOP,感觉这样能够让人对AOP的原理有一个比较深刻的认识,希望能帮到大家。而且最近学习又组建了ACM比赛的队伍,虽然已经要大三了,按理来说应该一心学习Java Web的开发,可是我对算法也有一定的兴趣,并且之前也确实花过一段时间练习算法题,所以还是决定要参加一下,所以写博客可能就会慢一点吧。

    一、什么是动态代理

    动态代理其实就是Java中的一个方法,这个方法可以实现:动态创建一组指定的接口的实现对象(在运行时,创建实现了指定的一组接口的对象)

    这里声明一下,本篇博客中会使用很多AOP中的术语,所以如果看不懂术语的话一定要先看一下细说Spring——AOP详解(AOP概览)

    例如:

    interface A {}

    interface B {}

    //obj对象的类型实现了A和B两个接口

    Object obj = 方法(new Class[]{A.class, B.class})

    二、动态代理初体验

    我们根据上面的思路来体验一下Java中的动态代理吧,首先我们要先写两个接口。

    interface A {

        public void a();

    }

    interface B {

        public void b();

    }

    然后我们就先来看一下动态代理的代码:

    public static Object newProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)

    上面这个就是动态代理类(Proxy)类中的创建代理对象的方法,下面介绍一下方法的三个参数:

    ClassLoader loader:方法需要动态生成一个类,这个类实现了A和B两个接口,然后创建这个类的对象。需要生成一个类,而且这个类也需要加载到方法区中,所以我们需要一个ClassLoader来加载该类

    Class<?>[] interfaces:我们需要代理对象实现的数组

    InvocationHandler h:调用处理器

    这里你可能对InvocationHandler有疑惑,这里先买个关子,下面马上揭晓。

    我们现在就使用动态代理创建一个代理对象吧。

    @Test

        public void test1() {

            /**

            * 三个参数

            * 1、ClassLoader

            * 方法需要动态生成一个类,这个类实现了A和B两个接口,然后创建这个类的对象

            * 需要生成一个类,这个类也需要加载到方法区中,所以我们需要一个ClassLoader来加载该类

            *

            * 2、Class[] interfaces

            * 我们需要代理对象实现的数组

            *

            * 3、InvocationHandler

            * 调用处理器

            */

            ClassLoader classLoader = this.getClass().getClassLoader();

            //这里创建一个空实现的调用处理器。

            InvocationHandler invocationHandler = new InvocationHandler() {

                @Override

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

                    return null;

                }

            };

            Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler);

            //强转为A和B接口类型,说明生成的代理对象实现了A和B接口

            A a = (A) obj;

            B b = (B) obj;

        }

    经过测试代码运行成功,说明生成的代理对象确实实现了A接口和B接口,但是我想你一定会对代理对象如何实现了A接口和B接口感兴趣,你一定想知道如果使用代理对象调用相应接口的方法会发生什么感兴趣,下面我们一起来探究一下:

    上面代码的基础上加上下面的代码

    a.a();

    b.b();

    我们可以发现什么也没有发生。这是因为我们根本没有为代理对象添加实现逻辑。可是实现逻辑添加在哪里呢?哈哈,当然是InvocationHandler中了。下面就看一看添加了实现逻辑的代码:

    @Test

        public void test2() {

            /**

            * 三个参数

            * 1、ClassLoader

            * 方法需要动态生成一个类,这个类实现了A和B两个接口,然后创建这个类的对象

            * 需要生成一个类,这个类也需要加载到方法区中,所以我们需要一个ClassLoader来加载该类

            *

            * 2、Class[] interfaces

            * 我们需要代理对象实现的数组

            *

            * 3、InvocationHandler

            * 调用处理器

            *

            * 代理对象实现的所有接口中的方法,内容都是调用InvocationHandler的invoke()方法

            */

            ClassLoader classLoader = this.getClass().getClassLoader();

            //这里创建一个空实现的调用处理器。

            InvocationHandler invocationHandler = new InvocationHandler() {

                @Override

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

                    System.out.println("你好!!!!");//注意这里添加了一点小逻辑

                    return null;

                }

            };

            Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler);

            //强转为A和B接口类型,说明生成的代理对象实现了A和B接口

            A a = (A) obj;

            B b = (B) obj;

            a.a();

            b.b();

        }

    截图如下:

    这里我们发现A接口和B接口的实现逻辑都是调用了invoke这个方法中的逻辑,其实除了调用代理对象的native方法,调用代理对象的其他所有方法本质都是调用了invoke方法,下面我们再来看第三个实例,让我们对动态代理有更深刻的认识。

    public void test3() {

            /**

            * 三个参数

            * 1、ClassLoader

            * 方法需要动态生成一个类,这个类实现了A和B两个接口,然后创建这个类的对象

            * 需要生成一个类,这个类也需要加载到方法区中,所以我们需要一个ClassLoader来加载该类

            *

            * 2、Class[] interfaces

            * 我们需要代理对象实现的数组

            *

            * 3、InvocationHandler

            * 调用处理器

            *

            * 代理对象实现的所有接口中的方法,内容都是调用InvocationHandler的invoke()方法

            */

            ClassLoader classLoader = this.getClass().getClassLoader();

            //这里创建一个空实现的调用处理器。

            InvocationHandler invocationHandler = new InvocationHandler() {

                @Override

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

                    System.out.println("你好!!!!");

                    return "Hello";//这里改为返回"Hello"

                }

            };

            Object obj = Proxy.newProxyInstance(classLoader, new Class[]{A.class, B.class}, invocationHandler);

            //强转为A和B接口类型,说明生成的代理对象实现了A和B接口

            A a = (A) obj;

            B b = (B) obj;

            a.toString();//注意这里调用了toString()方法

            b.getClass();//注意这里调用了getClass()方法

            //这里在A接口中添加了一个方法public Object aaa(String s1, int i);

            Object hello = a.aaa("Hello", 100);

            System.out.println(obj.getClass());//这里看一下代理对象是什么

            System.out.println(hello);//这里看一下返回值是什么

        }

    通过代码的结果我们大胆的猜测一下,代理对象方法的返回值其实就是invoke方法的返回值,代理对象其实就是使用反射机制实现的一个运行时对象。哈哈,当然这些肯定不是猜测了,其实确实就是这样。下面是时候总结一下InvocationHandler的invoke方法了。如下图所示:

    当我们调用代理对象的方法时,其对应关系就如上图所示。

    三、初步实现AOP

    在我们对动态代理有了一定的认识之后,我们就可以实现最基本版本的AOP了,当然,这是一个非常残缺的AOP实现,甚至都不能称之为AOP实现。

    我们先写一个接口:

    package demo2;

    /**

    * Created by Yifan Jia on 2018/6/5.

    */

    //服务生

    public interface Waiter {

        //服务方法

        public void server();

    }

    然后给出该接口的实现类:

    package demo2;

    /**

    * Created by Yifan Jia on 2018/6/5.

    */

    public class ManWaiter implements Waiter {

        @Override

        public void server() {

            System.out.println("服务中");

        }

    }

    然后我们就通过动态代理来对上面的ManWaiter进行增强:

    package demo2;

    import org.junit.Test;

    import java.lang.reflect.InvocationHandler;

    import java.lang.reflect.Method;

    import java.lang.reflect.Proxy;

    /**

    * Created by Yifan Jia on 2018/6/5.

    */

    public class Demo2 {

        @Test

        public void test1() {

            Waiter waiter = new ManWaiter();

            waiter.server();

        }

        @Test

        public void test2() {

            Waiter manWaiter = new ManWaiter();

            ClassLoader classLoader = this.getClass().getClassLoader();

            Class[] interfaces = {Waiter.class};

            InvocationHandler invocationHandler = new WaiterInvocationHandler(manWaiter);

            //得到代理对象,代理对象就是在目标对象的基础上进行了增强的对象

            Waiter waiter = (Waiter) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

            waiter.server();//前面添加“你好”,后面添加“再见”

        }

    }

    class WaiterInvocationHandler implements InvocationHandler {

        private Waiter waiter;

        WaiterInvocationHandler(Waiter waiter) {

            this.waiter = waiter;

        }

        @Override

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

            System.out.println("你好");

            waiter.server();//调用目标对象的方法

            System.out.println("再见");

            return null;

        }

    }

    结果如下:

    你肯定要说了,这算什么AOP,增强的代码都是硬编码到invoke方法中的,大家稍安勿躁,我们不是已经对需要增强的对象做了增强吗。这里可以的目标对象为manWaiter,增强为System.out.println("你好");和System.out.println("再见");,切点为server()方法调用。其实还是可以看做一下原始的AOP的。

    四、完善的AOP实现

    我们从初步实现的AOP中可以发现很多问题,比如我们不能把增强的逻辑硬编码到代码中,我们需要实现可变的增强,下面我们就解决一下这些问题,来实现一个比较完善的AOP。

    我们仍然引用上面的Waiter接口和Manwaiter实现类。

    然后我们添加一个前置增强接口:

    /**

    * 前置增强

    */

    public interface BeforeAdvice {

        public void before();

    }

    再添加一个后置增强接口:

    public interface AfterAdvice {

        public void after();

    }

    我们把产生代理对象的代码封装为一个类:

    package demo3;

    import com.sun.org.apache.regexp.internal.RE;

    import org.junit.After;

    import java.lang.reflect.InvocationHandler;

    import java.lang.reflect.Method;

    import java.lang.reflect.Proxy;

    /**

    * ProxFactory用来生成代理对象

    * 它需要所有的参数:目标对象,增强,

    * Created by Yifan Jia on 2018/6/5.

    */

    /**

    * 1、创建代理工厂

    * 2、给工厂设置目标对象、前置增强、后置增强

    * 3、调用creatProxy()得到代理对象

    * 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强

    */

    //其实在Spring中的AOP的动态代理实现的一个织入器也是叫做ProxyFactory

    public class ProxyFactory {

        private Object targetObject;//目标对象

        private BeforeAdvice beforeAdvice;//前值增强

        private AfterAdvice afterAdvice;//后置增强

        /**

        * 用来生成代理对象

        * @return

        */

        public Object creatProxy() {

            /**

            * 给出三个参数

            */

            ClassLoader classLoader = this.getClass().getClassLoader();

            //获取当前类型所实现的所有接口类型

            Class[] interfaces = targetObject.getClass().getInterfaces();

            InvocationHandler invocationHandler = new InvocationHandler() {

                @Override

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

                    /**

                    * 在调用代理对象的方法时,会执行这里的内容

                    */

                    if(beforeAdvice != null) {

                        beforeAdvice.before();

                    }

                    Object result = method.invoke(targetObject, args);//调用目标对象的目标方法

                    //执行后续增强

                    afterAdvice.after();

                    //返回目标对象的返回值

                    return result;

                }

            };

            /**

            * 2、得到代理对象

            */

            Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

            return proxyObject;

        }

    //get和set方法略

    }

    然后我们将相关的参数注入到ProxyFactory后就可以通过creatProxy()方法获取代理对象了,代码如下:

    package demo3;

    import org.junit.Test;

    /**

    * Created by Yifan Jia on 2018/6/5.

    */

    public class Demo3 {

        @Test

        public void tset1() {

            ProxyFactory proxyFactory = new ProxyFactory();//创建工厂

            proxyFactory.setTargetObject(new ManWaiter());//设置目标对象

            //设置前置增强

            proxyFactory.setBeforeAdvice(new BeforeAdvice() {

                @Override

                public void before() {

                    System.out.println("客户你好");

                }

            });

            //设置后置增强

            proxyFactory.setAfterAdvice(new AfterAdvice() {

                @Override

                public void after() {

                    System.out.println("客户再见");

                }

            });

            Waiter waiter = (Waiter) proxyFactory.creatProxy();

            waiter.server();

        }

    }

    结果如下:

    这时候我们已经可以自定义任意的增强逻辑了,是不是很神奇。

    五、动态代理实现AOP总结

    通过上面的内容,我们已经通过动态代理实现了一个非常简陋的AOP,这里的AOP实现还是有很多的不足之处。下面我把Spring中的ProxyFactory实现贴出来,大家可以研究一下Spring中的ProxyFactory的优势在哪里,另外,Spring中还有其他的基于动态代理实现的织入器,ProxyFactory只是其中最基础的版本,大家有兴趣可以研究一下。

    public class ProxyFactory extends ProxyCreatorSupport {

        public ProxyFactory() {

        }

        public ProxyFactory(Object target) {

            Assert.notNull(target, "Target object must not be null");

            this.setInterfaces(ClassUtils.getAllInterfaces(target));

            this.setTarget(target);

        }

        public ProxyFactory(Class... proxyInterfaces) {

            this.setInterfaces(proxyInterfaces);

        }

        public ProxyFactory(Class<?> proxyInterface, Interceptor interceptor) {

            this.addInterface(proxyInterface);

            this.addAdvice(interceptor);

        }

        public ProxyFactory(Class<?> proxyInterface, TargetSource targetSource) {

            this.addInterface(proxyInterface);

            this.setTargetSource(targetSource);

        }

        public Object getProxy() {

            return this.createAopProxy().getProxy();

        }

        public Object getProxy(ClassLoader classLoader) {

            return this.createAopProxy().getProxy(classLoader);

        }

        public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {

            return (new ProxyFactory(proxyInterface, interceptor)).getProxy();

        }

        public static <T> T getProxy(Class<T> proxyInterface, TargetSource targetSource) {

            return (new ProxyFactory(proxyInterface, targetSource)).getProxy();

        }

        public static Object getProxy(TargetSource targetSource) {

            if(targetSource.getTargetClass() == null) {

                throw new IllegalArgumentException("Cannot create class proxy for TargetSource with null target class");

            } else {

                ProxyFactory proxyFactory = new ProxyFactory();

                proxyFactory.setTargetSource(targetSource);

                proxyFactory.setProxyTargetClass(true);

                return proxyFactory.getProxy();

            }

        }

    }

  • 相关阅读:
    Selenium简单测试页面加载速度的性能(Page loading performance)
    Selenium Page object Pattern usage
    Selenium如何支持测试Windows application
    UI Automation的两个成熟的框架(QTP 和Selenium)
    分享自己针对Automation做的两个成熟的框架(QTP 和Selenium)
    敏捷开发中的测试金字塔(转)
    Selenium 的基础框架类
    selenium2 run in Jenkins GUI testing not visible or browser not open but run in background浏览器后台运行不可见
    eclipse与SVN 结合(删除SVN中已经上传的问题)
    配置Jenkins的slave节点的详细步骤适合windows等其他平台
  • 原文地址:https://www.cnblogs.com/zhuyeshen/p/12683901.html
Copyright © 2020-2023  润新知