java 动态代理类的实现,原理及应用
在目前的Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。
其实现主要通过是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现,如下,HelloWorld接口定义的业务方法,HelloWorldImpl是HelloWorld接口的实现,HelloWorldHandler是InvocationHandler接口实现。代码如下:
业务接口:
public interface HelloWorld { void sayHelloWorld() ;}业务接口实现:
public class HelloWorldImpl implements HelloWorld { public void sayHelloWorld() { System.out.println("Hello World!"); }}InvocationHandler实现,需要在接口方法调用前后加入一部份处理工作,这里仅仅在方法调用前后向后台输出两句字符串,其代码如下:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class HelloWorldHandler implements InvocationHandler { //要代理的原始对象 private Object objOriginal; /** * 构造函数。 * @param obj 要代理的原始对象。 */ public HelloWorldHandler(Object obj) { this.objOriginal = obj ; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result ; //方法调用之前 doBefore(); //调用原始对象的方法 result = method.invoke(this.objOriginal ,args); //方法调用之后 doAfter(); return result ; } private void doBefore() { System.out.println("before method invoke!"); } private void doAfter() { System.out.println("after method invoke!"); }}测试代码:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { HelloWorld hw = new HelloWorldImpl(); InvocationHandler handler = new HelloWorldHandler(hw); HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance( hw.getClass().getClassLoader(), hw.getClass().getInterfaces(), handler); proxy.sayHelloWorld(); }}? 首先获取一个业务接口的实现对象;
? 获取一个InvocationHandler实现,此处是HelloWorldHandler对象;
? 创建动态代理对象;
? 通过动态代理对象调用sayHelloWorld()方法,此时会在原始对象HelloWorldImpl. sayHelloWorld()方法前后输出两句字符串。
运行测试类输出如下:
before method invoke!Hello World!after method invoke!
此处Test类中的方法调用代码比较多,在我们的实际应用中可以通过配置文件来来简化客户端的调用实现。另外也可以通过动态代理来实现简单的AOP。
1. 代理模式
代理模式是常用的Java设计模式,它的特征是代理类与委托类有同样的接口,如下图所示。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理类的创建时期,代理类可分为两种。
◆静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
◆动态代理类:在程序运行时,运用反射机制动态创建而成。
2.动态代理类
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect包中的Proxy类和 InvocationHandler接口提供了生成动态代理类的能力。
2.1 Proxy类
Proxy类提供了创建动态代理类及其实例的静态方法。
(1)getProxyClass()静态方法负责创建动态代理类,它的完整定义如下:
public static Class getProxyClass(ClassLoader loader, Class[] interfaces) throws IllegalArgumentException
参数loader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的所有接口。
(2)newProxyInstance()静态方法负责创建动态代理类的实例,它的完整定义如下:
public static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler handler)throws IllegalArgumentException
参数loader指定动态代理类的类加载器,参数interfaces指定动态代理类需要实现的所有接口,参数handler指定与动态代理类关联的 InvocationHandler对象。
举例,以下两种方式都创建了实现Foo接口的动态代理类的实例:
View Code/**** 方式一 ****///创建InvocationHandler对象InvocationHandler handler = new MyInvocationHandler(...);//创建动态代理类Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });//创建动态代理类的实例Foo foo = (Foo) proxyClass.getConstructor( new Class[] { InvocationHandler.class}).newInstance(new Object[] { handler });/**** 方式二 ****///创建InvocationHandler对象InvocationHandler handler = new MyInvocationHandler(...);//直接创建动态代理类的实例Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[] { Foo.class },handler);由Proxy类的静态方法创建的动态代理类具有以下特点:
◆动态代理类是public、final和非抽象类型的;
◆动态代理类继承了java.lang.reflect.Proxy类;
◆动态代理类的名字以“$Proxy”开头;
◆动态代理类实现getProxyClass()和newProxyInstance()方法中参数interfaces指定的所有接口;
◆Proxy类的isProxyClass(Class cl)静态方法可用来判断参数指定的类是否为动态代理类。只有通过Proxy类创建的类才是动态代理类;
◆动态代理类都具有一个public类型的构造方法,该构造方法有一个InvocationHandler类型的参数。
由Proxy类的静态方法创建的动态代理类的实例具有以下特点:
◆假定变量foo是一个动态代理类的实例,并且这个动态代理类实现了Foo接口,那么“foo instanceof Foo”的值为true。把变量foo强制转换为Foo类型是合法的:(Foo) foo //合法
◆每个动态代理类实例都和一个InvocationHandler实例关联。Proxy类的getInvocationHandler(Object proxy)静态方法返回与参数proxy指定的代理类实例所关联的InvocationHandler对象。
◆假定Foo接口有一个amethod()方法,那么当程序调用动态代理类实例foo的amethod()方法时,该方法会调用与它关联的InvocationHandler对象的invoke()方法。
2.2 InvocationHandler接口
InvocationHandler接口为方法调用接口,它声明了负责调用任意一个方法的invoke()方法:
Object invoke(Object proxy,Method method,Object[] args) throws Throwable
参数proxy指定动态代理类实例,参数method指定被调用的方法,参数args指定向被调用方法传递的参数,invoke()方法的返回值表示被调用方法的返回值。
2.3 举例
如下图所示,HelloServiceProxyFactory类(如例程10-15所示)的getHello- ServiceProxy()静态方法负责创建实现了HelloService接口的动态代理类的实例。
http://new.51cto.com/files/uploadimg/20070212/105519704.jpg
例 HelloServiceProxyFactory.java
package proxy;import java.lang.reflect.*;public class HelloServiceProxyFactory {/** 创建一个实现了HelloService接口的动态代理类的实例* 参数helloService引用被代理的HelloService实例*/public static HelloService getHelloServiceProxy(final HelloService helloService){//创建一个实现了InvocationHandler接口的匿名类的实例InvocationHandler handler=new InvocationHandler(){public Object invoke(Object proxy,Method method,Object args[])throws Exception{System.out.println("before calling "+method); //预处理Object result=method.invoke(helloService,args);//调用被代理的HelloService实例的方法System.out.println("after calling "+method); //事后处理return result;}};Class classType=HelloService.class;return (HelloService)Proxy.newProxyInstance(classType.getClassLoader(),new Class[]{classType},handler);}//# getHelloServiceProxy()}如下所示的Client2类先创建了一个HelloServiceImpl实例,然后创建了一个动态代理类实例helloServiceProxy,最后调用动态代理类实例的echo()方法。
package proxy;public class Client2{public static void main(String args[]){HelloService helloService=new HelloServiceImpl();HelloService helloServiceProxy=HelloServiceProxyFactory.getHelloServiceProxy(helloService);System.out.println("动态代理类的名字为"+helloServiceProxy.getClass().getName());System.out.println(helloServiceProxy.echo("Hello"));}}打印结果如下:动态代理类的名字为$Proxy0before calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)after calling public abstract java.lang.String proxy.HelloService.echo(java.lang.String)echo:Hello能在运行过程中根据接口的类型动态的调用实现该接口的类
动态代理的主要作用就是:实现了日志和业务的分开,也就是某个类只是要提供了某些业务,比如银行取款业务。
这个类实现了取款业务的同时也需要实现日志功能,如果不用动态代理的话,那么由此一来该类代码里面已经额外地添加了自己不该添加的日志功能能代码。所以我们就得使用动态代理把它的业务代码和日志功能代码分开。所以用到了动态代理概念,spring里面的AOP就是一个很好的例子。
动态代理主要是在运行时期创建一个实现一组特定接口的代理类,拦截对目标对象方法的调用..