(一)代理概述
1、问题:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理等等,如何去做?
解答:编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
2、代理原理图,如下:
3、代理的优点
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换。例如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,
通过使用这个类和接口就可以生成动态代理对象。
JDK提供的代理只能针对接口做代理。
(1)Proxy类中的方法创建动态代理类对象
他有一个方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
该方法用来产生一个object类型的代理对象
该对象并没有对应的.class文件,但在内存中有对应Class类型
实现 :
Object o =
java.lang.reflect.Proxy.
newProxyInstance(ClassLoader loader,Class[] classes, InvocationHandler handler);
参数说明:
ClassLoader loader 类加载器 用于把.class文件(字节码文件)加载到内存中 通过getClassLoader()方法获取
Class[] classes 目标接口的类型数组
InvocationHandler handler 接口调用处理器
(2)InvocationHandler接口
只有1个方法
public Object invoke(Object proxy, Method method, Object[] args)
当代理对象调用父接口中定义的方法时 执行的就是该方法
1.IGDG package com.pers.proxy; public interface IGDG { void doSomeThing(); }
2.TargetGDG package com.pers.proxy; public class TargetGDG implements IGDG{ @Override public void doSomeThing() { System.out.println("我是目标类 正在忙着很多事情"); } }
3.ProxyGDG package com.pers.proxy; /* * 代理类 与目标类TargetGDG具有相同的接口 * 代理类的每个方法调用目标类的相同方法 并在调用方法的时候加上系统功能的代码 */ public class ProxyGDG implements IGDG { public void doSomeThing() { addLogging(); new TargetGDG().doSomeThing();//目标类的实现方法 doException(); } /* * 我要横切加入到目标类中的系统方法 这样的话就不用往每个类中都去加 */ public void addLogging(){ System.out.println("为每个类都加上日志"); } public void doException(){ System.out.println("为每个类都处理异常"); } //..........省略其他系统功能的代码 }
4.
ProxyDemo
package com.pers.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyDemo { public static void main(String[] args) { // TODO Auto-generated method stub //1.声明代理类对象 final Object obj = new ProxyGDG(); System.out.println("声明被代理的对象是:"+obj.getClass());//com.pers.proxy.ProxyGDG //使用proxy类创建IGDG接口的子类 Object proxyObject = Proxy.newProxyInstance( ProxyDemo.class.getClassLoader(), new Class[]{IGDG.class}, new InvocationHandler(){ @Override public Object invoke(Object proxyObject, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("你正在执行的方法名是:"+method.getName()); return method.invoke(obj,args); } }); //将子类强转为IGDG IGDG iGDG = (IGDG)proxyObject; iGDG.doSomeThing();//iGDG是代理 System.out.println("声明被代理的对象是:"+iGDG.getClass());//$Proxy0 } }
动态生成的类的内部代码分析
实现Collection接口的动态类中的各个方法的代码又是怎样的呢? InvocationHandler接口中定义的invoke方法接收的三个参数又是什么意思?
InvocationHandler接口中定义的invoke方法接收的三个参数意义,如下图说明:
说明:客户端调用了代理对象objProxy,调用了代理对象的add()方法,为该方法传递了字符串参数"abc"。
通过分析代码可以看出Java 动态代理,具体有如下四步骤:
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。