什么是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的动态代理必须实现接口