代理类

来源:互联网 发布:icloud软件下载 编辑:程序博客网 时间:2024/06/10 19:43

代理概念:厂商与消费者之间的中间环节

程序中的代理:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,

例如,异常处理、日志、计算方法的运行时间、事务管理、等等

如要在一个类中的方法的前后加入System.currentTimeMillis()来计算方法运行时间

在一个方法调用后写日记

我的理解:在目标方法的基础上添加辅助功能,以实现特殊的需求(有子类继承父类的感觉和二次开发的感觉)。

可见,代理类有诸多的好处

但是要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!

写成百上千个代理类,是不是太累!-->动态代理类


JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,

即动态代理类。

JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有

相同接口的目标类的代理。

CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,

如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库

来自:张孝祥_Java_基础加强_高新技术


我的理解:

Proxy提供了两个方法来实现代理类:

1、staticClass<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。

得到代理类,要通过其构造方法来实例化代理类对象

protected Proxy(InvocationHandle h),这就需要实现InvocationHandle接口,具体实现看后面代码


2、staticObject newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

直接得到代理类对象


一般4个地方添加辅助功能:

在调用目标方法之前

在调用目标方法之后

在调用目标方法前后

在处理目标方法异常的catch块中

//beforeMethod语句...
Object retVal = method.invoke(obj, args);    //调用目标方法
//afterMethod语句...

//both(before,after...)
//catch语句...

代理框架图:


客户端程序Client不直接调用Target,而是由Proxy代理调用Target,Client只要调用Proxy就可以了

代理类和Target实现同一个接口。

怎么理解代理类和目标类要实现相同的接口(可能多个)?

代理类的创建:

eg. Collection obj = new ArrayList<String>();   //要代理的对象(目标对象)

 Class clazz = Proxy.getProxyClass(obj.getClass().getClassLoader(), obj.getClass().getInterfaces());



InnvocationHandle 接口中定义的invoke方法接受的三个参数含义??

ArrayList<String> array = new ArrayList<String>();
Collection objProxy= (Collection)getProxy(array,new AdviceImpl());
objProxy.add("abc");



value = constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//beforeMethod语句...
Object retVal = method.invoke(obj, args);
//afterMethod语句...
//both(before,after...)
//catch语句...
return retVal;
}
}

注意:如果invoke的类型返回的是NULL,而又调用  objProxy.size()  ,它返回的是int,

那么就会报错。



动态代理的工作原理图:



package com.interview.proxy;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) {////得到所有的构造方法//getAllConstructors(Collection.class);////得到所有的方法//getAllMethods(Collection.class);//测试getProxy(final Object obj),在方法调用前和调用后的语句只能写死//ArrayList<String> array = new ArrayList<String>();//Collection lists = (Collection)getProxy(array);//lists.add("张三");//lists.add("李四");//System.out.println("大小为:"+lists.size());ArrayList<String> array = new ArrayList<String>();Collection lists = (Collection)getProxy(array,new AdviceImpl());lists.add("张三");lists.add("李四");System.out.println("大小为:"+lists.size());}/** * 利用目标对象(要代理的对象)得到代理类,查看代理类所有的构造方法并打印 * @param obj 对象 */public static void getAllConstructors(Object obj){Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);StringBuilder build = new StringBuilder();Constructor[] constructors = clazz.getConstructors();for(Constructor constructor: constructors){build.append(clazz.getName());build.append("(");Class[] params = constructor.getParameterTypes();for(Class param : params){build.append(param.getName()+",");}if(params.length >0)build.deleteCharAt(build.length()-1);build.append(")");System.out.println(build.toString());build.delete(0, build.length());}}/** * 利用目标对象(要代理的对象)得到代理类,查看代理类所有的方法并且打印 * @param obj 对象 */public static void getAllMethods(Object obj){Class clazz = Proxy.getProxyClass(obj.getClass().getClassLoader(), obj.getClass().getInterfaces());StringBuilder build = new StringBuilder();Method[] methods = clazz.getMethods();for(Method method: methods){build.append(method.getName());build.append("(");Class[] params = method.getParameterTypes();for(Class param : params){build.append(param.getName()+",");}if(params.length >0)build.deleteCharAt(build.length()-1);build.append(")");System.out.println(build.toString());build.delete(0, build.length());}}/** * 原理:通过Proxy的构造方法,利用反射技术实例化一个 * 需求:这个方法就要的beforeMethod方法等要为具体的语句,这样灵活性就差很多 *  这样催生出一个需求:参数中传递语句,但是java中没有类似javascript的eval("..语句"); *  解决方法是java可以传递对象,于是有了Advice接口(建议,契约),对象要实现这个接口, *  从而传递相应的java语句 * @param 需要代理的对象 * @return 对象的代理类 */public static Object getProxy(final Object obj){Object value = null ;Class clazz = Proxy.getProxyClass(obj.getClass().getClassLoader(), obj.getClass().getInterfaces());try {Constructor constructor = clazz.getConstructor(InvocationHandler.class);value = constructor.newInstance(new InvocationHandler(){public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//beforeMethod语句...Object retVal = method.invoke(obj, args);//afterMethod语句...//both(before,after...)//catch语句...return retVal;}});} catch (Exception e) {e.printStackTrace();}return value;}/** * 直接利用Proxy提供的类来实例化一个代理类,更加方便简单 * @param target 需要代理的对象 * @param advice 建议,契约,Advice是一个借口,定义了beforeMethod,afterMethod * 方法,由AdviceImpl类来实现,可以通过这个接口灵活的添加这样的语句 *  * 把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递, * 接收者只要调用这个对象的方法,即等于执行了外界提供的代码 * @return 对象的代理类 */public static Object getProxy(final Object target ,final Advice advice){Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),   //得到所以父类接口new InvocationHandler(){public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//Advice接口advice.beforeMethod(method);Object retVal = method.invoke(target, args);advice.afterMethod(method);return retVal;}});return obj;}}

Advice接口:

package com.interview.proxy;import java.lang.reflect.Method;public interface Advice {void beforeMethod(Method method);void afterMethod(Method method);}

Advice实现类AdviceImpl :

package com.interview.proxy;import java.lang.reflect.Method;public class AdviceImpl implements Advice {private Long beginTime;   //开始时间public void afterMethod(Method method) {Long endTime = System.currentTimeMillis();System.out.println(method.getName()+"方法调用时间为:"+(endTime-beginTime));}public void beforeMethod(Method method) {beginTime = System.currentTimeMillis();}}





0 0