-
Javassist 动态代理与 Cglib 一样是通过动态生成继承自目标类的代理类实现;
-
与其他类似的字节码编辑器不同,Javassist提供两个级别的API:源级别和字节码级别。如果用户使用源级API,他们可以在不了解Java字节码规范的情况下编辑类文件。 整个API仅使用Java语言的词汇表进行设计。 您甚至可以以源文本的形式指定插入的字节码; Javassist即时编译它。
-
源级别:高级API,人类可读的计算机语言指令,如:CtClass
-
字节码级别: 低级API,二进制操作指令,如:ClassFileWriter、ClassFile
节码(Byte-code)是一种包含执行程序、由一序列 op 代码/数据对组成的二进制文件。字节码是一种中间码,它比机器码更抽象。
如果你想要产生一个简单的类文件,javassist.bytecode.ClassFileWriter可能提供了最好的API。它提供了比javassist.bytecode.ClassFile更快的速度,尽管这个API更小一些。
-
-
Javassist 使用了 MethodHandle 性能更好;
-
Javassist 动态代理是基于反射实现,最终调用:
method.invoke
; -
Javassist 可以通过 MethodFilter 可以过滤不必要的方法使字节码文件更精简;
目标类:ProxyTarget
public class ProxyTarget {
public void doSomething() {
System.out.println("do something...");
}
}
Javassist 代理工厂:JavassistProxyFactory,继承 ProxyFactory
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;
import java.lang.reflect.Method;
public class JavassistProxyFactory<T> extends ProxyFactory {
private T target;
public JavassistProxyFactory(T target) {
this.target = target;
}
//获取代理类
public T getProxy() throws IllegalAccessException, InstantiationException {
setSuperclass(target.getClass());
setDefaultFilter();
T proxy = (T) createClass().newInstance();
((ProxyObject) proxy).setHandler(getDefaultHandler());
return proxy;
}
//设置默认的方法过滤器,如果此处不做过滤,代理类中将会把 Object 的基础方法都重写并生成对应的代理方法
private void setDefaultFilter() {
this.setFilter((Method m) -> {
if (m.getName().equals("doSomething")) {
return true;
}
return false;
});
}
//为代理类设置处理句柄
private MethodHandler getDefaultHandler() {
return (self, thisMethod, proceed, args) -> {
System.out.println("before " + thisMethod.getName() + " execution...");
Object result = thisMethod.invoke(target, args);
System.out.println("after " + thisMethod.getName() + " execution...");
return result;
};
}
}
ProxyFactory::constructor
//是否使用缓存:默认true
public static volatile boolean useCache = true;
//是否在代理类中生成 writeReplace 方法,用于将代理实例转换为可序列化的对象
public static volatile boolean useWriteReplace = true;
//是否只代理 public 修饰的方法
public static boolean onlyPublicMethods = false;
public ProxyFactory() {
superClass = null;
interfaces = null;
methodFilter = null;
handler = null;
signature = null;
signatureMethods = null;
//方法签名是否以:getHandler:() 开始 (getMethods中使用)
//决定代理类是继承自 Proxy(true) | ProxyObject(false)
hasGetHandler = false;
thisClass = null;
//如果非 null,将代理类 Class 文件写入到指定目录
writeDirectory = null;
factoryUseCache = useCache;
factoryWriteReplace = useWriteReplace;
}
ProxyFactory::createClass
开始创建代理类 Class 对象
public Class<?> createClass() {
//signature默认为null
if (signature == null) {
computeSignature(methodFilter);
}
return createClass1(null);
}
ProxyFactory::computeSignature
- 获取所有的声明方法(包含所有父类、接口的 public / protected / private 方法)并排序
- 过滤方法:不能被 final / static / private 修饰,方法声明类和目标类需要同包,再根据自定义过滤器过滤
- 将过滤后的方法,通过标记在长度为方法列表长度 / 8 的 byte 数组中,1 byte = 8 bit,每个 bit 对应一个方法
private void computeSignature(MethodFilter filter) // throws CannotCompileException
{
makeSortedMethodList();
int l = signatureMethods.size();
int maxBytes = ((l + 7) >> 3);
signature = new byte[maxBytes];
for (int idx = 0; idx < l; idx++)
{
Method m = signatureMethods.get(idx).getValue();
int mod = m.getModifiers();
if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod)
&& isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) {
setBit(signature, idx);
}
}
}
ProxyFactory::isVisible
判断方法是否可见:
- private 修饰为不可见
- public / protected 修饰为可见
- 方法声明类和目标类包名相同为可见
private static boolean isVisible(int mod, String from, Member meth) {
if ((mod & Modifier.PRIVATE) != 0)
return false;
else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
return true;
else {
String p = getPackageName(from);
String q = getPackageName(meth.getDeclaringClass().getName());
if (p == null)
return q == null;
return p.equals(q);
}
}
ProxyFactory::setBit
将 byte 素组对应 bit 设置为 1,表明方法需要在代理类中生成
private void setBit(byte[] signature, int idx) {
int byteIdx = idx >> 3;
if (byteIdx < signature.length) {
int bitIdx = idx & 0x7;
int mask = 0x1 << bitIdx;
int sigByte = signature[byteIdx];
signature[byteIdx] = (byte)(sigByte | mask);
}
}
ProxyFactory::makeSortedMethodList
- 检查类是否可以继承,和确定代理类名前缀
- 获取所有的声明方法(包含所有父类、接口的 public / protected / private 方法)列表,并排序,每个元素为一个 Map.Entry<String key, Method value>,其中 key为:
String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); // see keyToDesc().
即:方法名 + : + 方法签名(包含返回值(java 1.5后加入),参数类型)
private void makeSortedMethodList() {
checkClassAndSuperName();
hasGetHandler = false; // getMethods() may set this to true.
Map<String,Method> allMethods = getMethods(superClass, interfaces);
signatureMethods = new ArrayList<Map.Entry<String,Method>>(allMethods.entrySet());
Collections.sort(signatureMethods, sorter);
}
ProxyFactory::checkClassAndSuperName
- 确定代理类名前缀,如果类名以 java. / jdk. 开始或者只允许生成 public 方法,代理类名前缀将被设置为:
"javassist.util.proxy." + basename.replace('.', '_');
- 检查类是否可以继承(是否被 final 修饰),如果不可继承直接抛出异常
private void checkClassAndSuperName() {
if (interfaces == null)
interfaces = new Class[0];
if (superClass == null) {
superClass = OBJECT_TYPE;
superName = superClass.getName();
basename = interfaces.length == 0 ? superName
: interfaces[0].getName();
} else {
superName = superClass.getName();
basename = superName;
}
if (Modifier.isFinal(superClass.getModifiers()))
throw new RuntimeException(superName + " is final");
// Since java.base module is not opened, its proxy class should be
// in a different (open) module. Otherwise, it could not be created
// by reflection.
if (basename.startsWith("java.") || basename.startsWith("jdk.") || onlyPublicMethods)
basename = packageForJavaBase + basename.replace('.', '_');
}
private static final String packageForJavaBase = "javassist.util.proxy.";
RuntimeSupport::makeDescriptor
拼接方法签名:(参数签名1,参数签名2...)返回类型签名
public static String makeDescriptor(Class<?>[] params, Class<?> retType) {
StringBuffer sbuf = new StringBuffer();
sbuf.append('(');
for (int i = 0; i < params.length; i++)
makeDesc(sbuf, params[i]);
sbuf.append(')');
if (retType != null)
makeDesc(sbuf, retType);
return sbuf.toString();
}
RuntimeSupport::makeDesc
根据各个类型拼接类型签名
private static void makeDesc(StringBuffer sbuf, Class<?> type) {
if (type.isArray()) {
sbuf.append('[');
makeDesc(sbuf, type.getComponentType());
}
else if (type.isPrimitive()) {
if (type == Void.TYPE)
sbuf.append('V');
else if (type == Integer.TYPE)
sbuf.append('I');
else if (type == Byte.TYPE)
sbuf.append('B');
else if (type == Long.TYPE)
sbuf.append('J');
else if (type == Double.TYPE)
sbuf.append('D');
else if (type == Float.TYPE)
sbuf.append('F');
else if (type == Character.TYPE)
sbuf.append('C');
else if (type == Short.TYPE)
sbuf.append('S');
else if (type == Boolean.TYPE)
sbuf.append('Z');
else
throw new RuntimeException("bad type: " + type.getName());
}
else
sbuf.append('L').append(type.getName().replace('.', '/'))
.append(';');
}
ProxyFactory::createClass1
开始生成代理类 Class 对象,默认 lookup = null; factoryUseCache = true 执行 createClass2(cl, lookup):
private Class<?> createClass1(Lookup lookup) {
Class<?> result = thisClass;
if (result == null) {
ClassLoader cl = getClassLoader();
synchronized (proxyCache) {
if (factoryUseCache)
createClass2(cl, lookup);
else
createClass3(cl, lookup);
result = thisClass;
// don't retain any unwanted references
thisClass = null;
}
}
return result;
}
ProxyFactory::createClass2
- 拼接缓存 key:
目标类名:接口名1:接口名2:signature字节数组转换的字符串:w
- 通过 key 去缓存中搜索,存在直接从缓存的 ProxyDetail 中获取 Class 对象返回
- 否则,通过 createClass3 动态生成 Class 对象,存入缓存并返回
private void createClass2(ClassLoader cl, Lookup lookup) {
String key = getKey(superClass, interfaces, signature, factoryWriteReplace);
/*
* Excessive concurrency causes a large memory footprint and slows the
* execution speed down (with JDK 1.5). Thus, we use a jumbo lock for
* reducing concrrency.
*/
// synchronized (proxyCache) {
Map<String,ProxyDetails> cacheForTheLoader = proxyCache.get(cl);
ProxyDetails details;
if (cacheForTheLoader == null) {
cacheForTheLoader = new HashMap<String,ProxyDetails>();
proxyCache.put(cl, cacheForTheLoader);
}
details = cacheForTheLoader.get(key);
if (details != null) {
Reference<Class<?>> reference = details.proxyClass;
thisClass = reference.get();
if (thisClass != null) {
return;
}
}
createClass3(cl, lookup);
details = new ProxyDetails(signature, thisClass, factoryWriteReplace);
cacheForTheLoader.put(key, details);
// }
}
ProxyFactory::getKey
拼接缓存 key:目标类名:接口名1:接口名2:signature字节数组转换的字符串:w
private static char[] hexDigits =
{ '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public String getKey(Class<?> superClass, Class<?>[] interfaces, byte[] signature, boolean useWriteReplace)
{
StringBuffer sbuf = new StringBuffer();
if (superClass != null){
sbuf.append(superClass.getName());
}
sbuf.append(":");
for (int i = 0; i < interfaces.length; i++) {
sbuf.append(interfaces[i].getName());
sbuf.append(":");
}
for (int i = 0; i < signature.length; i++) {
byte b = signature[i];
int lo = b & 0xf;
int hi = (b >> 4) & 0xf;
sbuf.append(hexDigits[lo]);
sbuf.append(hexDigits[hi]);
}
if (useWriteReplace) {
sbuf.append(":w");
}
return sbuf.toString();
}
ProxyFactory::createClass3
- 生成代理类类名
- 创建 ClassFile
- 根据 writeDirectory 取值决定是否将 Class 写入到指定存储目录
- 用 ClassLoader 加载 ClassFile 字节码
- 为 Class 对象的字段 _filter_signature 和 default_interceptor 设置值(不会在指定目录的 Class 文件中写入)
private void createClass3(ClassLoader cl, Lookup lookup) {
// we need a new class so we need a new class name
allocateClassName();
try {
ClassFile cf = make();
if (writeDirectory != null)
FactoryHelper.writeFile(cf, writeDirectory);
if (lookup == null)
thisClass = FactoryHelper.toClass(cf, getClassInTheSamePackage(), cl, getDomain());
else
thisClass = FactoryHelper.toClass(cf, lookup);
setField(FILTER_SIGNATURE_FIELD, signature);
// legacy behaviour : we only set the default interceptor static field if we are not using the cache
if (!factoryUseCache) {
setField(DEFAULT_INTERCEPTOR, handler);
}
}
catch (CannotCompileException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
ProxyFactory::make
创建 ClassFile 对象:
- 写入类定义:类名,继承父类,实现接口(Proxy | ProxyObject)
- 创建常量池 ConstPool
- 如果关闭使用缓存,添加:
public static MethodHandler default_interceptor;
- 添加:
private MethodHandler handler;
- 添加:
public static byte[] _filter_signature;
- 添加:
public static final long serialVersionUID;
- 根据目标类为代理类添加相应构造器
- 添加方法签名过滤后的方法
- 添加:private static 方法块加载时初始化类
- 添加:
public void setHandler(final MethodHandler handler)
方法 - 如果 hasGetHandler = false(默认为 false),添加
public MethodHandler getHandler()
方法 - 如果 factoryWriteReplace = true(默认为 true),添加
Object writeReplace()
方法
private ClassFile make() throws CannotCompileException {
ClassFile cf = new ClassFile(false, classname, superName);
cf.setAccessFlags(AccessFlag.PUBLIC);
setInterfaces(cf, interfaces, hasGetHandler ? Proxy.class : ProxyObject.class);
ConstPool pool = cf.getConstPool();
// legacy: we only add the static field for the default interceptor if caching is disabled
if (!factoryUseCache) {
FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE);
finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
cf.addField(finfo);
}
// handler is per instance
FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE);
finfo2.setAccessFlags(AccessFlag.PRIVATE);
cf.addField(finfo2);
// filter signature is per class
FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE);
finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC);
cf.addField(finfo3);
// the proxy class serial uid must always be a fixed value
FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE);
finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL);
cf.addField(finfo4);
// HashMap allMethods = getMethods(superClass, interfaces);
// int size = allMethods.size();
makeConstructors(classname, cf, pool, classname);
List<Find2MethodsArgs> forwarders = new ArrayList<Find2MethodsArgs>();
int s = overrideMethods(cf, pool, classname, forwarders);
addClassInitializer(cf, pool, classname, s, forwarders);
addSetter(classname, cf, pool);
if (!hasGetHandler)
addGetter(classname, cf, pool);
if (factoryWriteReplace) {
try {
cf.addMethod(makeWriteReplace(pool));
}
catch (DuplicateMemberException e) {
// writeReplace() is already declared in the super class/interfaces.
}
}
thisClass = null;
return cf;
}
ProxyFactory::overrideMethods
添加方法签名后的方法,byte[] signature 对应 bit 值为1的被添加到 ClassFile 中
private int overrideMethods(ClassFile cf, ConstPool cp, String className, List<Find2MethodsArgs> forwarders)
throws CannotCompileException
{
String prefix = makeUniqueName("_d", signatureMethods);
Iterator<Map.Entry<String,Method>> it = signatureMethods.iterator();
int index = 0;
while (it.hasNext()) {
Map.Entry<String,Method> e = it.next();
if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_5 || !isBridge(e.getValue()))
if (testBit(signature, index)) {
override(className, e.getValue(), prefix, index,
keyToDesc(e.getKey(), e.getValue()), cf, cp, forwarders);
}
index++;
}
return index;
}
ProxyFactory::nameGenerator
生成代理类名,规则为:basename + "_$$_jvst" + 三位(this.hashCode() & 0xfff) + "_" + counter
public static UniqueName nameGenerator = new UniqueName() {
private final String sep = "_$$_jvst" + Integer.toHexString(this.hashCode() & 0xfff) + "_";
private int counter = 0;
@Override
public String get(String classname) {
return classname + sep + Integer.toHexString(counter++);
}
};
FactoryHelper::toClass
- 将 ClassFile 转换为字节流
- 根据类名和字节流等生成 Class 对象
DefineClassHelper.toClass
和DefineClassHelper.toPublicClass
通过MethodHandle
(Jdk7新特性,引入java.lang.invoke
包)更加灵活和轻量级。
public static Class<?> toClass(ClassFile cf, Class<?> neighbor,
ClassLoader loader, ProtectionDomain domain)
throws CannotCompileException
{
try {
byte[] b = toBytecode(cf);
if (ProxyFactory.onlyPublicMethods)
return DefineClassHelper.toPublicClass(cf.getName(), b);
else
return DefineClassHelper.toClass(cf.getName(), neighbor,
loader, domain, b);
}
catch (IOException e) {
throw new CannotCompileException(e);
}
}
public static Class<?> toClass(ClassFile cf, java.lang.invoke.MethodHandles.Lookup lookup)
throws CannotCompileException
{
try {
byte[] b = toBytecode(cf);
return DefineClassHelper.toClass(lookup, b);
}
catch (IOException e) {
throw new CannotCompileException(e);
}
}
FactoryHelper::writeFile
将 ClassFile 写入到指定存储目录
public static void writeFile(ClassFile cf, String directoryName)
throws CannotCompileException {
try {
writeFile0(cf, directoryName);
}
catch (IOException e) {
throw new CannotCompileException(e);
}
}
private static void writeFile0(ClassFile cf, String directoryName)
throws CannotCompileException, IOException {
String classname = cf.getName();
String filename = directoryName + File.separatorChar
+ classname.replace('.', File.separatorChar) + ".class";
int pos = filename.lastIndexOf(File.separatorChar);
if (pos > 0) {
String dir = filename.substring(0, pos);
if (!dir.equals("."))
new File(dir).mkdirs();
}
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
new FileOutputStream(filename)));
try {
cf.write(out);
}
catch (IOException e) {
throw e;
}
finally {
out.close();
}
}
}
测试类
public class JavassistDynamicProxyTester {
public static void main(String args[]) throws Exception {
ProxyTarget target = new ProxyTarget();
JavassistProxyFactory<ProxyTarget> factory = new JavassistProxyFactory<>(target);
//设置动态生成代理类文件的输出目录
factory.writeDirectory = "D:/javassist-dynamic-proxy";
ProxyTarget proxy = factory.getProxy();
proxy.doSomething();
}
}
目录结构
D:\javassist-dynamic-proxy>
D:.
| javassist-3.24.1-GA.jar
| JavassistDynamicProxyTester.java
| JavassistProxyFactory.java
| procyon-decompiler-0.5.34.jar
| ProxyTarget.java
| ProxyTarget_$$_jvst45a_0.class
执行命令
D:\javassist-dynamic-proxy>SET CLASSPATH=%CLASSPATH%;./javassist-3.24.1-GA.jar
// -encoding UTF-8 防止中文GBK编码导致 error
D:\javassist-dynamic-proxy>javac -encoding UTF-8 -d ./ ./*.java
D:\javassist-dynamic-proxy>java JavassistDynamicProxyTester
D:\javassist-dynamic-proxy>java -jar procyon-decompiler-0.5.34.jar ProxyTarget_$$_jvst45a_0.class -o .
procyon反编译代理类 Class 文件:ProxyTarget_$$_javassist_0.java
import java.io.ObjectStreamException;
import javassist.util.proxy.RuntimeSupport;
import java.lang.reflect.Method;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;
//
// Decompiled by Procyon v0.5.34
//
public class ProxyTarget_$$_jvst45a_0 extends ProxyTarget implements ProxyObject
{
private MethodHandler handler;
public static byte[] _filter_signature;
public static final long serialVersionUID;
private static Method[] _methods_;
public ProxyTarget_$$_jvst45a_0() {
this.handler = RuntimeSupport.default_interceptor;
}
public final void _d1doSomething() {
super.doSomething();
}
@Override
public final void doSomething() {
final Method[] methods_ = ProxyTarget_$$_jvst45a_0._methods_;
this.handler.invoke((Object)this, methods_[2], methods_[3], new Object[0]);
}
static throws ClassNotFoundException {
final Method[] methods_ = new Method[26];
RuntimeSupport.find2Methods((Class)Class.forName("ProxyTarget_$$_jvst45a_0"), "doSomething", "_d1doSomething", 2, "()V", methods_);
ProxyTarget_$$_jvst45a_0._methods_ = methods_;
serialVersionUID = -1L;
}
public void setHandler(final MethodHandler handler) {
this.handler = handler;
}
public MethodHandler getHandler() {
return this.handler;
}
Object writeReplace() throws ObjectStreamException {
return RuntimeSupport.makeSerializedProxy((Object)this);
}
}
RuntimeSupport::default_interceptor
代理类构造器中指定默认的拦截器:直接调用:proceed.invoke(self, args);
public static MethodHandler default_interceptor = new DefaultMethodHandler();
static class DefaultMethodHandler implements MethodHandler, Serializable {
/** default serialVersionUID */
private static final long serialVersionUID = 1L;
@Override
public Object invoke(Object self, Method m,
Method proceed, Object[] args)
throws Exception
{
return proceed.invoke(self, args);
}
};
RuntimeSupport::find2Methods
检索指定的方法名的两个方法,将它们存入 methods 对应索引 index (目标类方法) 和 index + 1 (代理类方法) 的位置
public static void find2Methods(Class<?> clazz, String superMethod,
String thisMethod, int index,
String desc, java.lang.reflect.Method[] methods)
{
methods[index + 1] = thisMethod == null ? null
: findMethod(clazz, thisMethod, desc);
methods[index] = findSuperClassMethod(clazz, superMethod, desc);
}
RuntimeSupport::makeSerializedProxy
将代理类实例转换为可用于流传输的序列化对象:
public static SerializedProxy makeSerializedProxy(Object proxy)
throws java.io.InvalidClassException
{
Class<?> clazz = proxy.getClass();
MethodHandler methodHandler = null;
if (proxy instanceof ProxyObject)
methodHandler = ((ProxyObject)proxy).getHandler();
else if (proxy instanceof Proxy)
methodHandler = ProxyFactory.getHandler((Proxy)proxy);
return new SerializedProxy(clazz, ProxyFactory.getFilterSignature(clazz), methodHandler);
}
Javassist
官方首页:http://www.javassist.org
教程:http://www.javassist.org/tutorial/tutorial.html
Github:https://github.com/jboss-javassist/javassist
参考:https://www.jianshu.com/p/43424242846b