前言
代理模式为对象提供一种代理以控制对这个对象的访问,而Java动态代理就是代理模式的一种实现,其优势是实现无侵入式的代码扩展,也就是方法的增强,让我们可以在不用修改源码的情况下,增强一些方法,比如在方法的前后做一些日志记录等等。
测试代码
/**
* @Description: 代理的对象必须实现接口
*/
interface Subject{
void rent();
}
/**
* @Description: 需要代理的对象,有一个出租房屋的方法
*/
public class RealSubject implements Subject {
@Override
public void rent() {
System.out.println("I want to rent my house");
}
}
/**
* @Description: 代理处理类,实现InvocationHandler接口,invoke为增强方法
*/
public class DynamicProxy implements InvocationHandler {
private Object subject; // 这个就是要代理的真实对象
public DynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("before rent house"); // 前置记录
System.out.println("Method: " + method); // 记录下执行的方法
method.invoke(subject, args); // 执行原有的方法
System.out.println("after rent house"); // 后置记录
return null;
}
}
/**
* @Description: 测试类
*/
public class JDKProxyTest {
public static void main(String[] args) {
Subject realSubject = new RealSubject(); // 需代理的真实对象
InvocationHandler handler = new DynamicProxy(realSubject); // 传进处理类
/*
* 通过Proxy的newProxyInstance方法来创建代理对象,需传入三个参数:
* 第一个参数 handler.getClass().getClassLoader(),获取处理类的类加载器;
* 第二个参数 realSubject.getClass().getInterfaces(),获取需代理对象的接口;
* 第三个参数 handler,处理类;
*/
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
handler);
System.out.println(subject.getClass().getSimpleName()); // L1
subject.rent();
}
}
执行结果:
$Proxy0 // L1处打印的代理对象的类名
before rent house // 前置记录
Method: public abstract void com.demo.srpingboot.common.JDKproxy.Subject.rent()
I want to rent my house // rent方法执行
after rent house // 后置记录
获取代理对象
代理对象在Proxy的newProxyInstance方法中获取,先着重理解getProxyClass0()方法,该方法生成代理类。
public class Proxy implements java.io.Serializable {
/** 缓存机制,传入KeyFactory和ProxyClassFactory */
private static final WeakCache[], Class>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h); // 非空
final Class>[] intfs = interfaces.clone(); // 先clone被代理对象的接口
...
/*
* 查找或生成指定的代理类
*/
Class> cl = getProxyClass0(loader, intfs);
try {
...
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h}); // L1 反射获取代理对象
} catch (...) {
... // 异常处理
}
}
private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {
if (interfaces.length > 65535) { // 被代理的对象实现的接口不能超过65535
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces); // 从缓存中获取代理对象
}
}
关于代码中会涉及到的两个函数式接口。
jdk底层使用了缓存以减少重复代理类的构建(二级缓存)。
- 首先看一下proxyClassCache,作为Proxy类私有静态域,在Proxy类被加载的时候生成一个全局唯一的WeakCache对象,并传入KeyFactory和ProxyClassFactory类型的对象,由这两个工厂生成二级缓存的key和对应的value。
- 在从缓存中取值之前,下列代码L1处先清除过期缓存,从清除过期缓存的过程中可以猜测到一些二级缓存的实现机制,包括一级缓存的key是一个CacheKey类型的对象,且这个对象不是一个”真正的对象“,而是一个弱引用对象,指向某个对象(而我们在L2处CacheKey的valueOf方法中得知,这个指向的对象是loader,也就是类加载器对象),另外WeakCache的reverseMap属性的key为二级缓存的value等。
- L2处将类加载器对象包装成弱引用对象,传入key为null时,将一个单例的Object对象作为一级缓存的key。 到这里查找缓存map的一级索引已经有了。
- L3处根据传入类加载器对象和接口数组,通过subKeyFactory(也就是传入KeyFactory对象)构建二级缓存的key。接着用这个key通过轮询和CAS的方式去获取或者生成二级缓存的value。
- 值得注意的是在代码L4处,创建了Factory这个工厂,这个工厂的生成二级缓存value最外层的工厂。并且在L5处直接将其作为二级缓存的value放入map中。也就是说此时二级缓存map的value值有可能是Factory对象,也有可能是CacheValue对象。
final class WeakCache {
/** 引用队列 */
private final ReferenceQueue refQueue = new ReferenceQueue<>();
/** 缓存的底层实现, key为一级缓存, value为二级缓存。这里为了支持null, map的key类型设置为Object,这里支持null的意思是当没有传入类加载器时,默认将创建好的Object对象做为key,这一点下面会讲到 */
private final ConcurrentMap
- 接着来看一下这个Factory,值得注意的是构建Factory时传入了四个参数,其中也包括二级缓存map和二级缓存的key,传入这个二级缓存map的原因是当然是为了生成真正的二级缓存value,CacheValue对象并替换掉原来的Factory对象。这里的双重校验,是解决延迟初始化的竞态条件"检查-初始化"。第一次生成代理类的时候存在一个延迟初始化的竞态条件。这里为了保证线程安全,第一次生成代理类时需要线程同步以保证线程安全,后续获取代理类则不需要,以减轻并发压力,因此引入了生成二级缓存时引入了Factory类。
private final class Factory对象 implements Supplier {
private final K key;
private final P parameter;
private final Object subKey;
private final ConcurrentMap> valuesMap;
Factory(K key, P parameter, Object subKey,
ConcurrentMap> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
@Override
public synchronized V get() {
Supplier supplier = valuesMap.get(subKey); // 重新检查
if (supplier != this) {
// 这里判断了一下获取到的Supplier对象是否为Factory类型,两种情况会不是Factory类型,
// 第一种是在等待时,对应的二级缓存的value已经被替换为CacheValue对象,
// 第二种是由于生成代理类失败被从二级缓存中移除了。
return null;
}
// 还是Factory对象
V value = null;
try {
// 委托valueFactory去生成代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { //如果生成代理类失败, 就将这个二级缓存删除
valuesMap.remove(subKey, this);
}
}
assert value != null;
// 将代理类包装成一个CacheValue对象,且这个对象也是个弱引用
CacheValue cacheValue = new CacheValue<>(value);
// 将cacheValue放入reverseMap, 并对它进行标记
reverseMap.put(cacheValue, Boolean.TRUE);
// 将包装后的cacheValue放入二级缓存中, 这个操作必须成功, 否则就报错
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// 顺利到这就返回由代理类包装成的CacheValue弱引用对象
return value;
}
}
- 最后来看一下这个ProxyClassFactory对象(valueFactory)如何生成代理类,也是通过类加载器对象和接口数组来生成。至此代理类已经被创建或者被找到,那么在Proxy代码L1处最后通过反射将代理对象创建即可。
private static final class ProxyClassFactory
implements BiFunction[], Class>>
{
// 代理类名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 代理类标记
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class> intf : interfaces) {
Class> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
... // 异常处理
}
String proxyPkg = null;
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();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 生成指定的代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
清除过期缓存
/**
* 清除过期缓存
*/
private void expungeStaleEntries() {
CacheKey cacheKey;
/*
* 循环引用队列,下面介绍弱引用时提到弱引用指向的对象被回收时,会放进传入的引用队列
*/
while ((cacheKey = (CacheKey)refQueue.poll()) != null) {
/* 直接移除,因为在引用队列中的引用对象表示了该引用指向的被引用对象已经GC回收,这里注意
被引用对象指向的对象和引用对象的区别 */
cacheKey.expungeFrom(map, reverseMap);
}
}
/**
* CacheKy是WeakReference弱引用的子类,也是WeakCache的私有静态内部类,是一级缓存的key,
* 当然key是Object类型的,是为了兼容null。
*/
private static final class CacheKey extends WeakReference {
private static final Object NULL_KEY = new Object();
private CacheKey(K key, ReferenceQueue refQueue) { // 传入WeakCache的属性,引用队列
super(key, refQueue); // 构造弱引用
this.hash = System.identityHashCode(key);
}
static Object valueOf(K key, ReferenceQueue refQueue) {
return key == null
// key为null时,不能被弱引用,所以这里用了一个Object类型的单例,这里也解释了一级缓存的key // 为Object类型的原因
? NULL_KEY
// 如果key不为null时,包装成一个弱引用对象
: new CacheKey<>(key, refQueue);
}
void expungeFrom(ConcurrentMap, ? extends ConcurrentMap, ?>> map,
ConcurrentMap, Boolean> reverseMap) { // 传入缓存map和reverseMap
ConcurrentMap, ?> valuesMap = map.remove(this); // 直接移除
if (valuesMap != null) { // valuesMap:二级缓存
for (Object cacheValue : valuesMap.values()) { // 遍历二级缓存的value
// 从这里也可以看出reverseMap的key为二级缓存的value
reverseMap.remove(cacheValue);
}
}
}
}
/**
* 弱引用,当一个堆中的对象仅仅被弱引用指向时(没有强引用指向),如果这时候JVM执行GC,则无论
* 内存空间是否足够,这个对象都会被回收。
*
* Java四种引用类型(取自深入理解Java虚拟机)
* 强引用:类似“Subject realSubject = new RealSubject();”,只要强引用存在,GC就不会
* 回收对象;
* 软引用:用来描述一些还有用并非必需的对象,对于软引用关联着的对象,在系统将要发生内存溢
* 出异常之前将会把这些对象列回可回收范围之中进行第二次回收;
* 弱引用:强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次GC;
* 虚引用:也被称为幽灵引用或幻影引用,最弱的一种引用关系。一个对象是否有虚引用存在,完全
* 不会对其生存时间构成影响,也无法通过虚引用取得一个对象实例。关联虚引用的唯一目
* 的就是在一个对象被GC回收时收到一个系统通知。
*/
public class WeakReference extends Reference {
public WeakReference(T referent) {
super(referent);
}
/**
* 被弱引用指向的对象(referent)在被回收后,会把弱引用对象(this),
* 也就是WeakReference对象或者其子类的对象,放入队列ReferenceQueue中
*
* @param referent 被弱引用指向的对象
* @param q 引用队列
*/
public WeakReference(T referent, ReferenceQueue super T> q) {
super(referent, q);
}
}
二级缓存key生成
通过KeyFactory(subKeyFactory)工厂生成二级缓存的key。key的生成方式比较简单,直接根据被代理类实现的接口数量来决定生成哪个Key。
private static final Object key0 = new Object();
private static final class KeyFactory
implements BiFunction[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]);
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
函数式接口
Supplier
生成一个T类型的Supplier容器,每次调用get方法返回一个不同的T类型实例。
@FunctionalInterface
public interface Supplier {
T get();
}
用法:
public static void main(String[] args) {
// 创建Supplier容器,声明类型,此时并不会调用对象的构造方法,不会创建对象
Supplier sup= TestSupplier::new;
//调用get()方法,此时会调用对象的构造方法
sup.get();
sup.get();
}
执行结果:
com.demo.srpingboot.common.JDKproxy.RealSubject@3b95a09c
com.demo.srpingboot.common.JDKproxy.RealSubject@6ae40994
BiFunction
与Function不同,可以传入三个参数,前两个参数做运算,第三个参数将执行结果返回。
@FunctionalInterface
public interface BiFunction {
R apply(T t, U u);
default BiFunction andThen(Function super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
用法:
public static int compute(int a,
int b,
BiFunction biFunction) {
return biFunction.apply(a, b);
}
public static void main(String[] args) {
compute(2, 3, (v1, v2) -> v1 + v2); // 5
compute(2, 3, (v1, v2) -> v1 - v2); // -1
compute(2, 3, (v1, v2) -> v1 * v2); // 6
}
总结
JDK动态代理底层使用二级缓存存放代理类,以减少代理类的重复构建以及快速创建代理对象。将类加载器包装成一个CacheKey对象作为一级索引,根据接口数组的数量产生不同的二级索引对象,并把通过类加载器和接口数组创建的代理类包装成CacheValue,将其放入二级缓存map中。在创建包装代理类为CacheValue的过程中,使用了一个Factory实现代理类的延迟初始化,巧妙的通过双重校验机制避免了创建代理类时线程安全的问题,同时又保证了读取代理类时的并发性能。