代理

来源:互联网 发布:守望先锋点赞 知乎 编辑:程序博客网 时间:2024/04/27 20:14


 代理


知识概要:

(1)代理的概念与作用

(2)动态代理技术

(3)分析JVM动态生成的类

(4)创建动态类的示例对象和方法

(5)分析InvocationHandler对象的运行原理

(6)分析动态类的原理结构

(7)实现类似spring的可配置的AOP框架


代理的概念与作用

生活中的代理


武汉人从武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是,一点区别都没有吗?从代理商那里买真的一点好处都没有吗?


程序中的代理


要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?


编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)



AOP

系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:


用具体的程序代码描述交叉业务:

method1         method2          method3

{                      {                       {


------------------------------------------------------切面


....                    ....                     ......


------------------------------------------------------切面


}                       }                       }

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采

用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:


------------------------------------------------------切面

func1     func2    func3

{             {                {

....            ....              ......

}             }                }

------------------------------------------------------切面


使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。


代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中


例子:


class proxy{


void sayHello(){


……….

try{


target.sayHello();

}catch(Exception e){

………..

}

………….

}


}


分析JVM动态生成的类

package cn.itheima.day2;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Collection;public class ProxyTest { public static void main(String[] args) { /*  * 编码列出动态类中的所有构造方法和接收参数的类型。  */ Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy.getName()); //$Proxy0System.out.println("--------begin constructors list-------"); Constructor[] constructors = clazzProxy.getConstructors(); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sb = new StringBuilder(name); sb.append("("); Class[] clazzParams = constructor.getParameterTypes(); for(Class clazzParam : clazzParams) { sb.append(clazzParam.getName()).append(","); } if (clazzParams !=null && clazzParams.length != 0) { sb.deleteCharAt(sb.length()-1); } sb.append(")"); System.out.println(sb.toString()); } /*  *  编码列出动态类中的所有方法和接收参数的类型。  * */ System.out.println("--------begin method list-------"); Method [] methods = clazzProxy.getMethods(); for (Method method : methods) { String name = method.getName(); StringBuilder sb =new StringBuilder(name); sb.append("("); Class[] clazzParams = method.getParameterTypes(); for (Class clazzParam : clazzParams) { sb.append(clazzParam.getName()).append(","); } if(clazzParams !=null && clazzParams.length != 0) { sb.deleteCharAt(sb.length()-1); } sb.append(")"); System.out.println(sb.toString()); } }} 

创建动态类的示例对象和方法

步骤:

1、创建动态类的实例对象。通过反射获得构造方法。

2、编写一个最简单的InvocationHandler类。

3、调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去。

4、将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类



<span style="font-size:18px;">package com.itheima.day03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; public class ProxyTest { public static void main(String[] args)throws Exception { Class<?> clazzProxy =Proxy.getProxyClass( Collection.class.getClassLoader(),  Collection.class); Collection<?> proxy =  (Collection<?>)clazzProxy.getConstructor(InvocationHandler.class).newInstance( new InvocationHandler(){ @Override public Object invoke(Object proxy,Method method,Object[] args)  throws Throwable{ return null; } }); System.out.println(proxy); //结果:null proxy.clear(); //执行没有返回值的方法,不会报告异常 proxy.size(); //执行有返回值的方法,会报告异常 } } </span>
<span style="font-size:18px;">代码示例: package com.itheima.day03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class ProxyTest { @SuppressWarnings("unchecked") public static void main(String[] args) throws Exception { @SuppressWarnings("rawtypes") Collection proxy = (Collection)Proxy.newProxyInstance( Collection.class.getClassLoader(), new Class[] {Collection.class },  new InvocationHandler() { ArrayList<String> target =new ArrayList<String>(); @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime =System.currentTimeMillis(); System.out.println(method.getName() +" 运行时间 " + (endTime - startTime) + "秒"); return retVal; } });proxy.add("黑马程序员"); proxy.add("黑马论坛"); proxy.add("CSDN社区"); System.out.println(proxy.size()); //结果:3 }} </span>

如果作为target的ArrayList对象放置在invoke方法内部定义,那么每次调用代理的某个方法,都会调用invoke


方法,这样作为target的ArrayList对象每次都会被创建,这样就导致最后调用proxy.size()的时候,结果为0


分析InvocationHandler对象的运行原理


        动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方

        法和一个InvocationHandler参数的构造方法。

构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?

答:接受的对象会通过构造函数赋值给某个成员变量。



}

     实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke  方法接受的

三个参数又是什么意思?图解说明如下:


$Proxy0 implements Collection{


InvocationHandler handler;

public $Proxy0(InvocationHandler handler){


this.handler = handler;

}




//生成的Collection接口中的方法的运行原理


int size(){


return handler.invoke(this,this.getClass().getMethod("size"),null);


}


void clear(){


handler.invoke(this,this.getClass().getMethod("clear"),null);


}


boolean add(Object obj){


return handler.invoke(this,this.getClass().getMethod("add"),obj);


}


}

总结分析动态代理类的设计原理与结构

         将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数 

        形式提供?

把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即

等于执行了外界提供的代码!为方法增加一个Advice参数

package cn.itheima.day2;import java.lang.reflect.Method;public interface Advice {void beforeMethod();void afterMethod(Object target,Method method);}
package cn.itheima.day2;import java.lang.reflect.Method; public class MyAdvice implements Advice{ private long startTime;@Override public void beforeMethod() {      startTime = System.currentTimeMillis(); } @Override public void afterMethod(Object target ,Method method) { long endTime =System.currentTimeMillis(); String objName = target.getClass().getName(); System.out.println("调用"+objName.substring((objName.lastIndexOf(".")+1),objName.length())+  "对象的"+method.getName()+"方法 耗时"+(endTime-startTime)+"秒"); } } 


package cn.itheima.day2;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;public class ProxyTest { public static void main(String[] args)throws Exception { /*  * 编码列出动态类中的所有构造方法和接收参数的类型。  */ Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy.getName()); //$Proxy0System.out.println("--------begin constructors list-------"); Constructor[] constructors = clazzProxy.getConstructors(); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sb = new StringBuilder(name); sb.append("("); Class[] clazzParams = constructor.getParameterTypes(); for(Class clazzParam : clazzParams) { sb.append(clazzParam.getName()).append(","); } if (clazzParams !=null && clazzParams.length != 0) { sb.deleteCharAt(sb.length()-1); } sb.append(")"); System.out.println(sb.toString()); } /*  *  编码列出动态类中的所有方法和接收参数的类型。  * */ System.out.println("--------begin method list-------"); Method [] methods = clazzProxy.getMethods(); for (Method method : methods) { String name = method.getName(); StringBuilder sb =new StringBuilder(name); sb.append("("); Class[] clazzParams = method.getParameterTypes(); for (Class clazzParam : clazzParams) { sb.append(clazzParam.getName()).append(","); } if(clazzParams !=null && clazzParams.length != 0) { sb.deleteCharAt(sb.length()-1); } sb.append(")"); System.out.println(sb.toString()); } Class<?> clazzProxy2 =Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class); Collection<?> proxy =(Collection<?>)clazzProxy2.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){ public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{ return null; } });System.out.println(proxy); //结果:null proxy.clear(); //执行没有返回值的方法,不会报告异常 //proxy.size(); //执行有返回值的方法,会报告异常 ArrayList target = new ArrayList();Collection proxy2 =(Collection) getProxy(target,new MyAdvice());proxy2.add("xyz");proxy2.add("abc");proxy2.add("cui");System.out.println(proxy2.size());}private static Object getProxy(final Object target,final Advice advice) {Object proxy2 = Proxy.newProxyInstance(target.getClass().getClassLoader(),/*new Class[]{Collection.class},*/target.getClass().getInterfaces(),new InvocationHandler(){public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{advice.beforeMethod();Object retVal = method.invoke(target,args);advice.afterMethod(target, method);return retVal;}});return proxy2;}}package cn.itheima.day2;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collection;public class ProxyTest { public static void main(String[] args)throws Exception { /*  * 编码列出动态类中的所有构造方法和接收参数的类型。  */ Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); System.out.println(clazzProxy.getName()); //$Proxy0System.out.println("--------begin constructors list-------"); Constructor[] constructors = clazzProxy.getConstructors(); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sb = new StringBuilder(name); sb.append("("); Class[] clazzParams = constructor.getParameterTypes(); for(Class clazzParam : clazzParams) { sb.append(clazzParam.getName()).append(","); } if (clazzParams !=null && clazzParams.length != 0) { sb.deleteCharAt(sb.length()-1); } sb.append(")"); System.out.println(sb.toString()); } /*  *  编码列出动态类中的所有方法和接收参数的类型。  * */ System.out.println("--------begin method list-------"); Method [] methods = clazzProxy.getMethods(); for (Method method : methods) { String name = method.getName(); StringBuilder sb =new StringBuilder(name); sb.append("("); Class[] clazzParams = method.getParameterTypes(); for (Class clazzParam : clazzParams) { sb.append(clazzParam.getName()).append(","); } if(clazzParams !=null && clazzParams.length != 0) { sb.deleteCharAt(sb.length()-1); } sb.append(")"); System.out.println(sb.toString()); } Class<?> clazzProxy2 =Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class); Collection<?> proxy =(Collection<?>)clazzProxy2.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){ public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{ return null; } });System.out.println(proxy); //结果:null proxy.clear(); //执行没有返回值的方法,不会报告异常 //proxy.size(); //执行有返回值的方法,会报告异常 ArrayList target = new ArrayList();Collection proxy2 =(Collection) getProxy(target,new MyAdvice());proxy2.add("xyz");proxy2.add("abc");proxy2.add("cui");System.out.println(proxy2.size());}private static Object getProxy(final Object target,final Advice advice) {Object proxy2 = Proxy.newProxyInstance(target.getClass().getClassLoader(),/*new Class[]{Collection.class},*/target.getClass().getInterfaces(),new InvocationHandler(){public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{advice.beforeMethod();Object retVal = method.invoke(target,args);advice.afterMethod(target, method);return retVal;}});return proxy2;}}


实现类似spring的可配置的AOP框架

工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
 #xxx=java.util.ArrayList
 xxx=cn.itcast.ProxyFactoryBean
 xxx.target=java.util.ArrayList
 xxx.advice=cn.itcast.MyAdvice
ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标
通知
编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象


演示代码

package cn.itheima.day2.aopframwork;import java.lang.reflect.Method;public interface Advice {void beforeMethod();void afterMethod(Object target,Method method);}

package cn.itheima.day2.aopframwork;import java.lang.reflect.Method; public class MyAdvice implements Advice{ private long startTime;@Override public void beforeMethod() {      startTime = System.currentTimeMillis(); } @Override public void afterMethod(Object target ,Method method) { long endTime =System.currentTimeMillis(); String objName = target.getClass().getName(); System.out.println("调用"+objName.substring((objName.lastIndexOf(".")+1),objName.length())+  "对象的"+method.getName()+"方法 耗时"+(endTime-startTime)+"秒"); } } 




package cn.itheima.day2.aopframwork;import java.io.InputStream;import java.util.Properties;public class BeanFactory{Properties props = new Properties();public BeanFactory(InputStream ips){try{props.load(ips);}catch (Exception e){e.printStackTrace();}}public Object getBean(String name){String className =props.getProperty(name);Object bean = null;try{Class clazz= Class.forName(className);        bean = clazz.newInstance();}catch (Exception e){e.printStackTrace();}if(bean instanceof ProxyFactoryBean){ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;Object proxy = null; try { Advice advice = (Advice)Class.forName( props.getProperty(name + ".advice")).newInstance(); Object target =Class.forName( props.getProperty(name + ".target")).newInstance(); proxyFactoryBean.setAdvice(advice); proxyFactoryBean.setTarget(target); proxy = proxyFactoryBean.getProxy(); } catch (Exception e) { e.printStackTrace(); } return proxy;}return bean;}}


package cn.itheima.day2.aopframwork;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactoryBean { private Advice advice; private Object target; public ProxyFactoryBean(){} public Advice getAdvice() { return advice; } public Object getTarget() { return target; } public void setAdvice(Advice advice) { this.advice = advice; } public void setTarget(Object target) { this.target = target; } public Object getProxy(){ Object objProxy =Proxy.newProxyInstance( target.getClass().getClassLoader(),  target.getClass().getInterfaces(),  new InvocationHandler() { @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { advice.beforeMethod();Object retVal = method.invoke(target, args);  advice.afterMethod(target ,method); return retVal; } }); return objProxy; } } 


package cn.itheima.day2.aopframwork;import java.io.InputStream; public class AopFrameworkTest { public static void main(String[] args)throws Exception { InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties"); Object objProxy = new BeanFactory(ips).getBean("proxy"); System.out.println(objProxy.getClass().getName()); } } 

config.propetry

运行结果





0 0
原创粉丝点击