Java动态代理深度解析
引言
说起动态代理,很多人可能都没有直接去使用过。但是只要用过Spring,那动态代理就是一个是个绕不过的坎,因为Spring的核心特性之一AOP就是基于动态代理来实现的,那么什么情况下需要用到动态代理呢?
场景
考虑这样一个教师的接口:
public interface Teacher {
void teach();
}
假设我们有一个TeacherChan的实现类,陈老师教的是摄影:
public class TeacherChan implements Teacher {
@Override
public void teach() {
System.out.println("大家好,我是陈老师,我教大家摄影!");
}
}
另外还有一个TeacherCang的实现类,苍老师教的是生物:
public class TeacherCang implements Teacher {
@Override
public void teach() {
System.out.println("大家好,我是苍老师,我教大家生物!");
}
}
不管是陈老师还是苍老师,只要实现了Teacher这个接口,给我们传道授业解惑,为了礼貌起见,我们总应该给人家问声好吧。而问好这件事不需要老师主动要求,可以交给代理来做,每次有老师来上课,代理自动做了问好这件事。而代理类又分为静态代理和动态代理,静态代理在编写代码时已经确定了要代理的类,只能代理单一的类型,在此略过,今天重点讲动态代理。
Java动态代理
Java动态代理创建代理类的方法为:
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
其中ClassLoader是用来定义代理类的class文件的,用系统默认的就好,interfaces是要代理的接口,InvocationHandler是用来实际执行代理方法的接口,常用做法是实现该接口,并将需要代理的类实例对象传进去。
实现自己的方法执行器:
public class JdkDynamicProxy implements InvocationHandler {
private Object proxied;
public JdkDynamicProxy(Object object) {
this.proxied = object;
}
/**
* proxy为创建的代理类实例,method是本次被代理的方法,args是方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------------------老师好[by jdk动态代理]-------------------");
// 执行代理方法
Object obj = method.invoke(proxied, args);
System.out.println("-------------------老师再见[by jdk动态代理]-------------------");
// 返回方法执行结果
return obj;
}
}
创建代理类对象并执行:
// 代理陈老师
Teacher proxy1 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(),
new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherChan()));
proxy1.teach();
// 代理苍老师
Teacher proxy2 = (Teacher) Proxy.newProxyInstance(Teacher.class.getClassLoader(),
new Class[]{Teacher.class}, new JdkDynamicProxy(new TeacherCang()));
proxy2.teach();
输出结果:
-------------------老师好[by jdk动态代理]-------------------
大家好,我是陈老师,我教大家摄影!
-------------------老师再见[by jdk动态代理]-------------------
-------------------老师好[by jdk动态代理]-------------------
大家好,我是苍老师,我教大家生物!
-------------------老师再见[by jdk动态代理]-------------------
实际上,Java会过滤掉接口所有final、native等方法,并为剩下的所有符合条件的方法生成代理方法。而且,熟悉Spring的朋友应该知道,Spring的AOP机制的实现不仅使用了Java的动态代理,而且还引入了CGLib。因为Java的动态代理只能代理接口,而不能代理原始的类。那么为什么Java不能代理类呢,答案是Java的单继承机制。
深入Java动态代理的实现
Java的动态代理是怎么实现的呢?其实很简单,就是运行时生成一个代理类,该类实现了需要代理的接口,并返回这个代理类的实例对象给调用者
。调试进入Proxy.newProxyInstance()的方法内部,可以看到在Proxy内部生成class字节码的方法:
// 生成的代理类名前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 生成的代理类名序号
private static final AtomicLong nextUniqueNumber = new AtomicLong();
// 序号值加1
long num = nextUniqueNumber.getAndIncrement();
// 代理类名:$ProxyN
String proxyName = proxyPkg + proxyClassNamePrefix + num;
...
// 生成代理类的class字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
不难看出,生成的代理类的类名是$ProxyN的形式,所以我们经常会看到$Proxy0这个类,就是动态代理在运行时生成的。
既然知道了动态代理是怎么生成代理类的了,那我们不妨把它生成的类打印出来看看,到底里面是怎么实现的。
// 调用Java生成字节码文件的方法
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
"com.test.$proxy0.class", new Class[]{Teacher.class}, Modifier.FINAL);
// 输出文件到本地
FileOutputStream out = new FileOutputStream(new File("/temp/$Proxy0.class"));
out.write(proxyClassFile);
out.flush();
out.close();
用java反编译软件打开生成的$Proxy0.class文件,内容如下:
package com.test.$proxy0;
import com.demos.java.basedemo.proxy.bean.Teacher;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class class extends Proxy
implements Teacher
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public class(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void teach()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.demos.java.basedemo.proxy.bean.Teacher").getMethod("teach", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
可以看出,代理类不仅代理了teach()这个方法,还代理了toString()、equals()和hashCode()方法,因为java中所有的类都是继承Object类的,所以自然有这些方法。其中还有一个地方要特别注意:生成的代理类继承了Proxy这个类,因此它只能通过实现接口来代理其他的类
,现在知道为什么Java动态代理只能代理接口了。
既然都讲到这个份上了,当然不能不继续深入了。接下来是真正压轴的环节,实现自己的动态代理类。
手动实现动态代理
首先分析一下实现动态代理需要的步骤:
1.拼接java类实现,并生成class字节码
2.加载class字节码到JVM
3.实现自己的InvocationHandler处理器
4.对外提供接口
既然知道了Java动态代理的原理,我们不妨借鉴Java生成的class文件格式,同时去掉默认继承的Proxy,使得我们自己的动态代理既可以代理接口,也可以代理类。先写出代理类的格式(假设代理的是类TeacherChan):
public class $Proxy0 extends TeacherChan {
private InvocationHandler handler;
private static Method m0;
static {
try {
// 利用反射获取TeacherChan的teach()方法
m0 = TeacherChan.class.getMethod("teach", new Class[]{});
} catch (NoSuchMethodException ne) {
throw new NoSuchMethodError(ne.getMessage());
}
}
// 构造方法中传入代理类处理器
public $Proxy0(InvocationHandler handler) {
this.handler = handler;
}
public void teach() {
try {
// 收集teach()方法传入的参数,此处参数为空
Object[] args = new Object[]{};
// 执行代理类的teach()
Object result = handler.invoke(this, m0, args);
// 如果有返回值,此处要返回result
} catch (Error|RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
}
好了,生成类的格式大概就是这样设计,实际编写代码时需要处理参数、返回值和异常的情况,略微有点繁琐。下面是动态类生成器:
public class MyProxyGenerator {
// 换行符
public static final String LINE_SEPARATOR = "\r\n";
// 动态代理类包名
public static final String PROXY_CLASS_PACKAGE = "com.demos.proxy";
// 动态代理类名前缀
public static final String PROXY_CLASS_NAME_PREFIX = "$Proxy";
// 动态代理类文件索引
public static final AtomicLong INDEX_GENERATOR = new AtomicLong();
// 动态代理生成文件临时目录
public static final String PROXY_CLASS_FILE_PATH = "/temp";
/**
* 生成代理类并加载到JVM
* @param clazz
* @param methods
* @throws Exception
*/
public static Class<?> generateAndLoadProxyClass(Class<?> clazz, Method[] methods) throws Exception {
long index = INDEX_GENERATOR.getAndIncrement();
// 代理类类名
String className = PROXY_CLASS_NAME_PREFIX + index;
String fileName = PROXY_CLASS_FILE_PATH + File.separator + className + ".java";
FileWriter writer = null;
try {
// 生成.java文件
writer = new FileWriter(new File(fileName));
writer.write(generateClassCode(PROXY_CLASS_PACKAGE, className, clazz, methods));
writer.flush();
// 编译.java文件
compileJavaFile(fileName);
// 加载class到JVM
String classPath = PROXY_CLASS_FILE_PATH + File.separator + className + ".class";
Class<?> proxyClass = MyClassLoader.getInstance().findClass(classPath, PROXY_CLASS_PACKAGE + "." + className);
return proxyClass;
} finally {
if (writer != null) {
writer.close();
}
}
}
/**
* 编译.java文件
* @param fileName
* @throws IOException
*/
private static void compileJavaFile(String fileName) throws IOException {
compileByTools(fileName);
// compileByExec(fileName);
}
/**
* 使用Runtime执行javac命令
* 注意: 需要指定classpath, 否则找不到依赖的类
* 建议使用compileByTools()
* @param fileName
* @throws IOException
*/
@Deprecated
private static void compileByExec(String fileName) throws IOException {
// 获取当前的classpath
String classpath = MyProxyGenerator.class.getResource("/").getPath();
// 运行命令: javac -classpath ${classpath} ${filepath}
String command = "javac -classpath " + classpath + " " + fileName;
Process process = Runtime.getRuntime().exec(command);
// 等待执行, 并输出错误日志
try {
InputStream errorStream = process.getErrorStream();
InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
int exitVal = process.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 使用JDK自带的JavaCompiler
* @param fileName
* @throws IOException
*/
private static void compileByTools(String fileName) throws IOException {
// 获取系统Java编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 获取标准文件管理器实例
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
try {
Iterable units = fileManager.getJavaFileObjects(fileName);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
task.call();
} finally {
fileManager.close();
}
}
/**
* 拼接 class 代码片段
* @param packageName 代理类包名
* @param clazz 要代理的类型
* @return
*/
private static String generateClassCode(String packageName, String className, Class<?> clazz, Method[] methods) throws Exception {
StringBuilder classCodes = new StringBuilder();
/*--------------------包名和依赖 start--------------------*/
classCodes.append("package ").append(packageName).append(";").append(LINE_SEPARATOR);
classCodes.append(LINE_SEPARATOR);
classCodes.append("import java.lang.reflect.*;").append(LINE_SEPARATOR);
classCodes.append(LINE_SEPARATOR);
/*--------------------包名和依赖 start--------------------*/
/*--------------------类定义 start--------------------*/
classCodes.append("public class ").append(className);
if (clazz.isInterface()) {
classCodes.append(" implements ");
} else {
classCodes.append(" extends ");
}
classCodes.append(clazz.getName()).append(" {").append(LINE_SEPARATOR);
classCodes.append(LINE_SEPARATOR);
/*--------------------类定义 end--------------------*/
/*--------------------声明变量InvocationHandler start--------------------*/
classCodes.append("private InvocationHandler handler;").append(LINE_SEPARATOR);
classCodes.append(LINE_SEPARATOR);
/*--------------------声明变量InvocationHandler end--------------------*/
/*--------------------声明代理方法 start--------------------*/
for (int i = 0; i < methods.length; i++) {
classCodes.append("private static Method m").append(i).append(";").append(LINE_SEPARATOR);
}
classCodes.append(LINE_SEPARATOR);
/*--------------------声明代理方法 end--------------------*/
/*--------------------代理方法对象初始化 start--------------------*/
classCodes.append("static {").append(LINE_SEPARATOR);
classCodes.append(" ").append("try {").append(LINE_SEPARATOR);
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
classCodes.append(" ").append(" ").append("m").append(i).append(" = ").append(clazz.getName())
.append(".class.getMethod(\"").append(method.getName()).append("\", new Class[]{");
// 方法参数
Parameter[] params = method.getParameters();
if (params.length != 0) {
for (int j = 0; j < params.length; j++) {
if (j != 0) {
classCodes.append(", ");
}
Parameter param = params[j];
classCodes.append(param.getType().getName()).append(".class");
}
}
classCodes.append("});").append(LINE_SEPARATOR);
}
classCodes.append(" ").append("} catch (NoSuchMethodException ne) {").append(LINE_SEPARATOR);
classCodes.append(" ").append(" ").append("throw new NoSuchMethodError(ne.getMessage());").append(LINE_SEPARATOR);
classCodes.append(" ").append("}").append(LINE_SEPARATOR);
classCodes.append("}").append(LINE_SEPARATOR);
classCodes.append(LINE_SEPARATOR);
/*--------------------代理方法对象初始化 end--------------------*/
/*--------------------定义构造函数 start--------------------*/
classCodes.append("public ").append(className).append("(InvocationHandler handler) {").append(LINE_SEPARATOR);
classCodes.append(" ").append("this.handler = handler;").append(LINE_SEPARATOR);
classCodes.append("}").append(LINE_SEPARATOR);
classCodes.append(LINE_SEPARATOR);
/*--------------------定义构造函数 end--------------------*/
/*--------------------填充其他函数 start--------------------*/
classCodes.append(generateMethodCode(clazz, methods));
/*--------------------填充其他函数 end--------------------*/
// 类结束
classCodes.append("}").append(LINE_SEPARATOR);
return classCodes.toString();
}
/**
* 拼接 method 代码片段
* @param clazz
* @param methods
* @return
* @throws Exception
*/
private static String generateMethodCode(Class<?> clazz, Method[] methods) throws Exception {
StringBuilder methodCodes = new StringBuilder();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
// 返回类型
String returnType = method.getReturnType().getName();
// 参数列表
Parameter[] params = method.getParameters();
// 异常列表
Class<?>[] exceptionTypes = method.getExceptionTypes();
/*--------------------方法定义 start--------------------*/
methodCodes.append("public ").append(returnType).append(" ").append(method.getName());
methodCodes.append("(");
// 填充参数
if (params.length != 0) {
for (int j = 0; j < params.length; j++) {
if (j != 0) {
methodCodes.append(", ");
}
Parameter param = params[j];
methodCodes.append(param.getType().getName()).append(" ").append(param.getName());
}
}
methodCodes.append(")");
// 填充异常
if (exceptionTypes.length != 0) {
methodCodes.append(" throws ");
for (int j = 0; j < exceptionTypes.length; j++) {
if (j != 0) {
methodCodes.append(", ");
}
methodCodes.append(exceptionTypes[j].getName());
}
}
methodCodes.append(" {").append(LINE_SEPARATOR);
/*--------------------方法定义 end--------------------*/
/*--------------------方法体 start--------------------*/
methodCodes.append(" ").append("try {").append(LINE_SEPARATOR);
// 方法参数
methodCodes.append(" ").append(" ").append("Object[] args = new Object[]{");
if (params.length != 0) {
for (int j = 0; j < params.length; j++) {
if (j != 0) {
methodCodes.append(", ");
}
Parameter param = params[j];
methodCodes.append(param.getName());
}
}
methodCodes.append("};").append(LINE_SEPARATOR);
// 执行InvocationHandler.invoke()
methodCodes.append(" ").append(" ").append("Object result = handler.invoke(this, m").append(i)
.append(", args);").append(LINE_SEPARATOR);
// 返回结果
if (!"void".equals(returnType)) {
methodCodes.append(" ").append(" ").append("return (").append(returnType).append(") result;").append(LINE_SEPARATOR);
}
// 异常处理
methodCodes.append(" ").append("} catch (Error|RuntimeException");
for (Class<?> exceptionType : exceptionTypes) {
methodCodes.append("|").append(exceptionType.getName());
}
methodCodes.append(" e) {").append(LINE_SEPARATOR);
methodCodes.append(" ").append(" ").append("throw e;").append(LINE_SEPARATOR);
methodCodes.append(" ").append("} catch (Throwable t) {").append(LINE_SEPARATOR);
methodCodes.append(" ").append(" ").append("throw new UndeclaredThrowableException(t);").append(LINE_SEPARATOR);
methodCodes.append(" ").append("}").append(LINE_SEPARATOR);
/*--------------------方法体 end--------------------*/
// 方法结束
methodCodes.append("}").append(LINE_SEPARATOR).append(LINE_SEPARATOR);
}
return methodCodes.toString();
}
}
实际上只是拼接前面给出的代理类实现而已,代码量有点大,但并不难理解。
下一步,实现自己的类加载器,来加载生成的class字节码:
public class MyClassLoader extends ClassLoader {
private static MyClassLoader loader;
private MyClassLoader() {
}
public static MyClassLoader getInstance() {
if (loader == null) {
synchronized (MyClassLoader.class) {
// 得到锁首先检查loader是否已经存在, 避免重复创建
if (loader == null) {
loader = new MyClassLoader();
}
}
}
return loader;
}
/**
* 加载class文件,并返回类型对象
*
* @param filePath
* @param className
* @return
* @throws ClassNotFoundException
*/
public Class<?> findClass(String filePath, String className) throws ClassNotFoundException {
try {
// 读取指定class文件的字节码
byte[] classBytes = Files.readAllBytes(Paths.get(filePath));
// 加载类并返回class类型对象
Class<?> clazz = defineClass(className, classBytes, 0, classBytes.length);
return clazz;
} catch (IOException e) {
e.printStackTrace();
}
throw new ClassNotFoundException(className);
}
}
到这一步,复杂的工作基本做完了,接下来只剩下自定义处理器类和对外接口了
自定义处理器类与Java动态代理的方式相同:
public class MyInvocationHandler implements InvocationHandler {
private Object proxied;
public MyInvocationHandler(Object object) {
this.proxied = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-------------------老师好[by 自定义动态代理]-------------------");
Object obj = method.invoke(proxied, args);
System.out.println("-------------------老师再见[by 自定义动态代理]-------------------");
return obj;
}
}
对外接口:
public class MyDynamicProxy {
public static <T> T newProxyInstance(Class<T> clazz, InvocationHandler handler) throws Exception {
// 要代理的方法: public & !final
Method[] proxyMethods = Arrays.stream(clazz.getMethods())
.filter(method -> !Modifier.isFinal(method.getModifiers()))
.collect(Collectors.toList())
.toArray(new Method[0]);
// 生成的代理类
Class<?> proxyClass = MyProxyGenerator.generateAndLoadProxyClass(clazz, proxyMethods);
// 代理类的构造方法
Constructor c = proxyClass.getConstructor(InvocationHandler.class);
// 创建代理类对象
Object proxyObj = c.newInstance(handler);
return (T) proxyObj;
}
}
搞定,测试一下效果:
TeacherChan proxy1 = MyDynamicProxy.newProxyInstance(
TeacherChan.class, new MyInvocationHandler(new TeacherChan()));
proxy1.teach();
TeacherCang proxy2 = MyDynamicProxy.newProxyInstance(
TeacherCang.class, new MyInvocationHandler(new TeacherCang()));
proxy2.teach();
输出:
-------------------老师好[by 自定义动态代理]-------------------
大家好,我是陈老师,我教大家摄影!
-------------------老师再见[by 自定义动态代理]-------------------
-------------------老师好[by 自定义动态代理]-------------------
大家好,我是苍老师,我教大家生物!
-------------------老师再见[by 自定义动态代理]-------------------
完美!大功告成!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。