文章目录
前言
说到动态代理不得不说AOP(Aspect-Oriented Programming)面向切面编程,它是OOP(Object Oriented Programming)的补充,通过AOP思想我们可以把公共部分抽取出来(比如权限控制、日志打印、任务执行耗时等等),一方面减少了代码污染,另一方面也降低了各业务逻辑之间的耦合度。Spring中的AOP就是基于CGLIB与JDK动态代理实现的。
1. 静态代理
静态代理就是目标类与代理类是相对确定的,因此称作静态代理。
看到代理时,我们会想到代理模式,什么是代理模式呢?代理模式即为其他对象提供对目标对象另外的访问方式,也就是通过代理对象访问目标对象。
接下来我们通过一个例子来了解什么是静态代理以及优点和缺点:
public interface Business {
void transfer();
}
public class Bank implements Business {
@Override
public void transfer() {
System.out.println("给目标账户执行转账中···");
}
}
public class ATM implements Business {
private Business business;
ATM(Business business) {
this.business = business;
}
@Override
public void transfer() {
check(false);
this.business.transfer();
check(true);
}
private void check(boolean finishFlag) {
String currentStatus = finishFlag ? "转账完毕,收起银行卡。" : "从票夹中取出取款卡,插入ATM,开始转账操作···";
System.out.println(currentStatus);
}
}
public class Consumer {
public static void main(String[] args) {
Bank bank = new Bank();
Business transfer = new ATM(bank);
transfer.transfer();
}
}
输出结果:
从票夹中取出取款卡,插入ATM,开始转账操作···
给目标账户执行转账中···
转账完毕,收起银行卡。
代码示例中,ATM(代理类)与Bank(目标类)都可以进行转账操作,ATM则在转账前后增加了一些额外的处理。
如果Business增加了一些新的业务比如取款、存款接口,那么目标类与代理类都需要去实现相应的业务接口。
静态代理的优缺点:
- 优点:隐藏了目标类接口的实现类提高安全性,在特定场景下实现了解耦。
- 缺点:代理类需要实现目标类的接口,灵活性差,例如:当目标类增加了某些接口,并且存在多个代理类,这将会导致目标类的所有代理类都需要做出相应修改。
2. 动态代理
2.1 JDK动态代理
JDK动态代理是通过
拦截器加反射机制
生成代理接口的匿名类。
public interface Business {
void transfer(String transferAmount);
void withdraw(String withdrawAmount);
}
public class Bank implements Business {
@Override
public void transfer(String transferAmount) {
System.out.println("给目标账户执行转账" + transferAmount + "元···");
}
@Override
public void withdraw(String withdrawAmount) {
System.out.println("给客户取款" + withdrawAmount + "···");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ATM implements InvocationHandler {
private Object target;
public ATM(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
operation(false);
Object result = method.invoke(target, args);
operation(true);
return result;
}
Object getJdkProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
private void operation(boolean finishFlag) {
String currentStatus = finishFlag ? "业务处理完毕,收起银行卡。" : "开始业务操作···";
System.out.println(currentStatus);
}
}
public class Consumer {
public static void main(String[] args) {
ATM atm = new ATM(new Bank());
Business business = (Business) atm.getJdkProxy();
business.transfer("100");
business.withdraw("1000000000");
}
}
开始业务操作···
给目标账户执行转账100元···
业务处理完毕,收起银行卡。
开始业务操作···
给客户取款1000000000···
业务处理完毕,收起银行卡。
2.1.1 探究JDK动态代理如何实现
2.1.1.1 java.lang.reflect.Proxy
···
/**
* 代理类的构造参数类型
*/
private static final Class<?>[] constructorParams = { InvocationHandler.class };
/**
* 代理类缓存
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/**
* 代理实例的调用处理器
* InvocationHandler是由代理实例的调用处理程序实现的接口。
* 每个代理实例都有一个关联的调用处理器。
* 当通过代理实例调用方法时,该方法的调用将被编码并分配到其调用处理程序的invoke方法。
*/
protected InvocationHandler h;
/**
* Prohibits instantiation.
*/
private Proxy() {
}
···
/**
* 创建代理类实例
* @param loader 创建代理类的加载器
* @param interfaces 代理类需要实现的接口数组
* @param h 调用处理器
*/
@CallerSensitive
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);
/*
* 通过指定的InvocationHandler调用它的构造
*/
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});
}
// 省略
}
我们调用Proxy.newProxyInstance()大致经过一下三个步骤:
Class<?> cl = getProxyClass0(loader, intfs)
得到代理类;final Constructor<?> cons = cl.getConstructor(constructorParams)
获得代理类构造;return cons.newInstance(new Object[]{h})
返回代理类实例。
第一步很关键,怎么得到代理类呢?
/**
* 获取代理类
*/
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(loader, interfaces);
看命名是从代理类缓存中根据目标类加载器与代理类需要实现的接口数组获取到对应代理类,那么具体步骤呢 ?
下面我们来看看proxyClassCache.get(loader, interfaces)
;是如何获取到代理类的。
2.1.1.2 java.lang.reflect.WeakCache
/**
1. K : 键
2. P : 参数
3. V : 值
4. K & V是弱引用
5. P 是强引用
*/
final class WeakCache<K, P, V> {
private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
// Object:key,第二个Object:subKey,Supplier< V >:value
// 缓存
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
// 存在标注CacheValue<V>的可用性
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
// 用于生成子键
private final BiFunction<K, P, ?> subKeyFactory;
// 用于生成值
private final BiFunction<K, P, V> valueFactory;
/**
* 构造WeakCache的实例
*
* @param subKeyFactory subKeyFactory函数从键和参数计算subKey
* @param valueFactory valueFactory函数根据键和参数计算value。
*/
public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
/**
* 通过缓存查找值。
* 如果给定的(key,subKey)对在缓存中没有任何条目,
* 或者该条目已被清除,则这将始终评估subKeyFactory函数,并可选地评估valueFactory函数。
*
* @param key 创建代理类的加载器
* @param parameter 代理类需要实现的接口数组(与key一起使用,创建子键和值的参数)
* @return 缓存的值
*/
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
// 通过弱引用机制清理过期缓存
expungeStaleEntries();
// 根据key(ClassLoader)生成cacheKey,一级key
Object cacheKey = CacheKey.valueOf(key, refQueue);
// 获取二级缓存
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
/*
* putIfAbsent() 并发场景下提高性能,保证线程安全:
* 1.如果传入key对应的value已经存在,就返回存在的value;
* 2.如果不存在,就添加key和value,返回null。
*/
ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// 根据目标类接口数组生成subKey
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// 通过subkey获取supplier(二级缓存的值)
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 无限循环找到对应的代理类return
while (true) {
// 通过subkey从缓存中获取到supplier
if (supplier != null) {
// supplier 可能是Factory 或者 CacheValue<V>
V value = supplier.get();
if (value != null) {
return value;
}
}
// 二级缓存里面supplier不存在
// 或者supplier返回null,(可能由于CacheValue是弱引用,若代理类不再使用被GC回收了,对应CacheValue的get值 == null)
// 懒加载的方式重新构造factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
// 二级缓存中通过subkey未获取到supplier
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
// supplier == null 缓存加入成功 supplier = factory,再次循环后返回
if (supplier == null) {
supplier = factory;
}
} else {
// 若supplier不为空且supplier.get() == null则supplier是无效的
if (valuesMap.replace(subKey, supplier, factory)) {
// 成功替换后,supplier = factory,再次循环后返回
supplier = factory;
} else {
// 通过subkey重新尝试获取supplier
supplier = valuesMap.get(subKey);
}
}
}
}
···
WeakCache.get()大致经过以下五步:
CacheKey.valueOf(key, refQueue)
通过目标类生成cacheKey作为一级Key;map.get(cacheKey)
获取二级缓存,若为空则通过putIfAbsent()
的方式放入new ConcurrentHashMap<>()
;Objects.requireNonNull(subKeyFactory.apply(key, parameter));
获取二级key;valuesMap.get(subKey)
然后通过二级缓存key获取二级缓存的值;valuesMap.putIfAbsent(subKey, factory)
二级缓存的值就是WeakCache$Factory
实例,然后通过supplier.get()
,返回生成的代理类;
接下来我们再看看V value = supplier.get();
是怎么生成代理类的呢?
2.1.1.3 WeakCache$Factory
private final class Factory implements Supplier<V> {
private final K key;
private final P parameter;
private final Object subKey;
private final ConcurrentMap<Object, Supplier<V>> valuesMap;
Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
@Override
public synchronized V get() {
// 重新检查
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// 可能是supplier被CacheValue替换了,或者因为失败而被删除,返回null
// WeakCache.get()来重试循环
// if (supplier == null) {
// supplier = factory;
// }
return null;
}
// 创建新值
V value = null;
try {
// 通过ProxyClassFactory生成代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) {
// 移除subKey与this
valuesMap.remove(subKey, this);
}
}
assert value != null;
// 使用CacheValue包装value
CacheValue<V> cacheValue = new CacheValue<>(value);
// reverseMap中标记Value可用
reverseMap.put(cacheValue, Boolean.TRUE);
// 尝试使用this替换cacheValue
// subKey key, supplier oldValue, cacheValue newValue
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// 返回代理类
return value;
}
}
WeakCache$Factory.get()大致经过以下步骤:
- 通过二级key获取supplier,检查supplier是否等于this,
false
:重新通过WeakCache.get()
来重试,true
:继续; return
通过valueFactory.apply(key, parameter)
生成的代理类;
总结
通过上面的源码阅读我们不难发现:JDK动态代理基于反射机制实现,只对基于接口进行代理,且生成的代理类继承Proxy;通过拦截器,在目标类的接口被调用的基础上,增加切面进行功能增强。在Spring中Jdk动态代理的应用也很广泛。