一、概述
1、什么是代理模式
先来看看代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗地讲,代理模式就是提供一个对象相当于中介者,他可以帮我们调用目标的功能,同时我们可以在使用目标功能的同时扩展其他功能,而不需要直接改到目标类。
2、为什么要使用代理模式
通过代理模式帮我们调用目标类,那为什么我们不直接调用目标类,而要额外弄一个类出来间接调用,这不是闲得慌吗?存在即是道理,既然存在这样一种模式,自然有它的好处,下面来看下代理模式的好处:
- 中介隔离作用:在某些情况下,如果某个类并不想被直接调用,那么通过创建代理类,就 可以避免目标类被直接调用,这样可以保护目标类不会被曝光
- 可以扩展功能而不用影响目标类:假设有一个核心类提供了一些核心业务,而我们希望使用这个功能时上报日志,或者某个业务要先进行某些处理才调用,那这样如果直接改核心类,就会给核心功能加上了一些无关的内容,变得这个功能与业务有很大关联,难以复用。这时我们使用代理类的话,就可以在代理类中添加自己的业务逻辑,而不需要破坏原有的核心功能。
3、代理模式的种类
代理模式可以分为静态代理和动态代理,下面会进行详解。
二、静态代理
1、概念
静态代理指是由程序员手动编写或者自动生成的代理类,在程序编译的时候就可以生成对应的class文件,在运行时可以直接使用。
2、静态代理的实现
静态代理实现起来比较简单,只需要与目标类实现相同的接口,然后持有目标类对象,就可以对目标类进行扩展。下面看例子:
首先创建一个接口ISayHello.java:
public interface ISayHello {
void sayHello();
}目标类User.java:
public class User implements ISayHello {
@Override
public void sayHello() {
System.out.println("hello");
}
}静态代理类StaticProxy.java
public class StaticProxy implements ISayHello {
/** 持有目标类的对象,实现间接调用 */
private User target;
public StaticProxy(User target) {
this.target = target;
}
@Override
public void sayHello() {
System.out.println("before hello");
if (target != null) {
target.sayHello(); // 实际调用目标功能
}
System.out.println("after hello");
}
}静态代理的使用
public class Main {
public static void main(String[] args) {
StaticProxy proxy = new StaticProxy(new User());
proxy.sayHello();
}
}输出结果
before hello
hello
after hello3、静态代理应用场景
对一些第三方框架进行扩展代理、自动生成的代码,例如Android中的AIDL等。
三、动态代理
1、概述
与静态代理不同,动态代理不需要提前创建对象,只需要提供一个动态创建器,程序会在运行时候动态生成对应的代理类。
2、动态代理的好处
动态代理的好处主要就是可以减少对接口的依赖,降低耦合度,减少冗余代码,方便对代理方法的统一管理。我们可以从以下几点体现出来:
- 减少方法的实现:如果一个接口有1000个方法,而我们只想代理其中某几个方法,使用静态代理需要代理类实现所有的方法,如果接口增加一个方法,相应的代理类也要增加实现,代码会变得十分冗余。但如果使用动态代理,就可以简单地只对某几个方法进行处理,不需要加入大量无用的代码。
- 可以服务多个目标类:因为静态代理在编译时期就已经生成对应的类,所以对于每一个代理类,都已经固定了要服务的目标类,这就导致了如果需要多个目标类,就要编写对应的代理类,导致类会很多。而使用动态代理,则可以一个动态处理器为不同的目标类作服务,从而减少类的编写。
3、动态代理的实现
动态代理的实现主要使用了InvocationHandler,下面看例子:
接口和目标类均使用静态代理的例子,现在实现动态代理类DynamicProxyHandler.java:
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
if (target == null) {
return null;
}
// 通过newProxyInstance方法创建代理类
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sayHello")) {
// 如果是需要代理的方法则进行处理
System.out.println("before hello");
Object invoke = method.invoke(target, args); // 实际调用目标功能
System.out.println("after hello");
return invoke;
}
return method.invoke(target, args);
}
}动态代理的使用
public class Main {
public static void main(String[] args) {
DynamicProxyHandler proxyHandler = new DynamicProxyHandler();
ISayHello proxy = (ISayHello) proxyHandler.bind(new User());
proxy.sayHello();
}
}输出结果
before hello
hello
after hello4、动态代理的原理
从上面的例子可以看到,动态代理生存代理类主要是依靠Proxy.newProxyInstance这一个静态方法,因此,下面就看看这一个方法做了什么
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 通过缓存获取代理类,没有则生成代理类
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;
}
});
}
// 调用构造函数构造代理类的实类
return cons.newInstance(new Object[]{h});
} // ...省略一堆catch
}上面这个方法抛开一些检查的逻辑,核心方法就是getProxyClass0(loader, intfs)来生成我们需要的代理类,后后通过构造方法传入InvocationHandler来创建实例,接下来看一下getProxyClass0(loader, intfs)的实现
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
// 检查接口的方法数
throw new IllegalArgumentException("interface limit exceeded");
}
// 从缓存中获取代理类,如果没有则会进行生成
// proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
return proxyClassCache.get(loader, interfaces);
}这个方法只有几行代码,首先检查一下接口的数量,然后从缓存中获取类,如果找不到的话会使用ProxyClassFactory来生成,下面看看是如何生成的
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// 代理类的名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 代理类的序号,以此确定唯一的代理类
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) {
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
// 判断是否可以由指定的类加载
}
if (!interfaceClass.isInterface()) {
// 判断是否一个接口
}
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
// 判断是否有重复的接口
}
}
String proxyPkg = null;
// 生成代理类的访问标志, 默认是public final的
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
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) {
// 公有的接口,则默认包名com.sun.proxy
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
// 生成代理类序号
long num = nextUniqueNumber.getAndIncrement();
// 代理类的名字 包名+前缀+序号,例如com.sun.proxy$Proxy1
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 使用generateProxyClass生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 根据二进制文件生成相应的Class实例
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}可以看到,这个类通过 apply来生成我们所需的代理类,这个类主要做了几件事:
- 进行一些合法性判断,不合法则抛出异常
- 生成包名+前缀+序号的代理类,例如com.sun.proxy$Proxy1
- 如果接口是公有的,则默认包名是com.sun.proxy;否则和接口的包名相同
- 通过ProxyGenerator.generateProxyClass来生成字节码
- 同过defineClass0来生成class实
5、动态代理应用场景
例如Retrofit网络加载框架、Spring中AOP的实现等。
四、总结
- 代理模式可以在不改变目标类的情况下对功能进行扩展
- 静态代理编译时便生成class文件,动态代理运行时通过反射生成对应的class文件
- 静态代理需要实现接口的所有方法,一个代理类能服务的目标类有限
- 动态代理可以只对某些方法进行处理,一个代理处理器可以服务多个目标类
- 对于一些接口比较简单、或者自动生成的通用性代码,可以选择使用静态代理;对于一些庞大的接口,频繁地需要改动接口,你已经觉得改得很烦,可以选择使用动态代理。