Java的代理分为静态代理和动态代理,静态代理因为违反开闭原则,一次只能代理一个对象,缺少实用性,所以一般用的很少,所以只记一下动态代理。
动态代理定义:为其它对象提供一种代理以控制对这个对象的访问控制;在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。
举一个实际的例子,比如我想要买一包烟,但生产这包烟的是卷烟厂,我不可能直接去卷烟厂去买,所以现在就需要一个商店作为代理,向消费者销售卷烟厂生产的香烟,在这里商店就是代理类。
首先我们写一个SaleTools接口,提供了卖东西的方法。
/**
* 售卖方式
*/
public interface SaleTools {
void saleCigarette();
}
然后定义一个实现了SaleTools的类CigarFactory ,我们可以把它看成实际的卷烟厂,通过saleCigarette()方法生产并销售香烟。
/**
* 卷烟厂
*/
public class CigarFactory implements SaleTools{
@Override
public void saleCigarette() {
System.out.println("----------卖了一包烟");
}
}
然后定义代理类StoreProxy
/**
* 商店 代理类
*/
public class StoreProxy implements InvocationHandler {
private Object factory;
public void setFactory(Object factory) {
this.factory = factory;
}
void before() {
System.out.println("----选择品牌,进了一批香烟");
}
void after() {
System.out.println("-----卖完数钱------");
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(factory, args);
after();
return result;
}
}
在这个类里通过Object类型声明了一个持有的工厂对象,通过setFactory(Object factory)赋值进来。
继续分析getProxyInstance()方法,在这个方法里我们把持有factory的类加载器ClassLoader、Interfaces以及一个InvocationHandler通过Proxy创建了一个代理实现类的对象。
然后接下来的invoke方法就是InvocationHandler接口的实现方法,通过对代理实现类的反射操作为我传过来一个Method,通过执行这个方法就可以实现我们持有对象的操作。
最后来看买烟的消费者,也就是我们的目标用户
/**
* 消费者
*/
public class Consumer {
public static void main(String[] args) {
//实现接口的类通过创建对象赋值给接口
SaleTools factory=new CigarFactory();
StoreProxy storeProxy =new StoreProxy();
storeProxy.setFactory(factory);
SaleTools salesclerk = (SaleTools) storeProxy.getProxyInstance();
salesclerk.saleCigarette();
}
}
从上面可以看出,我们实例化CigarFactory后赋值给SaleTools接口对象factory,然后把factory传入storeProxy代理类作为持有对象。
通过storeProxy.getProxyInstance()获取factory的代理实现类的对象salesclerk后,我们就可以让salesclerk执行完整的操作。打印结果如下:
大家看到了,最终执行销售方法的不是CigarFactory工厂,也不是StoreProxy代理类,是通过代理类内持有的factory对象生成的一个代理实现类的对象salesclerk。这个对象所属的代理实现类不是我们写的,是Proxy通过虚拟机动态生成的。我们Debug看一下就知道。
我可以看到上图下部黑框中,salesclerk对象的名字是“$Proxy0...”类型的,我们进入到Proxy.newProxyInstance()源码研究下。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
.
.
.
/*
* Look up or generate the designated proxy class.
*/
Class> cl = getProxyClass0(loader, intfs);
.
.
.
final Constructor> cons = cl.getConstructor(constructorParams);
.
.
.
return cons.newInstance(new Object[]{h});
.
.
.
}
我们只看主要的方法:
通过我们传入的类加载器和接口创建了一个动态代理类的class对象cl,cl获取构造器后,最后直接返回通过构造器创建的动态代理类的对象。
可是,这里面是怎么就根据一个类加载器和接口就创建了一个不存在的类呢?
我继续深入getProxyClass0(loader, intfs)方法
private static final WeakCache[], 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);
}
这个方法就很简单,首先检查了一下是否超过最大接口数,然后出现了动态代理类在JDK内部的缓存对象proxyClassCache,我们的需要的动态代理类就是从这里面取出来的,可是我们还没创建,它是怎么取的呢,继续深入:
public V get(K key, P parameter) {
.
.
.
// create subKey and retrieve the possible Supplier stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue instance
V value = supplier.get();
if (value != null) {
return value;
}
}
.
.
.
}
最主要的是这一句‘ subKeyFactory.apply(key, parameter)’,我们继续进入apply方法
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
.
.
.
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
从上可以看出动态代理实现类的命名是由3部分构成的,
proxyPkg
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
public final class ReflectUtil {
public static final String PROXY_PACKAGE = "com.sun.proxy";
}
proxyClassNamePrefix
private static final String proxyClassNamePrefix = "$Proxy";
num
long num = nextUniqueNumber.getAndIncrement();
好的,到现在类名有了,结合传入的接口,JDK直接为我们生成了字节码数组proxyClassFile,最后通过类加载器生成动态代理类
private static native Class> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
最终通过一开始Proxy.newInstance方法里的
Class> cl = getProxyClass0(loader, intfs);
创建了动态代理类,最终实例化了对象。
至此,动态代理类及对象的创建过程就完成了。通过这个代理对象,我们可以不直接使用目标对象就可以使用它的方法,而且还可以对执行方法前和执行方法后做一个额外的操作实现方法的增强。
第一次写学习博客,感觉不错,但由于知识局限难免出错,请大家多多指正。