java代理分静态代理和动态代理,java静态代理实现有两种:继承,聚合。java动态代理实现有两种:JDK动态代理,CGLIB代理。
区别:
使用静态代理时,在编译时期就有确定的.class文件,即java类文件是我们自己创建并编写的,然后通过编译器编译,在项目中生成对应的.class文件;
使用动态代理时,在编译时期没有明确的.class文件,即我们并没有创建java代理类的类文件,因此编译后也就不会出现.class文件,他是在运行时在内存中动态的产生代理类的类字节码,并通过类加载器将字节码载入到jvm中生成对应的类对象。
特点:
静态代理由于有明确的类文件,所以会产生很多代理类的类文件,在复杂的系统中,会造成类爆炸,不好维护,灵活性低。
动态代理减少了类的编写与创建,简化了代理过程,灵活性好,耦合度低。
大概过程: .java->.class->Class对象->object
具体过程:在程序运行时,利用反射+字符串拼接生成.java类的字符串形式,将此字符串保存到磁盘,经过第三方编译工具的动态编译,在磁盘上生成对应的.class文件,然后通过URL类加载器将.class字节码文件载入到jvm内存中,随之加载生成类对象,通过类对象获取构造器对象,通过构造器对象构造生成实例对象,这里为什么不用类对象直接newInstance()的原因是,这个方法调用的是默认的空参构造器,我们生成的代理类可能不存在空参构造,所以为了通用性,我们采用构造器对象调用指定的构造器方法生成实例对象。
代理逻辑的具体实现类CustomInvocationHandler的作用:该类中的invoke()方法是具体的代理处理逻辑,我们之前拼接的字符串中,在代理类中持有一个CustomInvocationHandler类型的引用,我们每回调用代理类的一个方法实际都是调用的CustomInvocationHandler中的invoke()方法来处理代理逻辑的,我们一会放上我们拼接后的java类看一看便知
1.生成代理对象的工具类
package com.wang.proxy.custom;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ProxyUtil {
//下面这两个字符串都可以自己定义,使用PATHNAME时得先创建对应的目录,
//比如我下面写的这个路径,需要自己先去D盘下创建com目录,
//com目录下在创建一个子目录wang,你也可以在拼接字符串的时候自动去处理生成目录,
//使用file.mkdir()创建目录,CLASSNAME应该和PATHNAME的路径名称一致;
public static final String PATHNAME="d:\\com\\wang\\$Proxy.java";
public static final String CLASSNAME="com.wang.$Proxy";
/**
*
* @param interfaces 接口的类对象
* @param h 逻辑处理类
* @return 返回一个代理对象
*/
public static Object newInstance(Class interfaces, CustomInvocationHandler h){
Object proxy=null;
//1.生成java类的字符串形式
String content=generateJavaFile(interfaces);
//2.写入到磁盘中
File file =new File(PATHNAME);
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
//3.动态编译磁盘下的.java文件,生成对应的.class
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//4.通过URLClassLoad类加载器,将文件系统中的字节码文件载入jvm内存,
//并生成对应的类对象。
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass(CLASSNAME);
//5.通过类对象构造实例对象
Constructor constructor = clazz.getConstructor(CustomInvocation.class);
proxy = constructor.newInstance(h);
//clazz.newInstance(); //此处不能用此方法直接构造实例对象的原因是,
//此方法调用类的空参构造器,类中可能没有空参构造器
}catch (Exception e){
e.printStackTrace();
}
return proxy;
}
/**
* 此方法通过字符串拼接生成.java类的字符串形式
* @param interfaces 接口的类对象
* @return .java文件的字符串形式
*/
private static String generateJavaFile(Class interfaces){
Method methods[] =interfaces.getDeclaredMethods(); //获取到接口下面声明的所有方法对象
String line="\n";
String tab ="\t";
String infName = interfaces.getSimpleName(); //接口名
String content ="";
String packageContent = "package com.wang;"+line; //包名
String importContent = "import "+interfaces.getName()+";"+line //引包语句
+"import com.wang.proxy.custom.CustomInvocation;"+line
+"import java.lang.Exception;"+line
+"import java.lang.reflect.Method;"+line;
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line; //声明类的哪一行
String filedContent =tab+"private CustomInvocation h;"+line; //类中的引用,固定的是CustomInvocation h类型,
// 代理类中所有方法的实现直接调用h.invoke()方法就能实现逻辑的动态代理
String constructorContent =tab+"public $Proxy (CustomInvocation h){" +line //构造器语句
+tab+tab+"this.h =h;"
+line+tab+"}"+line;
String methodContent = "";
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName(); //方法的返回类型
String methodName =method.getName(); //方法名称
// Sting.class String.class
Class args[] = method.getParameterTypes(); //参数的类型列表,例如[String.class,Integer.class]
String argsContent = ""; //定义方法内的参数内容
String paramsContent=","; //反射获取方法对象时的getDeclaredMethod()方法内的形参
int flag =0;
String methodArgs="new Object[]{"; //反射获取方法对象时的getDeclaredMethod()方法内的形参类型
for (Class arg : args) {
String temp = arg.getSimpleName();
//String
//String p0,Sting p1,
argsContent+=temp+" var"+flag+",";
paramsContent+=temp+".class,";
methodArgs+="var"+flag+",";
flag++;
}
methodArgs=methodArgs.substring(0,methodArgs.length()-1);
if(!paramsContent.equals(",")) {
paramsContent = paramsContent.substring(0, paramsContent.length() - 1);
}else{
paramsContent="";
}
methodArgs+="}";
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(","));
}
if(methodArgs.equals("new Object[]}")) {
methodArgs = "(Object[])null";
}
//"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
String returnContext="";
//"return ("+returnTypeName+")h.invoke(this,method,"+methodArgs+");"+line
if(returnTypeName.equals("void"))
returnContext="h.invoke(this,method,"+methodArgs+");"+line;
else
returnContext="return ("+returnTypeName+")h.invoke(this,method,"+methodArgs+");"+line;
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+interfaces.getName()+"\").getDeclaredMethod(\""+methodName+"\""+paramsContent+");"+line
+tab+tab+returnContext;
methodContent+=tab+"}"+line;
}
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
return content;
}
}
在此,我们先看一下generateJavaFile()方法生成的字符串,写到磁盘上的效果如下:
这里只是简单的代理的UserService接口的一些方法,实际JDK动态代理还代理的Object下的所有方法,我们将在源码剖析时展示JDK生成的类文件样式;
package com.wang;
import com.wang.proxy.custom.service.UserService;
import com.wang.proxy.custom.CustomInvocation;
import java.lang.Exception;
import java.lang.reflect.Method;
public class $Proxy implements UserService{
private CustomInvocation h;
public $Proxy (CustomInvocation h){
this.h =h;
}
public String update(String var0)throws Exception {
Method method = Class.forName("com.wang.proxy.custom.service.UserService").getDeclaredMethod("update",String.class);
return (String)h.invoke(this,method,new Object[]{var0});
}
public void save()throws Exception {
Method method = Class.forName("com.wang.proxy.custom.service.UserService").getDeclaredMethod("save");
h.invoke(this,method,(Object[])null);
}
public void save(String var0)throws Exception {
Method method = Class.forName("com.wang.proxy.custom.service.UserService").getDeclaredMethod("save",String.class);
h.invoke(this,method,new Object[]{var0});
}
}
2.统一代理逻辑的接口CustomInvocation
package com.wang.proxy.custom;
import java.lang.reflect.Method;
public interface CustomInvocation {
Object invoke(Object proxy, Method method,Object[] args);
}
3.代理逻辑的具体实现类CustomInvocationHandler
package com.wang.proxy.custom;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class CustomInvocationHandler implements CustomInvocation {
private Object target;
public CustomInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Object result=null;
try {
System.out.println("代理对象的前置增强方法!");
result= method.invoke(target,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return result;
}
}
4.测试类
package com.wang.proxy.custom;
import com.wang.proxy.custom.service.UserServerImpt;
import com.wang.proxy.custom.service.UserService;
public class Client {
public static void main(String[] args) {
UserService o = (UserService) ProxyUtil.newInstance(UserService.class,new CustomInvocationHandler(new UserServerImpt()));
try {
o.save();
System.out.println(o.update("带参数和返回值的目标方法"));
o.save("带参数的目标方法");
} catch (Exception e) {
e.printStackTrace();
}
}
}
在此先说明,我们只是在这手写模拟了JDK动态代理的过程,实际过程比我们模拟的要复杂的多,JDK动态代理没有生成.java文件,是直接将数据写入到byte[]数组中,然后直接加载这个数组生成对应的类对象,稍后我们就会看到具体实现,现在我们来说手写模拟的缺点如下:
缺点:是我们自定义的动态代理,生成.java文件需要磁盘IO,动态编译也需要磁盘IO,相对于JDK动态代理来说性能差很多。
区别:用的类加载器不同,我们自己模拟的动态代理,采用的URLClassLoad,因为.class文件是在磁盘上的,所以我们需要通过URL去获取。但是JDK自带的动态代理是采用当前类的类加载器,因为此时的.class是在内存中的,以byte[]数组的形式存在的,所以可以直接用当前类的类加载器加载生成对应的类对象。
在弄清使用以及原理前,我们先要统一一些概念:
目标对象:要被代理的对象;
代理对象:用JDK动态代理生成的对象
目标类的方法对象:就是目标类中的每个方法自己特有的对象,在java中万物皆对象,一个方法本身也是一个对象,这个对象具有invoke()方法,可以调用方法;
如题,我们要使用动态代理,仅仅需要写一个逻辑处理类,其他的类JDK已经帮我们写好了, 此处理类需要一个目标对象的引用,因为在该类的invoke()方法内调用method.invoke()方法时(这两个invoke方法是不同的,往下看逻辑处理类便知),需要目标对象才能调用目标对象的方法,比如method是目标类中的某个方法对象,method.invoke(obj,args)这个obj应该是目标对象才可以实现方法的调用,所以我们在编写逻辑处理类时需要传入目标对象。
被代理类对应的接口:
package com.wang.Design.proxy.a;
public interface Service {
public void save();
}
被代理类:
package com.wang.Design.proxy.a;
public class RealService implements Service{
public void save(){
System.out.println("正在保存");
}
}
测试类:
package com.wang.Design.proxy.a;
import java.lang.reflect.Proxy;
public class Demo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
//目标对象
RealService service=new RealService();
System.out.println("目标对象所在的类"+service.getClass().toString());
Service proxyService = (Service) Proxy.newProxyInstance(RealService.class.getClassLoader(), RealService.class.getInterfaces(),(new <RealService>Handler(service)));
System.out.println("代理对象所在的类"+proxyService.getClass().toString());
//执行代理对象代理的方法
proxyService.save();
}
}
这里涉及到一个主要的生产代理实例的静态方法Proxy.newInstance()
这个方法返回一个代理对象,该对象所在的代理类用loader类加载器载入jvm,这个代理类实现了interfaces[]这些接口,因此会实现这些接口中的所有方法,每个方法的具体实现逻辑是h实例,也就是我们下面要写的逻辑处理类的实例;
下面的是逻辑处理类,相当于我们模拟动态代理类时的CustomInvocationHandler类
package com.wang.Design.proxy.a;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Handler<T> implements InvocationHandler {
private T service;
public Handler(){}
public Handler(T service) {
this.service=service;
}
public Handler(Class<T> clazz) throws IllegalAccessException, InstantiationException {
this.service=clazz.newInstance();
}
@Override
//proxy表示代理类的实例,method表示传入的方法对象,实际运行时这个method应为目标类的方法对象
//args代表参数数组,实际运行时这个args应该为目标类的方法中的参数类型数组,例如[String.class,Intger.clsss]
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result; //用于接收目标方法的返回结果,可能为Null,说明目标方法没返回值
if(method.getName().equals("save")){
System.out.println("开启事务");
result=method.invoke(service,args); //这句话代表调用了目标对象的目标方法,
//在这句话的前后即为我们可以添加的处理逻辑
System.out.println("关闭事务");
}
return result;
}
}
在上面,我们写了一个逻辑实现类Handler,我们通过Proxy.newInstance方法返回了一个代理实例,实质上这个实例所在的代理类含有一个InvocationHandler类型的引用h,当我们调用代理类的某个方法时,这个方法的具体实现就是调用h的invoke()方法,我们将方法对象(method)传入,以便invoke()方法内部可以自己调用目标类中的目标方法,在invoke()的实现中,我们通过在method.invoke()方法执行的前后加入打印语句,来模拟要增强的动作,注意method.invoke()和InvocationHandler接口内的invoke()方法不是一个东西,下面我给上JDK动态代理生成代理类的.class字节码,大家便能更加理解。
import com.wang.dao.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class wang extends Proxy implements UserDao {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m0;
private static Method m3;
public wang(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, m4, (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);
}
}
public final void update() throws {
try {
super.h.invoke(this, m3, (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"));
m4 = Class.forName("com.wang.dao.UserDao").getMethod("save");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.wang.dao.UserDao").getMethod("update");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我们发现这个代理类每个方法的具体实现都是调用Proxy类中的h的invoke()方法,这个h即为我们写的逻辑实现类。还可以看到每个方法对象都是通过反射获得的目标类中的具体方法的方法对象。
通过上面分析得知,我们的代理类实例所在的代理类,是在程序运行期间动态生成的,而不是预先编译而成的,而每个代理类都继承了Proxy类,此类中含有一个InvocationHandler类型的引用,这也说明了JDK动态代理不能基于继承实现,因为java是单继承的,哪这个代理类实例到底是怎么生成的呢?
我们生成的代理类实例,是通过Proxy中的静态方法 newProxyInstance(ClassLoader loader, Class>[] interfaces,InvocationHandler h) 生成的,我们从这开始,逐步分析生成实例的过程:
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);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {//还是在检查权限
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//这里通过类对象获取到了构造器对象,通过构造器对象构造出的对象实例,
//构造时调用的带参构造器将逻辑处理对象h注入,此处没有直接使用clazz.newInstance()原因之前说过。
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;
}
});
}//通过带参构造传入h,使代理对象拥有h对象;
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);
}
}
getProxyClass0(loader, intfs)方法源码:
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
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);
}
这个方法十分简单,第一行是一个参数验证,重要的是return语句前面的源码注释,翻译过来如下:
如果实现给定接口的给定加载器定义的代理类存在,则只返回缓存的副本;否则,它将通过ProxyClassFactory创建代理类
意思很明显,如果当前要返回的代理对象所在的代理类存在,则直接从缓存中取出返回,不存在则用工厂生产一个代理类。我们可以了解到JDK底层用了缓存来提高获取代理类对象的性能
proxyClassCache.get(loader, interfaces)方法源码:
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 stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> 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;
}
}
// 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);
}
}
}
}
这个方法前面就是在找缓存,判断缓存是否有,如果没有,会进入到下面while (true) 的死循环,直到return value; 我们从前一个方法getProxyClass0(loader, intfs)了解到,当前这个方法返回的是一个类对象,所以可以断定这个value就是类对象,我们观察value在哪被赋过值,可以看到 V value = supplier.get(); ,value是从supplier.get()方法获取的,我们进入到supplier.get()源码:
这个get()源码要找,我们点入到get方法看到他是一个接口定义的方法,实现有很多,和JDK动态代理相关的类是WeakCache,所以我们找到了WeakCache下的get方法
@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怎么产生的,内部的一些额外操作,例如取缓存,判断权限之类的就略过了,直接看value是怎么产生的。我们看到此方法还是返回的一个value,观察那个地方修改过value,发现value = Objects.requireNonNull(valueFactory.apply(key, parameter));
可以看到是valueFactory.apply(key, parameter);方法生成了一个value,由名字可以得出,应该是工厂生产出了这个类对象,我们进入valueFactory.apply(key, parameter);方法,发现他也是一个接口,具体实现类有如下,我们调出和Proxy相关的,发现ProxyClassFactory类
进入ProxyClassFactory类的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());
}
}
}
直接上重点,我们找到return的地方,发现return的是一个 defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);方法,很显然这个方法返回了一个代理类类对象,这个方法的参数前两个都是提供好的(loader表示类加载器,proxyName表示代理类的名字),我们看第三个参数proxyClassFile,可以看到他是一个byte[]数组,可以了解到这个方法是用类加载器+类名+一个比特数组生成的,这个比特数组在哪生成的呢?我们看到return语句的上一行: byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
我们可以了解到这个proxyClassFile 其实就是代理类的字节码文件,当前是以byte[]的形式存在于内存中的,他通过代理类名+接口+标记生成了这个byte[],这个数组实际上是外部的.class文件载入内存后的存在形式,我们可以看到,这里是直接生成的byte[],我们进入此方法,观察byte[]数组是怎么形成的
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;
}
可以看到这是OpenJDK的源码,我们看到最后返回的var4,观察var4怎么生成的;
final byte[] var4 = 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);
}
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);
}
}
}
发现这个byte数组是由 var13.toByteArray();返回的,这个var13在方法中
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
可以看到 在源码中生成了一个比特流,然后通过向流中写入类的字节码信息,最后以byte[]形式返回,看下图,可能会有疑问他写的var14,最后怎么返回var13?其实他们是一个,var14.writeShort方法点进入会发现,他持有一个var13,这个var13是通过var14的构造方法传入的,实质还是在var13中写入。
这个out实质上就是构造器传入的var13;
这里可以看到,JDK动态代理底层是直接生成字节码在内存中的byte流的,而不是先写.java类文件再编译再载入的,我们回到刚才 return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);这个方法,我们现在知道了proxyClassFile的出处,我们看看defineClass0是怎么用byte[]生成这个类对象的
我们发现这个方法是一个native方法,方法的具体实现可能是由C或C++实现的。omg到底了,再往下走就触碰到我的知识盲区了…
我们知道了用字节码文件的byte数组和类加载器,通过这个方法可以生成对应的类对象。
总结一下动态代理的流程:实质上先通过ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);方法,用反射+接口,将生成的内容追加到流中,生成对应的byte[],这个数组内容实际上就是字节码(我们可以将这个byte[]通过输出流输出到文件系统中,通过IDEA反编译即可看到字节码文件)。
然后通过本地方法
defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
传入字节码和类加载器,生成对应的代理类类对象并返回,中间涉及到很多的权限认证、缓存架构、异常判断等操作,但本质的实现就是这样。