程序中代理的概念与作用
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等。
代理架构图:
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类,还是使用代理类,
这样以后很容易切换,例如:想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易。
1.分析代理类的作用与原理及AOP概念
代理是实现AOP功能的核心和关键技术。
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面。
安全,事务,日志等功能要贯穿到好多个模块中,所以,他们就是交叉业务
用具体的程序代码描述交叉业务:
交叉业务的编程问题即为面向方面的变成(Aspect oriented program,简称AOP,AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围。)
2.动态代理技术
> JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
> JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理.
*如果目标类没有实现接口,要想生成代理使用 CGLIB
> CGLIB 库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的的类生成动态代理类,那么可以使用CGLIB库。
> 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
> 1.在调用目标方法之前
> 2.在调用目标方法之后
> 3.在调用目标方法前后
> 4.在处理目标方法异常的catch块中
void sayHello(){ .............. try{ target.sayHello(); }catch(Exception e){ ............... } ................. }
创建动态类及查看其方法列表信息
分析JVM动态生成的类
创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。
编码列出动态类中的所有构造方法和参数签名
编码列出动态类中的所有方法和签名
创建动态类的实例对象
> 用反射获得构造方法
> 编写一个最简单的InvocationHandler类
> 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
> 打印创建的对象和调用对象的没有返回值得方法和getClass方法,调用其他有返回值的方法会抛异常。
public class ProxyTest { public static void main(String[] args) { // 1.先创建动态类 ,在创建实例对象 Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy1.getName()); System.out.println("-----------begin constructors list"); /*$Proxy0() $Proxy0(InvocationHandler,int)*/ Constructor[] constructors = clazzProxy1.getConstructors(); //返回所有的构造方法 for(Constructor constructor: constructors){ String name = constructor.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] clazzParams = constructor.getParameterTypes();//返回所有的参数列表类型 for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(","); } if(clazzParams!= null && clazzParams.length != 0){ sBuilder.deleteCharAt(clazzParams.length-1); //去掉最后一个 , } sBuilder.append(")"); System.out.println(sBuilder.toString()); } System.out.println("-----------begin methods list"); /*$Proxy0() $Proxy0(InvocationHandler,int)*/ Method[] methods = clazzProxy1.getMethods(); //返回所有的方法 for(Method method: methods){ String name = method.getName(); StringBuilder sBuilder = new StringBuilder(name); sBuilder.append("("); Class[] clazzParams = method.getParameterTypes();//返回所有的参数列表类型 for(Class clazzParam : clazzParams){ sBuilder.append(clazzParam.getName()).append(","); } if(clazzParams!= null && clazzParams.length != 0){ sBuilder.deleteCharAt(sBuilder.length()-1); //去掉最后一个 , } sBuilder.append(")"); System.out.println(sBuilder.toString()); } } }
> InvocationHandler 对象的内部功能
Constructor constructor = clazzProxy1 .getConstructor(InvocationHandler.class); class MyInvocationHander1 implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } } Collection proxy1 = (Collection) constructor .newInstance(new MyInvocationHander1()); System.out.println(proxy1); // 这里返回为null 是因为proxy1.toString()为null proxy1.clear(); // proxy1.size(); //由于有返回值,会抛异常 Collection proxy2 = (Collection) constructor .newInstance(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } }); // 2.在创建动态类时直接实例化对象 Collection proxy3 = (Collection) Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[] { Collection.class }, new InvocationHandler() { ArrayList target = new ArrayList<>(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object object = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - startTime)); return object; } }); proxy3.add("zxx"); proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size());
/**
* $proxy0 为什么返回的不是ArrayList而是一个代理
* 因为getClass()是从Object上继承的,Object中只有三个方法时交给handler处理的 hashCode,equals,toString
* 所以调用getClass()时,返回的是自己实现的
*/
System.out.println(proxy3.getClass().getName());
> InvocationHandler 对象的运行原理
Client程序调用objProxy.add("abc")方法时,涉及三个要素:objProxy对象、add方法、"abc"参数
Class Proxy${
add(Object object){
return handler.invoke(Object proxy, Method method, Object[] args);
}
}
> 面向切面编程:
把切面的代码,以对象的形式进行封装,以对象的形式传递给你,你只要执行这个对象,就等于执行了切面的代码
> 编写可生成代理和插入通告的通用方法
Collection proxy3 = (Collection)getProxy(new ArrayList<>(),new MyAdvice()); //每调用一次,都会调用 一次InvocationHandler 的 invoke方法 proxy3.add("zxx"); proxy3.add("lhm"); proxy3.add("bxd"); System.out.println(proxy3.size());
getProxy方法
private static Object getProxy(final Object target, final MyAdvice advice) { Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.beforeMethod(method); Object object = method.invoke(target, args); advice.afterMethod(method); return object; } }); return proxy3; }
MyAdvice类:
public class MyAdvice implements Advice { long startTime = 0; @Override public void beforeMethod(Method method) { startTime = System.currentTimeMillis(); } @Override public void afterMethod(Method method) { long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - startTime)); } }
目的是把 代理 和系统功能都转义成了外边的 对象
完成Spring 只完成一件事:写MyAdvice, target是在配置文件中配置的