开篇复习一下代理模式,什么是代理模式?菜鸟教程上写的是,在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
举个最简单的例子,写个静态代理的demo
// 抽象角色:开发者
public interface ICoder {
void code();
}
// 代理角色
public class Person implements ICoder {
// 名称
private String name;
public Person(String name) {
this.name = name;
}
@Override
public void code() {
System.out.println(name + " write code");
}
}
// 真实角色:开发者代理类
public class CoderAgent implements ICoder {
public ICoder coder;
@Override
public void code() {
coder.code();
}
}
public void main() {
Person person = new Person("vod");
CoderAgent agent = new CoderAgent();
agent.coder = person;
agent.code();
}
代理模式的demo写完了,但是总觉得哪里怪怪的…
我明明可以直接调用person.code
,为什么还要再增加一层代理。
这个嘛,大部分时候代理模式会增加一些前置或后置逻辑,还有判空等操作。新的代理类如下
public class CoderAgent implements ICoder {
public ICoder coder;
// 前置逻辑
private void before() {
System.out.println("coder is design");
}
// 后置逻辑
private void after() {
System.out.println("coder is testing");
}
@Override
public void code() {
// 判空处理
if (coder != null) {
before();
coder.code();
after();
}
}
}
这时候,我们发现,以上代理模式的实现都是静态的代码实现,如果Person新实现一个接口叫 IEmployee
,这时候代理应该怎么处理?
public interface IEmployee {
void salary(int moneyCent);
}
一般来说,一个代理类代理一个接口,当代理对象实现多个接口的情况下,要么有多个代理类的实现,要么有个臃肿且难以复用的代理类出现,而这两情况无疑是我们不愿意看到的。
那么,这时候我们就会用到动态代理, Proxy
注意,动态代理类路径为
java.lang.reflect.Proxy
,不要引net
包中的Proxy
源码先放在这里
/**
* @param loader 类加载器
* @param interfaces 代理接口
* @param h 代理逻辑处理器
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
// 生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
try {
// 获取构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 通过构造方法生成代理类的对象
if (!Modifier.isPublic(cl.getModifiers())) {
cons.setAccessible(true);
}
// 将代理类的对象返回出去
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
// catch 省略
}
}
源码放完了,那就再来一个demo
public void main2() {
// 生成代理角色
Person person = new Person("vod");
// 生成代理类,入参分别为类加载器、需要代理的接口、处理器
Object obj = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{ICoder.class, IEmployee.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 当代理类的方法被调用时,就会触发一下逻辑
if (person != null) {
return method.invoke(person, args);
}
return null;
}
});
// 写写代码
((ICoder)obj).code();
// 发发工资
((IEmployee)obj).salary(50000000);
}
这时候,我们发现,动态代理生成的代理类,它所实现的接口完全可以通过在运行时,通过改变调用newProxyInstance
的接口数组的参数,去动态改变,这让代理类更灵活了。
那么问题又来了,它是怎么实现的?
// 生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
看方法中,这句代码返回的代理类,咱们深挖下去
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
// 接口数大于六万五
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
从这里可以看出,每次生成的代理类,都会缓存在 proxyClassCache
这个缓存池中,这个缓存池的get方法源码太长不看,简单说就是有就取,没有就通过工厂类生成,而这个工厂类就是new缓存池时候的入参,ProxyClassFactory
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 其余方法参数略
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 中间略
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
其实,这时候android的jar包和java的有一点区别,java的源码是
public Class<?> apply(ClassLoader var1, Class<?>[] var2) {
// 前,略
byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);
try {
return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
} catch (ClassFormatError var14) {
throw new IllegalArgumentException(var14.toString());
}
}
}
但是这并不影响重点,然而重点的方法 generateProxyClass
…
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
好了native层就不看了,看了也看不懂,看懂了也不会用,会用了也请告诉我一下哈哈。
至于这个方法生成的类具体是什么样子,我在其他博客上找了一个demo
1 public class Proxy0 extends Proxy implements UserDao {
2
3 //第一步, 生成构造器
4 protected Proxy0(InvocationHandler h) {
5 super(h);
6 }
7
8 //第二步, 生成静态域
9 private static Method m1; //hashCode方法
10 private static Method m2; //equals方法
11 private static Method m3; //toString方法
12 private static Method m4; //...
13
14 //第三步, 生成代理方法
15 @Override
16 public int hashCode() {
17 try {
18 return (int) h.invoke(this, m1, null);
19 } catch (Throwable e) {
20 throw new UndeclaredThrowableException(e);
21 }
22 }
23
24 @Override
25 public boolean equals(Object obj) {
26 try {
27 Object[] args = new Object[] {obj};
28 return (boolean) h.invoke(this, m2, args);
29 } catch (Throwable e) {
30 throw new UndeclaredThrowableException(e);
31 }
32 }
33
34 @Override
35 public String toString() {
36 try {
37 return (String) h.invoke(this, m3, null);
38 } catch (Throwable e) {
39 throw new UndeclaredThrowableException(e);
40 }
41 }
42
43 @Override
44 public void save(User user) {
45 try {
46 //构造参数数组, 如果有多个参数往后面添加就行了
47 Object[] args = new Object[] {user};
48 h.invoke(this, m4, args);
49 } catch (Throwable e) {
50 throw new UndeclaredThrowableException(e);
51 }
52 }
53
54 //第四步, 生成静态初始化方法
55 static {
56 try {
57 Class c1 = Class.forName(Object.class.getName());
58 Class c2 = Class.forName(UserDao.class.getName());
59 m1 = c1.getMethod("hashCode", null);
60 m2 = c1.getMethod("equals", new Class[]{Object.class});
61 m3 = c1.getMethod("toString", null);
62 m4 = c2.getMethod("save", new Class[]{User.class});
63 //...
64 } catch (Exception e) {
65 e.printStackTrace();
66 }
67 }
68
69 }
如果感觉以上代码太长不看,一句话概括就是
生成的类继承所有接口与Object的方法,实现方式统一为调用动态代理的入参
InvocationHandler
的 invoke 方法进行处理
OK,那么动态代理与代理模式到这就讲完了,祝大家
0 warnning, 0 error, 0 bug