continue;
} else {
/*
-
如果没有这个接口列表已经生成或者正在生成的代理类
-
需要去生成这些接口的代理类,将这些接口标记为待生成
*/
cache.put(key, pendingGenerationMarker);
break;
}
}while (true);
}
- 类继承关系:
Proxy 类是父类,这个规则适用于所有由 Proxy 创建的动态代理类(这也导致Java动态代理的缺陷,由于Java不支持多继承,所以无法实现对 class 的动态代理,只能对于 Interface 进行代理),该类实现了所有代理的一组接口,所以 Proxy 类能够被安全地类型转换到其所代理的某接口
- 代理类的根类 java.lang.Object 中的 hashCode(),equals()和().toString 方法同样会被分派到调用处理器 invoke 方法执行
创建一个动态代理类
public class serviceProxy implements InvocationHandler {
private Object target;
/**
-
绑定委托对象并返回一个代理对象
-
@param target 真实对象
-
@return 代理对象
*/
public Object bind(Object target, Class[] interfaces) {
this.target = target;
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/**
-
通过代理对象调用方法首先进入这个方法
-
@param proxy 代理对象
-
@param Method 方法,被调用方法
-
@param args 方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
- JDK动态代理
*/
Object result = null;
// 反射方法前调用
System.err.println("–反射方法前执行的方法–");
// 反射执行方法,相当于调用target.xXX()
result = method.invoke(target, args);
// 反射方法后调用
System.err.println("–反射方法后执行的方法–");
return result;
}
}
-
bind方法:
-
bind方法中的newProxyInstance方法,就是生成一个代理对象
-
第一个参数: 类加载器
-
第二个参数: 真实委托对象所实现的接口. 代理对象挂在那个接口下
-
第三个参数: this 代表当前 HelloServiceProxy 类, 也就是使用HelloServiceProxy作为对象的代理
-
invoke方法:
-
invoke方法有三个参数:
-
第一个 proxy 是代理对象
-
第二个是当前调用那个方法
-
第三个是方法的参数
ProxyTest
public class ProxyTest {
public static void main(String[] args) {
HelloServiceProxy proxy = new HelloServiceProxy();
HelloService service = new HelloServiceImpl();
// 绑定代理对象
service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});
service.sayHello(“user”);
}
}
-
Java编译器编译好Java文件后,产生.class文件在磁盘中:
-
class 文件是二进制文件,内容是只有 JVM 虚拟机能够识别的机器码
-
JVM 虚拟机读取字节码文件,取出二进制数据
-
加载到内存中,解析 .class 文件内的信息,生成对应的 Class 对象
-
加载class文件字节码到系统内,转换成class对象,然后再实例化:
-
定义一个类
-
自定义一个类加载器,用于将字节码转换成 class 对象
-
编译 .class 文件,在程序中读取字节码,然后转换成相应的class对象,再实例化
在运行期生成二进制字节码
-
在代码中,动态创建一个类:
-
由于 JVM 通过字节码的二进制信息加载类,因此在运行期的系统中,遵循 Java 编译系统组织 .class 文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类
-
可以使用开源框架在运行时期按照 Java 虚拟机规范对 class 文件的组织规则生成对应的二进制字节码. 比如 ASM,Javassist
ASM
-
ASM是一个Java字节码操控框架:
-
能够以二进制形式修改已有类或者动态生成类
-
ASM 在创建 class 字节码的过程中,操纵的级别是底层 JVM 汇编指令级别
-
ASM 可以直接产生二进制 class 文件,也可以在类被加载入Java虚拟机之前动态改变类行为
-
ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户的要求生成新类
-
通过ASM生成类的字节码:
-
使用 ASM 框架提供的 ClassWriter 接口,通过访问者模式进行动态创建 class 字节码
-
然后使用 Java 反编译工具 (JD_GUI) 打开硬盘上生成的 类.class 文件查看类信息
-
再使用定义的类加载器将 class 文件加载到内存中,然后创建 class 对象,并且实例化一个对象,调用 code 方法,查看 code 方法中的结果
-
至此表明: 在代码里生成字节码,并动态地加载成class对象,创建实例是完全可以实现的
Javassist
-
Javassis 是一个开源的分析,编辑和创建Java字节码的类库,已经加入JBoss应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架:
-
Javassist是JBoss一个子项目,主要优点在于简单快速
-
直接使用Java编码的形式,不需要虚拟机指令,就能改变类的结构或者动态生成类
Proxy类
// 映射表: 用于维护类装载器对象到其对应的代理类缓存
private static Map loaderToCache = new WeakHashMap();
// 标记: 用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object();
// 同步表: 记录已经被创建的动态代理类类型,主要通过方法isProxyClass进行相关判断
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
// 关联的调用处理器引用
protected InvocationHandler h;
newProxyInstance
- Proxy 静态方法 newProxyInstance:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
/*
- 检查关联调用处理器是否为空,如果是空则抛出异常
*/
if (h == null) {
throw new NullPointerException();
}
/*
- 获得与指定类型装载器和一组接口相关的代理类类型对象
*/
Class<?> cl = getProxyClass0(loader, interfaces);
/*
- 通过反射获取构造函数对象并生成代理类实例
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
SecurityManager sm = System.getSecurityManager();
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
/*
-
使用doPrivilege创建动态代理类实例
-
因为代理类实现可能需要特殊权限的非公共接口
*/
return AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
private static Object newInstance(Constructor<?> cons, InvocationHa
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
ndler h) {
try {
return cons.newInstance(new Object[] {h});
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantationException e) {
throw new InternalException(e.toString());
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalException(e.toString());
}
}
}
- 动态代理的真正的关键是在 getProxyClass0() 方法
-
通过getProxyClass0方法中生成具体的class文件的过程:
-
定义 path
-
将 class 文件写到指定的硬盘中
-
反编译生成的 class 文件
getProxyClass0()方法分为四个步骤:
- 对这组接口进行一定程度的安全检查:
1.1 接口类对象是否对类装载器可见
1.2 接口类对象与类装载器所识别的接口类对象完全相同
1.3 确保是interface类型而不是class类型.
for (int i = 0; i < interfaces.length; i++ ) {
/*
- 验证类加载器是否将此接口的名称解析为相同的类对象
*/
String interfaceName = interface[i].getName();
Class interfaceClass = null;
try {
/*
-
forName(String name, boolean initialize, ClassLoader loader)
-
Returns the Class object associated with the class or interface with the given string name,
-
using the given class loader
*/
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interface[i]) {
throw new IllegalArgumentException(interface[i] + “is not visible from class loader.”);
}
/*
- 验证类加载器得到的类对象是否是interface类型
*/
if (! interfaceClass.isInterface()) {
throw new IllegalArgumentException(interfaceClass.getName() + “is not an interface.”);
}
/*
- 验证类加载器得到的类对象接口不是一个重复的接口
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(“repeated interface:” + interface.getName());
}
interfaceSet.add(interfaceClass);
interfaceName[i] = interfaceName;
}
- 从 loaderToCache 映射表中获取以类装载器对象为关键字所对应的缓存表,如果不存在,就会创建一个新的缓存表并更新到 loaderToCahe 中:
2.1 loaderToCache 存放键值对 : 接口名字列表:动态生成的代理类的类对象的引用
2.2 当代理类正在被创建时,会临时进行保存 : 接口名字列表:pendingGenerationMarker
2.3 标记 pendingGenerationMarker 的作用是通知后续的同类请求(接口数组相同且组内接口排列顺序也相同)代理类正在被创建,请保持等待直至创建完成
/*
- 寻找类加载器的缓存表,如果没有就为类加载器创建代理类缓存
*/
Map cache;
synchronized (loaderToCache) {
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap();
loaderToCache = put(loader, cache);
}
}
do {
/*
- 以接口名字作为关键字获得对应的cache值
*/
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class)((Reference)value).get();
}
if (proxyClass != null) {
// 如果已经创建,直接返回
return proxyClass;
} else if (value == pendingGenerationMarker) {
// 代理类正在创建,保持等待
try {
cache.wait()
} catch (InterruptException e) {
}
// 等待被唤醒,继续循环并通过二次检查以确保创建完成,否则重新等待
continue;
} else {
// 标记代理类正在被创建
cache.put(key, pendingGenerationMarker);
// 跳出循环已进入创建过程
break;
}
} while(true)
- 动态创建代理类的class对象
/*
- 选择一个名字代理类来生成
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber ++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
- 验证类加载器中没有使用这个名字定义的类
*/
…
// 动态生成代理类的字节码数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
// 动态地定义新生成的代理类
proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
-
这里的类格式错误指的是生代理类代码中的错误
-
还有一些应用到代理类生成的参数的错误,比如一些虚拟机限制的超量
*/
throw new IllegalArgumentException(e.toString());
}
// 将生成的代理类对象记录到proxyClasses中
proxyClasses.put(proxyClass, null);
首先根据接口 public 与否的规则生成代理类的名称 - $ProxyN 格式,然后动态生成代理类. 所有的代码生成工作由 ProxyGenerator 完成,该类在 rt.jar 中,需要进行反编译
public static byte[] generateProxyClass(final String name, Class[] interfaces) {
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 动态生成代理类的字节码
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Void run() {
try{
FileOutputStream file = new FileOutputStream(doToSlash(name) + “.class”);
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(“I/O exception saving generated file :” + e);
}
}
}
);
}
// 返回代理类的字节码
return classFile;
}
- 代码生成过程进入结尾部分,根据结果更新缓存表. 如果代理类成功生成则将代理类的类对象引用更新进缓存表,否则清除缓存表中对应的关键值,最后唤醒所有可能的正在等待的线程
finally {
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass;
- Proxy 角色在执行代理业务的时候,就是在调用真正业务之前或者之后完成一些额外的功能
-
代理类就是在调用真实角色的方法之前或者之后做一些额外的业务
-
为了构造具有通用性和简单性的代理类,可以将所有的触发真实角色动作交给一个触发管理器,让这个管理器统一管理触发,这个触发管理器就是 InvocationHandler
-
动态代理工作的基本工作模式:
-
将方法功能的实现交给 InvocationHandler 角色
-
外接对 Proxy 角色中每一个的调用 ,Proxy 角色都会交给 InvocationHandler 来处理
-
InvocationHandle r则调用具体对象角色的方法
- 在这种模式中,代理Proxy和RealSubject应该实现相同的的
public方法,有两种方式:
-
一个比较直观的方式: 就是定义一个功能接口,然后让 Proxy 和 RealSubject 来实现这个接口 ( JDK 中的动态代理机制 - Java动态代理机制 )
-
比较隐晦的方式: 通过继承实现 Proxy 继承 RealSubject. 因为Proxy继承自RealSubject, 这样Proxy则拥有了RealSubject的功能 ,Proxy 还可以通过重写 RealSubject 中的方法来实现多态( cglib )
JDK动态代理机制
-
JDK动态代理机制通过接口为RealSubject创建一个动态代理对象:
-
获取 RealSubject 上的所有接口列表
-
确定要生成的代理类类名
-
根据需要实现的接口信息,在代码中动态创建该 Proxy 类的字节码
-
将对应的字节码转换成对应的 class 对象
-
创建 InvocationHandler, 用来处理 Proxy 所有方法调用
-
Proxy 的 class 对象,以创建的 handler 为参数,实例化一个 proxy
-
JDK动态代理实例:
-
定义两个接口Vehicle和Rechargeable
-
Vehicle接口表示交通工具类,有drive()方法
-
Rechargeable接口表示可充电,有recharge()方法
-
定义一个实现两个接口的类ElectricCar,类图如下:
- 创建ElectricCar的动态代理类:
/**
- 交通工具接口
*/
public interface Vehicle {
public void drive();
}
/**
- 充电接口
*/
public interface Rechargable {
public void recharge();
}
/**
-
电动车类
-
实现Rechargable, Vehicle接口
*/
public class ElectricCar implements Rechargable, Vehicle {
@Override
public void drive() {
System.out.println(“ElectricCar can drive.”);
}
@Override
public void recharge() {
System.out.println(“ElectricCar can recharge.”);
}
}
/**
- 动态代理类的触发器
*/
public class InvocationHandlerImpl implements InvocationHandler {
private ElectricCar car;
public InvocationHandlerImpl(Electric car) {
this.car = car;
}
@Override
public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
System.out.println(“正在调用方法:” + paramMethod.getName() + “…”);
paramMethod.invoke(car, null);
System.out.println(“方法” + paramMethod.getName() + “调用结束.”);
return null;
}
}
public class ProxyTest {
public static void main(String[] args) {
ElectricCar car = new ElectricCar();
// 获取对应的ClassLoader
ClassLoader classLoader = car.getClass().getClassLoader();
// 获取ElectricCar所实现的所有接口
Class[] interfaces = car.getClass().getInterfaces();
// 设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用
InvocationHandler handler = new InvocationHandlerImpl(car);
/*
-
创建代理对象在这个过程中:
-
a. JDK根据传入的参数信息动态地在内存中创建和 .class 等同的字节码
-
b. 根据相应的字节码转换成对应的class
-
c. 然后调用newInstance()创建实例
*/
Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);
Vehicle vehicle = (Vehicle) o;
vehicle.drive();
Rechargable rechargable = (Rechargable) o;
rechargable.recharge();
}
}
-
生成动态代理类的字节码并且保存到硬盘中:
-
JDK提供了 sun.misc.ProxyGenerator.generateProxyClass(String proxyName, calss[] interfaces) 底层方法来产生动态代理类的字节码
-
定义一个工具类,用来将生成的动态代理类保存到硬盘中:
public class proxyUtils {
/*
-
根据类信息,动态生成二进制字节码保存到硬盘中
-
默认是clazz目录下
-
@params clazz 需要生成动态代理类的类
-
@proxyName 动态生成代理类的名称
*/
public static void generateClassFile(Class clazz, String proxyName) {
// 根据提供的代理类名称和类信息,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(ProxyName, clazz.getInterfaces());
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
// 保留到硬盘中
out = new FileOutputStream(paths + proxyName + “.class”);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 修改代理类名称为 “ElectricCarProxy”, 并保存到硬盘,使用以下语句:
ProxyUtils.generateClassFile(car.getClass(), “ElectricCarProxy”);
这样将在 ElectricCar.class 同级目录下产生 ElectricCarProxy.class 文件
- 使用反编译工具 jd-gui.exe 打开,将会看到以下信息:
/**
-
生成的动态代理类的组织模式是继承Proxy类,然后实现需要实现代理的类上的所有接口
-
在实现过程中,是通过将所有的方法都交给InvocationHandler来处理
*/
public final class ElectricCarProxy extends Proxy implements Rechargable,Vehicle {
private static Method m1;
private static Method m3;
private static Method m4;
private static Method m0;
private static Method m2;
public ElectricCarProxy(InvocationHandler paramInvocationHandler) throws {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) throws {
try {
/*
- 方法功能的实现交给InvocationHandler处理
*/
return ((Boolean) this.h.invoke(this, m1, new Object[] {paramObject})).booleanValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new Undeclared ThrowableException(localThrowable);
}
}
public final void recharge() throws {
try {
/*
- 方法功能的实现交给InvocationHandler处理
*/
this.h.invoke(this, m3, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new Undeclared ThrowableException(localThrowable);
}
}
public final drive() throws {
try {
/*
- 方法实现交给InvocationHandler处理
*/
this.h.invoke(this, m4, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new Undeclared ThrowableException(localThrowable);
}
}
public final int hasCode() throws {
try {
/*
- 方法功能交给InvocationHandler处理
*/
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new Undeclared ThrowableException(localThrowable);
}
}
public final String toString() throws {
try {
/*
- 方法功能实现交给InvocationHandler处理
*/
return (String)this.h.invoke(this, m2, null);
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new Undeclared ThrowableException(localThrowable);
}
}
static {
try {
/*
-
为每一个需要方法对象
-
当调用相应的方法时,分别将方法对象作为参数传递给InvocationHandler处理
*/
m1 = Class.forName(“java.lang.Object”).getMethod(“equals”, new Class[] { Class.forName(“java.lang.Object”) });
m3 = Class.forName(“com.oxford.proxy.Rechargable”).getMethod(“recharge”, new Class[0]);
m4 = Class.forName(“com.oxford.proxy.Vehicle”).getMethod(“drive”, new Class[0]);
m0 = Class.forName(“java.lang.Object”).getMethod(“hasCode”, new Class[0]);
m2 = Class.forName(“java.lang.Object”).getMethod(“toString”, new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessge());
}
}
}