Java架构师交流群:793825326
java版本:jdk1.8
IDE:idea2019
按照目前流行的分类,java有两种代理:静态代理,动态代理。
静态代理很简单,但灵活性很低,需要自己写代理类,实现相关接口,下面是一个静态代理的示例代码:
接口:
public interface IDataService {
void save();
}
实现:
public class DataService implements IDataService {
@Override
public void save() {
System.out.println("数据保存!");
}
}
代理类:
public class DataServiceProxy implements IDataService {
private IDataService dataService;
public DataServiceProxy(IDataService service)
{
this.dataService=service;
}
@Override
public void save() {
//do something...
this.dataService.save();
//do somthing...
}
}
测试代码:
DataServiceProxy dataServiceProxy=new DataServiceProxy(new DataService());
dataServiceProxy.save();
如果要同时支持代理另外一个类,则该代理类还要同时实现另外一个类的接口。
如果使用java的Proxy这个类,则就避免了自己写代理类的麻烦,这种方案,也就是我们通常所说的动态代理。动态代理和静态代理的划分依据我个人理解是:是否根据不同的类提前写好代理类,这里面所说的这个代理类,只能代理特定的对象,不具备普适性。如果我们将静态代理类的实现方案采用反射的方法实现,那么这个代理类则不用再实现具体的接口,同时可以代理任何的对象,它便是动态代理了。所以,使用java的Proxy实现代理功能,则属于动态代理。
下面讲一下如何使用Proxy实现动态代理:
通用代理类:
public class ProxyFactory<T> {
private T target;
public ProxyFactory(T target){
this.target=target;
}
public T getProxyInstance(){
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) ->
{
System.out.println("before:do something...");
Object returnValue = method.invoke(target, args);
System.out.println("after:do something...");
return returnValue;
}
);
}
}
测试代码:
IDataService dataService=new DataService();
ProxyFactory<IDataService> proxyFactory=new ProxyFactory<>(dataService);
proxyFactory.getProxyInstance().save();
运行结果:
before:do something...
数据保存!
after:do something...
首先写一个泛型类,实例化时将委托对象注入到代理类中,然后使用Proxy的newProxyInstance方法创造代理类,并转为T类型。获取到代理类之后,就可以像成长的类一样使用了。
这里面用到了泛型类,内部类,以及java 8的新特性lamda表达式。
这里我们的委托方法是没有返回值的,那么returnValue就是空,如果有返回值,returnValue为method的返回值。
值得一提的是,该方法仅支持实现了接口的委托,如果DataService没有实现IDataService,则使用如下测试代码时,会抛出异常。
DataService dataService=new DataService();
ProxyFactory<DataService> proxyFactory=new ProxyFactory<>(dataService);
Object obj=proxyFactory.getProxyInstance();
DataService ds=(DataService)obj;
异常信息:
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to ProxyTest.DataService
at ProxyTest.App.main(App.java:18)
那么如果DataService不实现IDataService,但我们又想实现动态代理,应该怎么做呢,这就要用到一个第三方的库
Cglib了,该库的具体介绍,参考我的另一篇文章https://blog.csdn.net/dap769815768/article/details/90448389。
下面的简单分析下Proxy.newProxyInstance()这个方法的源码,这里我只分析一些关键的代码,许多其他的代码比如合法性检查,非空判断,是否已经存在判断等等都省略掉了。
1.Debug下跟踪代码,看到的第一部分代码如下:
@CallerSensitive
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);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
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 (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
2.这个方法其他的部分我们不关注,主要是一些异常处理,条件判断的东西,重点关注下面的三句代码:
Class<?> cl = getProxyClass0(loader, intfs);
......
......
......
final Constructor<?> cons = cl.getConstructor(constructorParams);
......
......
......
return cons.newInstance(new Object[]{h});
第一句使用getProxyClass0()创建一个代理类
第二句根据代理类获取到构造方法
第三句根据构造方法创建一个代理类实例并返回,这里的构造方法的参数是我们传入的InvocationHandler
3.所以重点在第一句,如何在运行时创建一个新的类,跟踪代码,进入到方法内部:
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);
}
这里我们会发现,接口数不能超过65535,否则就抛出异常。由于我们只继承了一个接口,所以很显然interfaces的长度应该为1。
4.跟踪进get方法:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//前面的代码大致是进行一些合法检查,以及是否已经存在判断,如果不存在则创建新的,重点
//关注supplier是如何生成的,下面的代码是一个无限循环,目的是得到supplier,并得到返回值
while (true) {
//根据supplier获取要创建的类,即Class<?>
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
//如果supplier为空,则创建一个
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
重点关注这个无限循环体内的代码,这部分的代码分成两部分,一部分是根据supplier的get方法获取到新建的类,并返回,一部分是生成supplier的代码。生成的代码其实就是一句话,其他的都是判断是否已经存在了:
factory = new Factory(key, parameter, subKey, valuesMap);
5.跟进Factory这个类的构造方法:
Factory(K key, P parameter, Object subKey,ConcurrentMap<Object, Supplier<V>> valuesMap)
{
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
这个类继承了Supplier<V>接口:
构造方法只是把必要的变量传进来,并没有其他的操作,重点还是get这个方法,这个方法最终可以获得我们想要的Class<?>:
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
//具体创建新类的方法
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// try replacing us with CacheValue (this should always succeed)
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}
这个方法的核心代码仍然只有一句:
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
也就是valueFactory.apply()这个方法。
6.跟进valueFactory.apply()这个方法:
@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 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());
}
}
Class.forName是利用反射获取类的方法,这里简单讲解一下它的用法。
正常情况下,我们使用一个类的步骤是这样的:
IDataService dataService=new DataService();
dataService.save();
如果我们没法直接拿到这个类的其他信息,只是知道类名和方法名,应该怎么做呢,看下面的代码:
try {
Class<?> ds=Class.forName("springaoptest.DataService");
ds.getMethod("save").invoke(ds.newInstance());
}
catch (Exception ex)
{
System.out.println(ex.toString());
}
那么这里面的Class.forName()的方法的作用就显而易见了,利用反射根据类名获取到类的信息,然后通过newInstance()方法实例类。通过getMethod()方法获取到方法名,通过invoke()来执行方法。
我们可以看到valueFactory.apply()首先做的就是使用反射获取到接口信息,然后进行一系列合法性的判断。如果不合法则抛出异常。
紧接着就是经过一系列的操作生成代理类的名称,获取到的类名为:com.sun.proxy.$Proxy0。生成类名后,便是生成类文件了:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlags);
最核心的方法就在这里:ProxyGenerator.generateProxyClass。后面一句代码:
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
的作用利用ProxyGenerator.generateProxyClass方法生成的类文件生成最终的代理类,这是一个本地方法,到这里便无法继续跟踪代码了:
private static native Class<?> defineClass0(ClassLoader loader, String name,byte[] b, int off, int len);
7.所以要关注的是ProxyGenerator.generateProxyClass,跟踪进ProxyGenerator.generateProxyClass()方法,看下这个方法的实现:
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
该方法的核心代码仍然就只是一句:final byte[] var4 = var3.generateClassFile();
至于"if (saveGeneratedFiles)"这句if语句,可以帮助我们查看自动生成的代理类,后面我们会用到它,我还会再提它,这里先标记一下。
8.跟踪进var3.generateClassFile()这个方法:
private byte[] generateClassFile() {
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName,
"Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
//以上语句的作用是给动态生成的class类添加方法,其实就是往proxyMethods这个数组里面添加元素
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
这部分代码的可读性比较差,因为里面很多变量名都是var,不过这部分代码其实并没有必要读太通透,因为它涉及的东西不多,最终的目的也就是根据之前获取到的参数比如类名,接口信息等等生成最终的class文件。
这个方法的第一步是添加所有的必要的方法信息,放到一个数组this.methods里面并且进行合法性检查和做一些必要的设置,比如方法数和属性均不能超过65535。一直到这句:
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
也就是我们熟悉的IO操作,从这句开始,就是进行类文件数据流读写操作了。然后下面的一系列操作,就是往var14这个字节流里面写数据,这些数据包含了java字节码文件的基本信息,比如版本啊,方法啊,可访问性啊等等。所有的流数据写好之后,返回:return var13.toByteArray();
9.至此,动态代理类就生成了。那么如果我们想看到这个生成的动态代理类长什么样,应该怎么办呢,这就用到了我在第7步提到的参数(saveGeneratedFiles)了,把这个参数设置为true,系统就会把生成的流数据转为文件,如何把这个数据设置为true呢,很简单,在main方法里面加一句代码:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
IDataService dataService=new DataService();
ProxyFactory<IDataService> proxyFactory=new ProxyFactory<>(dataService);
IDataService ds=proxyFactory.getProxyInstance();
ds.save();
再次定位到saveGeneratedFiles这个变量的申明代码:
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(
new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
你就明白为何这么做,它就会变成true了。这相当于系统的权限控制器,我们加入的代码,是让和它相关的全局属性的键值对的value为true。这样,系统就具备了这个权限。
加了这句代码之后,你只需要启动代码,等代码执行结束后,你的项目目录下就多了一个自动生成的类文件:
10.打开这个文件,就能看到反编译后的java代码了:
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import springaoptest.IDataService;
public final class $Proxy0 extends Proxy implements IDataService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $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 void save() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 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"));
m3 = Class.forName("springaoptest.IDataService").getMethod("save");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从反编译后的源码我们可以发现,系统自动生成的三个方法:equals、toString、hashCode和我们自己的方法save一样,都是使用我们自己传进去的InvocationHandler的invok方法调用的,也就是说它们最终执行的代码都是我们的代理类ProxyFactory里面的这部分代码:
System.out.println("before:do something...");
Object returnValue = method.invoke(this.target, args);
System.out.println("after:do something...");
return returnValue;
也就是说这四个方法,在被执行的时候都会打印日志。
整个java动态代理的过程,大概就是如此,这里很多细节都被我直接忽略了,后面如果有需要,再深入研究下。