Spring AOP底层原理之动态代理模式

什么是AOP?

  AOP(Aspect Oriented Programming)  面向切面编程。

  指在程序运行期间,将某段代码动态切入到指定位置进行运行的这种编程方式。

什么是动态代理?

  有一个这样的场景  在执行业务逻辑代码的时候加上日志输出  我们可以尝试用动态代理的方法实现

  首先定义一个业务逻辑的接口(很重要!下面解释), 里面有业务逻辑的一些方法

  

public interface MyTask{
      void method1(Object arg1,Object arg2);
      void method2(Object arg1);    
}    

  真实的业务实现了这个接口

public class Task1 implements MyTask{

    public void method1(Object arg1, Object arg2) {
        System.out.println("执行业务1...");
    }

    public void method2(Object arg1) {
        System.out.println("执行业务2...");
    }
}

  现在我们可以添加日志功能,如果不适用动态代理   我们可以在每个业务方法内部加入日志输入  如下

public class Task1 implements MyTask{

    public void method1(Object arg1, Object arg2) {
        System.out.println("日志:method1方法前……");
        System.out.println("执行业务1...");
        System.out.println("日志:method1方法后……");
    }

    public void method2(Object arg1) {
        System.out.println("日志:method2方法前……");
        System.out.println("执行业务2...");
        System.out.println("日志:method2方法后……");
    }
}

  但是这样做有明显的缺点:

    1)如果有大量的方法  日志写起来非常的麻烦  

    2)日志是辅助功能,业务是核心功能  这样辅助和核心就写到了一起  存在耦合问题!

  我们可以用动态代理模式解决上述问题

    首先我们的Task2  中的业务逻辑  不加任何的日志输出

    

public class Task2 implements MyTask{
    public void method1(Object arg1, Object arg2) {
        System.out.println("执行业务1...");
    }

    public void method2(Object arg1) {
        System.out.println("执行业务2...");
    }
}

    我们需要创建一个代理类ProxyHandler 实现 InvocationHandler 方法如下   

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

public class ProxyHandler implements InvocationHandler {
    /**
     *  传入真实角色
     */
    private Object target;
    /**
     *
     * @param target 注入真实角色
     */
    public void setTarget(Object target) {
        this.target = target;
    }
    /**
     * @param proxy  代理对象;给jdk使用,任何时候都不要动这个对象
     * @param method  当前将要执行目标对象的方法
     * @param args  这个方法调用时候  外界传入的值
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志:执行" + method.getName() + "前");
        method.invoke(target,args);
        System.out.println("日志:执行" + method.getName() + "后");
        return null;
    }
}

  最后可以测试执行

import java.lang.reflect.Proxy;
public class Client {
    public static void main(String[] args) {
        Task2 task2 = new Task2();
        ProxyHandler handler = new ProxyHandler();
        handler.setTarget(task2);
//        设置要代理类的对象
        /*
            通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
            第一个参数:host.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
                      就是host的类加载器

            第二个参数:host.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,
                      这样代理对象就能像真实对象一样调用接口中的所有方法

            第三个参数:handler,我们将代理对象关联到上面的ProxyHandler自定义对象上
                      方法执行器,帮我们目标对象执行目标方法
         */
        MyTask proxy = (MyTask) Proxy.newProxyInstance(task2.getClass().getClassLoader(),
                task2.getClass().getInterfaces(),handler); // 动态生成代理类
        proxy.method1(1,2);
        proxy.method2(1);
    }
}

  执行结果如下 

  日志:执行method1前
  执行业务1...
  日志:执行method1后
  日志:执行method2前
  执行业务2...
  日志:执行method2后

动态代理类的缺点

  1、复杂难写

  2、java默认的动态代理  必须实现接口  否则不能动态代理

    原因:首先我们看一下动态代理Proxy的类  它是哪个类?

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(proxy.getClass());
        return null;
    }

执行结果如下:class com.sun.proxy.$Proxy0

  这说明 proxy的类和我们的Task2根本不是一个类  那么proxy为什么能执行我们定义的Task2类中的方法呢?

  这是因为proxy和我们的Task2实现了同一个接口  那就是MyTask  这也就是为什么我们能放心的将proxy类强制转换为MyTask的原因

  所以java jdk的动态代理必须实现接口

Spring 中AOP底层也是使用的动态代理  但是它解决了java动态代理的两个缺点!  在我之后的随笔中会继续讨论!

猜你喜欢

转载自www.cnblogs.com/lxy-java/p/12821534.html