代理模式是经典设计模式中的一种,属于对象创建型设计模式。
下面以一个模拟需求说明静态代理和动态代理:
RealSubject(真实角色):/** * 代理接口。处理给定名字的任务。 */ public interface Subject { /** * 执行给定名字的任务。 * @param taskName 任务名 */ public void dealTask(String taskName); }
ProxySubject(代理类)/** * 真实类,处理具体业务 * @author zhangliang * * 2016年4月5日 下午6:53:16 */ public class RealSubject implements Subject { @Override public void dealTask(String taskName) { // TODO Auto-generated method stub System.out.println("realSubject正在执行任务:"+taskName); } }
测试类:/** * 代理类,实现类代理接口 * @author zhangliang * 2016年4月5日 下午6:58:06 */ public class ProxySubject implements Subject { //代理类持有一个委托类的对象引用 private Subject delegate; public ProxySubject(Subject delegate) { this.delegate = delegate; } @Override public void dealTask(String taskName) { // TODO Auto-generated method stub System.out.println("do something before"); delegate.dealTask(taskName); System.out.println("do something after"); } }
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理模式的结构跟上面的静态代理模式稍微有所不同,多引入了一个InvocationHandler角色。
先解释一下InvocationHandler的作用:
在静态代理中,代理Proxy中的方法,都指定了调用了特定的realSubject中的对应的方法:
在上面的静态代理模式下,Proxy所做的事情,无非是调用在不同的request时,调用触发realSubject对应的方法;更抽象点看,Proxy所作的事情;在Java中 方法(Method)也是作为一个对象来看待了,动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:
在这种模式之中:代理Proxy 和RealSubject 应该实现相同的功能,这一点相当重要。(我这里说的功能,可以理解为某个类的public方法)
在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
a.一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。
b.还有比较隐晦的方式,就是通过继承。因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。
其中JDK中提供的创建动态代理的机制,是以a 这种思路设计的,而cglib 则是以b思路设计的。
JDK的动态代理创建机制----通过接口
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
1)java.lang.reflect.Proxy
这是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
2)java.lang.reflect.InvocationHandler
invoke(Object obj,Method method, Object[] args)。在实际使用时,第一个参数obj一般是指代理 类,method是被代理的方法,args为该方法的参数数组。 这个抽 象方法在代理类中动态实现。
具体步骤是:
a. 实现InvocationHandler接口创建自己的调用处理器
b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
/** * 动态代理类对应的调用处理程序类 */ public class SubjectInvocationHandler implements InvocationHandler { //代理类持有一个委托类的对象引用 private Object delegate; public SubjectInvocationHandler(Object delegate) { this.delegate = delegate; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy:"+proxy.getClass().getName()); System.out.println("method:"+method.getName()); System.out.println("args:"+args[0].getClass().getName()); System.out.println("Before invoke method..."); Object object=method.invoke(delegate, args);//普通的Java反射代码,通过反射执行某个类的某方法 System.out.println("After invoke method..."); return object; } }
后来看了下,补充如下动态代理的核心其实就是代理对象的生成,即Proxy.newProxyInstance(classLoader, proxyInterface, handler)。
源码如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.获取代理类class
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler. 获取待invocation handler参数的构造器
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,我们看下getProxyClass(loader, interfaces)方法用于获取代理类,它主要做了三件事情:在当前类加载器的缓存里搜索是否有代理类,没有则生成代理类并缓存在本地JVM里。清单三:查找代理类。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 代理的接口数量不能超过65535(没有这种变态吧)
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
return proxyClassCache.get(loader, interfaces);
}
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
具体的缓存逻辑这里暂不关心,在这个get方法里,我们看到了如下代码:
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法,主要生成并加载代码:
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
try {
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
此处分别是:生成代理类的字节码文件并保存到硬盘中(默认不保存到硬盘)
使用类加载器将字节码加载到内存中
ProxyGenerator是sun.misc包中的类,它没有开源,感兴趣的可以用反编译软件打开jre\lib\rt.jar。
再回顾下,由Proxy类的getProxyClass0()方法生成目标代理类,然后拿到该类的构造方法,最后通过反射的newInstance方法,产生代理类的实例对象。
使用Java动态代理机制的好处:
1、减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。 2、系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。
缺点:
代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,目前无法使用该机制。 那怎么办呢?幸好我们有cglib。“CGLIB(Code Generation Library),是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。”待整理。