代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
静态代理主要是通过继承来实现,若需要多个代理就会生成多个代理类,这样会导致类的急剧增多。动态代理可以解决静态代理的缺陷,可以对任意接口或方法生成任意的代理类,对被代理的对象和接口方法都能灵活的访问控制。
下面我们主要看看jdk中动态代理的实现方式:
Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:
(1). Interface InvocationHandler:该接口中仅定义了一个方法Object:
invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为方法的参数数组。
2).Proxy:该类即为动态代理类,其中主要包含以下内容:
Protected Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。
Static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用。
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
动态代理代码实现步骤:
1.创建一个handler类实现invocationHandler接口,实现invoke方法,handler类构造函数中包括目标对象的引用。
2.创建被代理的对象类和接口。
3.客户端调用类中通过调用newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)返回一个代理类实例,该实例调用目标对象的方法。
下面看一个实例:
目标对象接口和实现类:
package com.proxy;
public interface Hello {
void sayHello(String str);
}
package com.proxy;
public class HelloImpl implements Hello{
@Override
public void sayHello(String str) {
System.out.println("hello: " +str);
}
}
日志处理handler:
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Title: LogHandler.java
* @Package: com.proxy
* @Desc: 日志处理Handler
* @Copyright: AsiaInfo-Linkage
* @author: gaoyb
* @date: Mar 12, 2013 3:33:06 PM
* @Email: gaoyb3@asiainfo-linkage.com
*/
public class LogHandler implements InvocationHandler{
//定义目标对象类引用
private Object obj = null;
public LogHandler(Object realSubject) {
this.obj = realSubject;
}
/*
* 动态代理类$Proxy0在调用被代理类的sayHello()方法时,会调用自己的sayHello()方法,而自己的sayHello()方法里会调用super.h.invoke(this, , ),
* super.h 也就是父类构造函数中的InvocationHandler h,这里的h就是我们的LogHandler对象。
* protected Proxy(InvocationHandler h) {
this.h = h;
}
所以调用sayHello()的请求就会被转发到LogHandler的invoke方法中,该invoke方法中 m.invoke(obj, args);会去调用obj对象的m中的方法。
*/
@Override
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
System.out.println("proxy class:"+proxy.getClass().getName());
doBefore();
//这里可以插入其他动作处理
Object res = m.invoke(obj, args);
//这里可以插入其他动作处理
doAfter();
//这里可以插入其他动作处理
return res;
}
private void doBefore(){
System.out.println("before log: do something....");
}
private void doAfter(){
System.out.println("after log: do something....");
}
客户端调用:
package com.proxy;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String args[]){
HelloImpl impl = new HelloImpl();//被代理的对象
LogHandler handler = new LogHandler(impl);
/* 下面代码具体做了什么呢?。。。。
*
* impl.getClass().getClassLoader():获得被代理类的类加载器
* impl.getClass().getInterfaces():获得被代理类实现的所有接口(这里只有一个Hello接口)
* handler:就是用于执行除了被代理接口中方法之外的用户自定义的额外操作,它也是用户需要代理的最终目的。
*
* 第一步:通过为 Proxy类指定 ClassLoader对象和一组interface来创建动态代理类$Proxy0:
* Proxy.getProxyClass(ClassLoader loader, Class<?>... interfaces) 根据上面两个参数生成代理类$Proxy0的Class对象。
* 同时这个动态生成的$Proxy0类实现了要代理类的实现的所有接口,并继承了java.lang.reflect.Proxy类($Proxy0 extends Proxy implements Hello)。
*
* 第二步:通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型InvocationHandler h:
* 实例化这个动态生成的$Proxy0类的一个实例,实例化代理类的构造函数为Proxy(InvocationHandler h),
* 也就是说要实例化这个动态生成的$Proxy0类,必须给它一个InvocationHandler参数,也就是我们自己实现的用来在被代理类方法执行前后做额外工作的handler类(LogHandler)
* Constructor cons = cl.getConstructor(constructorParams);
* return (Object) cons.newInstance(new Object[] { h });
*
* 第三步:将这个$Proxy0类强制转型成被代理类接口类型(Hello),调用被代理类的方法sayHello()。
*/
Hello proxy = (Hello)Proxy.newProxyInstance(impl.getClass().getClassLoader(),
impl.getClass().getInterfaces(), handler);
proxy.sayHello("dynamic proxy....");
}
}
结果:
proxy class:$Proxy0
before log: do something....
hello: dynamic proxy....
after log: do something....
动态代理主要在日志、事务、权限、拦截器等切面操作中上使用,在spring Aop中有比较好的使用,大家不妨去研究研究。