一、代理模式
代理模式会给某个对象提供一个代理对象,并由代理对象控制原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。代理模式一般会有三个角色:
抽象接口:代理角色和真实角色对外提供的公共方法,一般为一个接口。
真实角色:需要实现抽象接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。真正的业务逻辑在此。
代理角色:需要实现抽象接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。
一个简单的例子就是,业务中需要统计某些方法的执行时间。由于这是和业务逻辑不相干的功能,我们不想把它放在真实角色中,而是通过代理角色在调用执行真实角色方法的前后插入代码来统计执行时间。那么就可以把被统计的方法抽取到接口中:
public interface IFunction {
void action();
}
public class RealSubject implements ITime {
@Override
public void action() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("RealSubject action() execute!");
}
}
public class ProxySubject implements IFunction {
private IFunction iFunction;
public ProxySubject(IFunction iTime) {
this.iFunction = iTime;
}
@Override
public void action() {
// 计算时间的操作是对真实对象方法的增强
long startTime = System.currentTimeMillis();
// 实际执行还是执行真实对象的方法
iFunction.action();
long endTime = System.currentTimeMillis();
System.out.println("Run time:" + (endTime - startTime));
}
}
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.action();
}
}
输出结果为:
RealSubject action() execute!
Run time:500
可以看到代理起到隔离调用者与真实对象的作用,不会使二者产生直接联系。
上边这种实现模式是静态代理,静态代理类需要持有真实对象的引用,一个代理可以代理一个或多个被代理对象。
二、静态代理的弊端
一对一的问题是时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差;而一对多代理对象会出现扩展能力差的问题(在一对多时会出现100个真实对象共用一个代理,需要1个代理和100个真实对象同时产生关系,从而因为关系复杂而很难扩展)。
比如消费者 Consumer 想要买房,那么他就要去找房产中介 HouseAgent,二者可以抽象出 IHouse 接口:
public interface IHouse {
void buyHouse();
}
public class Consumer implements IHouse {
@Override
public void buyHouse() {
System.out.println("Consumer buyHouse()");
}
}
public class HouseAgent implements IHouse {
private IHouse iHouse;
public HouseAgent(IHouse iHouse) {
this.iHouse = iHouse;
}
@Override
public void buyHouse() {
before();
iHouse.buyHouse();
after();
}
private void before() {
System.out.println("Before buying house");
}
private void after() {
System.out.println("After buying house");
}
}
public class Test {
public static void main(String[] args) {
HouseAgent houseAgent = new HouseAgent(new Consumer());
houseAgent.buyHouse();
}
}
接下来 Consumer 又想买车,那么他就又要去找一个买车的代理 CarAgent(根据软件设计的单一职责原则和开闭原则,不应该在 IHouse 接口中增加买车的方法 buyCar(),而是新增 ICar 接口),这样的话就新增如下代码:
public interface ICar {
void buyCar();
}
public class CarAgent implements ICar {
private ICar iCar;
public CarAgent(ICar iCar) {
this.iCar = iCar;
}
@Override
public void buyCar() {
before();
iCar.buyCar();
after();
}
private void before() {
System.out.println("Before buying car");
}
private void after() {
System.out.println("After buying car");
}
}
public class Consumer implements IHouse, ICar {
@Override
public void buyHouse() {
System.out.println("Consumer buyHouse()");
}
@Override
public void buyCar() {
System.out.println("Consumer buyCar()");
}
}
public class Test {
public static void main(String[] args) {
Consumer consumer = new Consumer();
HouseAgent houseAgent = new HouseAgent(consumer);
houseAgent.buyHouse();
CarAgent carAgent = new CarAgent(consumer);
carAgent.buyCar();
}
}
在一个代理只维护一个接口的情况下,每新增一个接口就要增加一个代理类,接口数量增多时也会造成代理类数量增多。
三、JDK 动态代理
JDK 动态代理可以有效的弥补静态代理的不足,可以通过一个代理类实现全部的代理功能,但是 JDK 动态代理只能代理接口,不能代理一个类。
动态代理使用方式为:
public class Test {
public static void main(String[] args) {
final Consumer consumer = new Consumer();
// 生成动态代理对象
Object object = Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{IHouse.class, ICar.class},
new InvocationHandler() {
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
return method.invoke(consumer, args);
}
});
// 代理对象转换成真实对象实例并执行业务方法
IHouse iHouse = (IHouse) object;
iHouse.buyHouse();
}
}
使用 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler) 可以创建一个动态代理对象,需要传入三个参数:
- loader:加载生成的代理类到 JVM 中的 ClassLoader。
- interfaces:动态代理类要代理(实现)的接口。
- invocationHandler:一个接口对象,动态代理对象在执行方法时,会回调到接口中唯一的方法 invoke()。
invoke(Object object, Method method, Object[] args) 有三个参数,它们的含义分别是:
- object:动态代理对象,其实就是通过 Proxy.newProxyInstance() 生成的那个代理对象。
- method:动态代理调用的方法对象。
- args:method 方法的参数。
使用动态代理时注意不要犯这种错误:
public static void main(String[] args) {
Object object = Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{IHouse.class, ICar.class},
new InvocationHandler() {
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
System.out.println(object);
return null;
}
});
IHouse iHouse = (IHouse) object;
iHouse.buyHouse();
}
这样会造成堆溢出。原因是动态代理对象执行了方法之后回调 invoke(),在 invoke() 中输出时相当于调用了 object.toString() 方法,因为只要调用动态代理的方法就会先回调到 invoke(),这样就形成了死循环似的调用,造成堆溢出。
再来看下 Proxy 的源码:
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 ....
}
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");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
先克隆一份 interfaces,如果 <loader,interfaces> 所对应的 Class 对象 已经被缓存,那么就拿缓存数据的拷贝,否则通过 ProxyClassFactory 创建出一个 Class。拿到 Class 对象后,通过构造方法和传进来的 InvocationHandler 创建出动态代理对象。
那么动态代理的 Class 是如何生成的呢?它与一般的 Class 生成方式不同:
- 一般的 Class 都是由 Java 源文件经过编译先在硬盘上生成字节码 .class 文件,再通过 ClassLoader 进行类加载,加载进内存称为 Class 对象。
- 动态代理的 Class 不会在硬盘上生成,而是直接在内存中产生。
生成动态代理 Class 类及其对象的正是前面提到过的,Proxy 的静态内部类 ProxyClassFactory,其代码如下:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use the default package.
proxyPkg = "";
}
{
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
ProxyClassFactory 实现了函数式接口 BiFunction<T, U, R>,其中泛型 T,U 是其接口方法 apply(T,U) 的参数类型,R 是 apply(T,U) 的返回值类型。在这里其实就是传入 ClassLoader 和接口的 Class 数组,得到动态代理的 Class 对象。
ProxyClassFactory 类中定义的两个常量 proxyClassNamePrefix 和 nextUniqueNumber 是用来定义生成的动态代理类文件的名称的,从倒数第二行代码来看,命名规则为:代理所在的包名 + proxyClassNamePrefix + nextUniqueNumber,即形如 com.frank.agent$0。
apply() 方法内,先对我们传入的代理接口进行检查。先通过接口名和 loader 反射拿到 interfaceClass 对象,并检查这个 interfaceClass 对象是否和提供名字的接口 intf 相同;再看 interfaceClass 是否是一个接口;最后检查 interfaceClass 是否已经被添加到 interfaceSet 中了(interfaceSet 是一个 IdentityHashMap,即不以 equals() 而是以 == 的结果作为比较标准)。
接口筛选完毕后,开始构造要生成的动态代理类的包名,这个包名由 interfaces 中接口的访问控制符决定:
- 如果所有接口都由 public 修饰,那么就使用默认的包名"";
- 如果存在非 public 属性的接口,那么所有非 public 的接口包名必须相同,动态代理类也就使用这个包名。
最后就是生成代理类了。API 29 不再使用 ProxyGenerator 而是直接生成。先获取到所有接口中的所有方法,然后再收集所有方法声明的异常,把它们转换成数组传递给最终生成动态代理 Class 对象的 generateProxy()。
我们仍然可以使用 ProxyGenerator 生成动态代理的 .class 文件:
private static void proxy() {
String name = IHouse.class.getName() + "$Proxy0";
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{IHouse.class});
File file = new File(name+".class");
try {
if (!file.exists() && file.createNewFile()) {
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
这样就会生成一个名字为 com.frank.agent2.IHouse$Proxy0.class 的 .class 文件:
public final class IHouse$Proxy0 extends Proxy implements IHouse {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public IHouse$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buyHouse() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.frank.agent2.IHouse").getMethod("buyHouse");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
动态代理类 IHouse$Proxy0 继承 Proxy 实现了 IHouse 接口,有4个 Method 类型成员,从 m0~m3 依次对应 hashCode()、equals()、toString() 和 IHouse 接口中的 buyHouse()。
当动态代理对象调用 buyHouse() 时,会执行到 super.h.invoke(),就是调用创建动态代理对象时创建的那个 InvocationHandler 的 invoke()。invoke() 的参数依次传递的是动态代理对象、动态代理对象调用方法的 Method 对象以及调用方法的参数(方法没参数就传 null)。这与前边介绍的 invoke() 方法参数含义是一致的。