代理模式是一个经常被各种框架使用的模式,比如Spring AOP、Mybatis中就经常用到,当一个类访问另外一个类困难时,可通过一个代理类来间接访问,在Java中,为了保证程序的简单性,代理类与目标类需要实现相同的接口。也就是说代理模式起着一个中转站的作用,如图:
用一个简单的例子来描述代理模式:
public interface Catch{
void print();
}
public class Cat implements Catch{
@Override
public void print() {
System.out.println("Cat 开始抓老鼠");
}
}
public class ProxyClassTest implements Catch{
private Catch aCatch;
public ProxyClassTest(Catch aCatch){
this.aCatch=aCatch;
}
@Override
public void print() {
System.out.println("dog请求cat抓老鼠");
aCatch.print();
}
}
public class Dog {
public static void main(String args[]) {
ProxyClassTest test=new ProxyClassTest(new Cat());
test.print();
}
}
从上述代码可以看出,当dog向代理类提出请求抓老鼠后,原本请求类(dog)便可以不管了,而代理类则通过自己的方式找到可以实现dog的请求的类或方法,然后执行完成dog的请求。代理模式的好处也可以得出了,就是原本请求类是可以不关注它的请求行为是如何具体的实现的,减少了代码间的耦合度。
WeakCache类
在讨论Proxy类之前,不得不先了解这个类,这个类是用来干嘛的呢?从其英文意义上来看,它是弱缓存类,也就是说它是一个起着缓存作用的类,它用来存贮软引用类型的实例,从该类在Proxy类中的实例化可以看出:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
从上述代码中可以看出,WeakCache是缓存类加载器以及该加载器加载的委托类的。其中KeyFactory类用来生产Key的,生成因不同数量的类加载器采取不同的生成策略来对应生成的Key对象,而其实现如下:
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
private static final class Key1 extends WeakReference<Class<?>> {
private final int hash;
Key1(Class<?> intf) {
super(intf);
this.hash = intf.hashCode();
}
......
}
在这里列出了Key1类的实现,从其源代码中看出,该类继承了WeakReference类,也就是弱引用类,而每个Key类具体用来干嘛的呢?其实就是作为键的生成策略存在,保证其键的唯一性,而KeyFactory工厂类根据类加载器数量的不同采取键的不同生成策略。后者ProxyClassFactory工厂类则负责验证其里面的委托接口是否已经加载过、是否是接口、验证其访问权限、验证包的信息与判定,最后通过defineClass方法实现类(广义上接口也是类)的加载,也就是把.class文件转变成对应接口的Class类对象:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
......
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
......
//最后调用了native本地方法实现其Class对象的获取
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
......
}
}
也就是说,WeakCache类的实质作用是缓存实现了代理目标接口的类的信息以及对这些类的一些操作,以及这些类的类加载器的。其中的重要的方法是get方法,该方法是来获取WeakCache缓存中与委托类的Class类对象的。这句话什么意思呢?就拿上文的代理模式的例子来说,这个代理目标接口实现的类就是Cat类,也就是委托类,而代理类是ProxyClassTest类。WeakCache重要的方法是,get方法,该方法实现了缓存的机制,其源码以及注释如下:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
//删除过期的缓存
expungeStaleEntries();
//创建CatchKey对象,Key不为空
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 懒惰地为特定的CaseKeKE安装第二级值映射
//根据CatchMap获取ConcurrentMap对象
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
//如果没有则创建一个ConcurrentMap对象
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//利用KeyFactory获取Key对象
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//利用Key对象取出Supplier,Supplier是一个接口
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
//不为空,则直接取出
if (supplier != null) {
// 从supplier接口的实现类获取Class类对象
V value = supplier.get();
if (value != null) {
return value;
}
}
//如果supplier为空,进行缓存,Factory是supplier接口的实现类
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
//赋值factory
supplier = factory;
}
} else {
//假如不为空,且factory中value为空,则替换factory
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
//继续尝试获取接口实现类的supplier类对象
supplier = valuesMap.get(subKey);
}
}
}
}
get方法利用ConcurrentMap类来进行缓存,里面存贮Key对象以及Factory对象,Factory类用来存贮缓存的信息的,其实现了Supplier接口,其get方法里面调用了ProxyClassFactory类的apply方法来返回Class类对象,而且get方法是一个同步的方法,也表明WeakCache缓存是线程安全的。
Proxy类
回到我们的主题,也就是Proxy类,这是Java官方提供的一个代理类,用来实现代理模式,而其提供的是动态的代理,而上述猫抓老鼠的例子是静态的代理,两者之间有什么区别呢?静态代理的代理类虽然减少了代码间的耦合度,但是有如下的缺点:
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,没有对多种对象的代理。
而动态代理则消除掉了这些弊端,Java为动态代理提供了一个接口,该接口是代理类需要实现的:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
proxy是代理类的对象,method是委托者被代理的方法的对象,args指代理类实例上方法调用的参数。
Proxy类中主要的方法是检查权限类以及创建代理类的实例类,因为Proxy没有公共的构造方法,所以只能通过newProxyInstance方法来创建代理类的实例,这个代理类的实例并不是Proxy类的实例,这个需要注意:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//InvocationHandler接口实现类不为空
Objects.requireNonNull(h);
//克隆接口
final Class<?>[] intfs = interfaces.clone();
//进行类加载器的权限判断
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//获取WeakCache类缓存的Class类对象
Class<?> cl = getProxyClass0(loader, intfs);
//代理权限检查
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//修饰符权限判断
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
//更改修饰符权限
cons.setAccessible(true);
return null;
}
});
}
//创建对应InvocationHandler实现类的实例
return cons.newInstance(new Object[]{h});
......
}
通过一个例子来加深理解:
public interface Catch{
void print(String mouse);
}
public class Cat implements Catch{
@Override
public void print(String mouse) {
System.out.println("Cat 开始抓"+mouse);
}
}
public class ProxyClassTest implements InvocationHandler{
private Object oclass;
public Object ProxyClassInstance(Object oclass){
this.oclass=oclass;
return Proxy.newProxyInstance(oclass.getClass().getClassLoader(),oclass.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(oclass,args);
return null;
}
}
public class Test {
public static void main(String args[]) {
Catch test= (Catch) new ProxyClassTest().ProxyClassInstance(new Cat());
test.print("老鼠");
}
}
//打印:Cat 开始抓老鼠
利用Java实现动态代理类,需要实现InvocationHandler接口,重写里面的invoke方法,并由Proxy类newProxyInstance方法来创建委托类的实例。从代码中看出,代理类并不需要依赖委托类,消除了静态代理的弊端。