Spring框架——自动代理
Spring框架中的面向切面思想在实现的过程中,核心的技术就是动态代理
目的:
在实际开发中,通常为目标类的所有业务方法附加通用性的增强功能,比如日志功能、事务处理等。最简单的方式,将表示增强功能的代码直接添加到业务方法中。
将表示增强功能的代码直接添加到业务方法中被称为:代码侵入
增强代码侵入业务代码所产生的问题:
(1)业务方法的核心功能不突出;
(2)增强代码与业务代码耦合在一起,不利于日后的维护
如何改进呢:
使用代理模式将增强代码与业务代码分离。
静态代理传送:
动态代理
首先看看静态代理的不足:
静态代理类的每个代理方法中,含有相同的增强代码,比如含有时间点记录的代码
如果目标对象含有很多的业务方法,比如几十个业务方法,会造成代理方法中含有大量的相同代码,产生代码冗余。
动态代理用到InvocationHandler接口
InvocationHandler接口中只有一个invoke方法,即
Object invoke(Object proxy, Method method, Object[] args)
invoke方法的作用,以反射的方式将代理方法的调用解析为统一的形式:
invoke(proxy,method, null);
在代理方法中,以反射的方式,通过调用InvocationHanler对象的invoke方法,实现代理方法的调用委派。
如何让JVM动态创建代理对象呢?
需要使用JavaSE JDK中的Proxy类。
Proxy类位于java.lang.reflect包下,是JVM所创建的动态代理类的父类。
Proxy类提供了如下的静态方法,用于通知JVM执行时,动态生成代理类(Proxy的子类),并创建代理对象;
static Object newProxyInstance
(ClassLoader loader,
Class<?>[ ] interfaces,
InvocationHandler h)
Proxy类的封装
采用工厂模式封装动态代理对象的创建过程:
(1)定义一个获得动态代理对象的静态方法
geDynamicProxy(IStringProcessing target),
其中参数类型是业务接口
(2)根据传递进来的目标对象,首先创建
InvocationHandler对象并设置它和目标对象
的依赖关系,然后再调用Proxy类的静态方法
newProxyInstanc(…),生成动态代理对象;
Demo:
已经实现一个接口中两个方法,现在想需要统计两个方法执行的时间。要求不能代码侵入,代理执行不能代码冗余。
1 /* 2 * 接口 3 */ 4 public interface IString { 5 public void addString();//拼接字符串 6 public void buildString();//缓存字符串 7 } 8 9 10 public class StringPro implements IString { 11 private final int AMOUNT=30000; 12 @Override 13 public void addString() { 14 //核心业务代码 15 System.out.println("adding string ..."); 16 String s=""; 17 for(int i=0;i<AMOUNT;i++){ 18 s+="A"; 19 } 20 } 21 22 @Override 23 public void buildString() { 24 //核心业务代码 25 System.out.println("building string ..."); 26 StringBuilder builder=new StringBuilder(); 27 for(int i=0;i<AMOUNT;i++){ 28 builder.append("A"); 29 } 30 } 31 32 } 33 34 35 import java.lang.reflect.InvocationHandler; 36 import java.lang.reflect.Method; 37 import java.util.Date; 38 39 /* 40 * 添加增强代码,并通过InvocationHandler减少冗余代码 41 */ 42 public class LoginHandler implements InvocationHandler { 43 private Object target; 44 45 public Object getTraget() { 46 return target; 47 } 48 49 public void setTraget(Object target) { 50 this.target = target; 51 } 52 53 public LoginHandler(){} 54 @Override 55 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 56 long start = System.currentTimeMillis(); 57 System.out.println(new Date(start) + "开始:"); 58 59 // 核心业务代码 60 61 /* 62 * 重点在这里:调用关联的target对象,而不是invoke方法传入的proxy对象, 63 * 通过这种替换可以实现调用对目标代码的增强,而不是代理对象。 64 */ 65 Object result = method.invoke(target, args); 66 // 增强代码 67 long end = System.currentTimeMillis(); 68 System.out.println(new Date(end) + "结束,耗时:" + (end - start)); 69 return result; 70 } 71 72 } 73 74 75 76 import java.lang.reflect.Proxy; 77 78 public class ProxyFactory { 79 public static Object getDynamicProxy(IString target){ 80 LoginHandler l = new LoginHandler(); 81 l.setTraget(target); 82 //通过Proxy对象的静态方法创建代理对象 83 Object proxyObject = Proxy.newProxyInstance( 84 target.getClass().getClassLoader(), 85 target.getClass().getInterfaces(), l); 86 return proxyObject; 87 } 88 } 89 90 91 92 93 import proxy.IString; 94 import proxy.ProxyFactory; 95 import proxy.StringPro; 96 97 98 public class TestIoc { 99 private static final String LOCATION = "applicationContext.xml"; 100 public static void main(String[] args) { 101 IString is = new StringPro(); 102 IString dproxy = (IString) ProxyFactory.getDynamicProxy(is); 103 System.out.println(dproxy.getClass().getName()); 104 dproxy.addString(); 105 System.out.println(); 106 dproxy.buildString(); 107 } 108 }