我实在接触spring的时候才接触到代理这个东西的,一直想整理一下笔记。
什么是代理模式:代理模式是通过代理对象访问目标对象,这样可以在目标对象基础上增强额外的功能。简单来说就是要创建一个新的对象,我们通过这个新的代理对象来操作目标对象。
例如我们租房子,我们可以直接和房东谈,房东其实就是我们的目标对象。但是有时候房东没空理你,所以他们就委托一个中介公司来代他和我们谈,这时候中介公司就可能会搞小动作,例如水电费收贵点,房租收贵1000,这就是所谓的增强操作。
代理的实现有4种:静态代理、jdk代理、cglib代理、Aspectj
1、静态代理
静态代理其实就是你自己写一个代理对象的类。就像下面的 Intermediary
package com.hongcheng.dubbo_api.producer; /** 房东,一个接口 */ interface Landlord{ /** 出租房子 */ void rentHouse(); } /** 包租婆,具体实现 */ class LandlordWoman implements Landlord{ /** 出租房子 */ @Override public void rentHouse(){ System.out.println("包租婆 ====> 房子800租给你了"); } } /** * 中介公司,这就是代理类 * */ class Intermediary implements Landlord{ private Landlord landlord; public Intermediary(Landlord landlord) { this.landlord = landlord; } /** 出租房子 */ @Override public void rentHouse() { System.out.println("中介公司 ====> 房子1000出租,我要赚200"); this.landlord.rentHouse(); } } /** * 测试 * */ public class StaticProxy { public static void main(String[] args) { // 要有一个包租婆 Landlord landlordWoman = new LandlordWoman(); // 包租婆委托给中介公司 Intermediary intermediary = new Intermediary(landlordWoman); // 中介公司出租房子给各位韭菜 intermediary.rentHouse(); } }
静态代理优点:
- 可以对目标类的每个方法都做不同的处理,进行更细致的处理。
静态代理缺点:
- 必须手动重写目标类的所有方法,如果目标类方法过多,编写会很麻烦。
- 如果目标类的方法修改了,代理类也必须同步修改,维护麻烦。
2、 jdk代理
jdk代理这是一种动态代理,不需要我们自己去撸这个一个代理类,由jdk的api来帮我们创建这个类,我们只是要设计一下要怎么代理就好
package com.hongcheng.dubbo_api; import junit.framework.TestCase; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** 房东,一个接口 */ interface Landlord{ /** 出租房子 */ void rentHouse(); } /** 包租婆,具体实现 */ class LandlordWoman implements Landlord { /** 出租房子 */ @Override public void rentHouse(){ System.out.println("包租婆 ====> 房子800租给你了"); } } /** * 测试 * */ public class AppTest extends TestCase { public static void main(String[] args) throws IOException { /** 我们的目标对象 */ LandlordWoman hongcheng = new LandlordWoman(); /** 我们目标对象的类加载器 */ ClassLoader classLoader = hongcheng.getClass().getClassLoader(); /** 我们目标对象实现的接口 */ Class>[] interfaces = hongcheng.getClass().getInterfaces(); /*** * 创建代理对象 * @param classLoader 类加载器,一般用我们目标对象的类加载器 * @param interfaces 我们目标对象实现的接口 * @param InvocationHandler 我们要对目标对象所有方法做的增强处理 * */ Object instance = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { /*** * 我们对目标对象的所有方法做的增强处理。 * @param proxy jdk帮我们创建的代理对象 * @param method 我们自己的目标对象的方法 * @param args 我们目标对象方法的调用参数 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理开始"); System.out.println("中介公司 ====> 房子1000出租,我要赚200");
// 这里要记住,invoke方法传入的对象是我们的目标对象,不是代理对象 Object invoke = method.invoke(hongcheng); System.out.println("代理结束,结果===>" + invoke); return invoke; } }); Landlord hong = (Landlord)instance; hong.rentHouse(); // 这里阻塞程序是为了用工具将代理对象的class弄成.class文件 System.in.read(); } }
jdk代理优点:
- 只需要写一个对所有方法的增强处理就可以,jdk的api会自动处理所有方法,不管后面你怎么更改目标类,代理类的创建的代码都不用改变。
jdk代理缺点:
- 不能更细致的处理个别方法,所有方法的增强处理都是一样的。
- 目标类必须实现一个接口
让我们来看看jdk帮我们生成的代理对象是咋样的先:
$Proxy0 就是我们的代理对象:
特点:
继承Proxy类,实现了我们的Landlord接口
每个方法的调用实际上都是调用我们写的那个匿名内部类 InvocationHandler 的invoke方法,将我们原本的方法和参数传入。
package com.hongcheng.dubbo_api; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; final class $Proxy0 extends Proxy implements Landlord { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) { super(var1); } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.hongcheng.dubbo_api.Landlord").getMethod("rentHouse"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } public final boolean equals(Object var1) { 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 String toString() { 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() { 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 rentHouse() { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }
匿名内部类,增强方法的实现。
package com.hongcheng.dubbo_api; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; final class AppTest$1 implements InvocationHandler { AppTest$1(LandlordWoman var1) { this.val$hongcheng = var1; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理开始"); System.out.println("中介公司 ====> 房子1000出租,我要赚200"); Object invoke = method.invoke(this.val$hongcheng); System.out.println("代理结束,结果===>" + invoke); return invoke; } }
3、cglib代理
cglib 就是 Code Generator Library ,是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。
要用cglib需要引入jar包,这里我用maven
cglib cglib 3.3.0
package com.hongcheng.dubbo_api; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.io.IOException; import java.lang.reflect.Method; /** 房东,一个接口 */ interface Landlord{ /** 出租房子 */ void rentHouse(); } /** 包租婆,具体实现 */ class LandlordWoman implements Landlord { /** 出租房子 */ @Override public void rentHouse(){ System.out.println("包租婆 ====> 房子800租给你了"); } public final void finalMethod(){ System.out.println("finalMethod ====> 执行了"); } public static void staticMethod(){ System.out.println("staticMethod ====> 执行了"); } public void privateMethod1(){ System.out.println("privateMethod1 ====> 执行了"); this.privateMethod2(); } private void privateMethod2(){ System.out.println("privateMethod2 ====> 执行了"); } } /*** * 测试 * */ public class CglibProxy { public static void main(String[] args) throws IOException { // 一个很重要的对象,用来生成代理对象,翻译过来就是“增强器”,和jdk代理的Proxy类似功能 Enhancer enhancer = new Enhancer(); // 设置代理对象的父类,这是必须步骤 enhancer.setSuperclass(LandlordWoman.class); // 设置回调,其实就是增强处理对象,每次调用代理的方法,都会回调调用intercept方法 enhancer.setCallback(new MethodInterceptor() { /** * @param proxyObj 代理对象 * @param method 目标类的方法 * @param args 方法的参数 * @param methodProxy 代理对象的方法 * */ @Override public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("代理开始"); System.out.println("中介公司 ====> 房子1000出租,我要赚200"); /** * methodProxy.invokeSuper(proxyObj, args); * 这种方法使用的是代理对象的方法调用,传入的也是代理对象,但最后还是调用我们自己的目标方法 * */ Object invoke = methodProxy.invokeSuper(proxyObj, args); /** * method.invoke(new LandlordWoman(), args); * 这种方法使用的是我们目标对象的方法调用,传入的是目标对象,需要自己传入一个目标类的实例 * */ // Object invoke2 = method.invoke(new LandlordWoman(), args); System.out.println("代理结束,结果===>" + invoke); return invoke; } }); // 创建代理对象 LandlordWoman proxy= (LandlordWoman) enhancer.create(); proxy.rentHouse(); proxy.finalMethod(); proxy.privateMethod1(); proxy.staticMethod(); // 这里阻塞程序是为了用工具将代理对象的class弄成.class文件 System.in.read(); } }
cglib代理优点:
- 只需要写一个对所有方法的增强处理就可以,cglib的api会自动处理所有方法,不管后面你怎么更改目标类,代理类的创建的代码都不用改变。
- 目标类可以不实现任务接口。
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类,性能上比较好
cglib代理缺点:
- 不能更细致的处理个别方法,所有方法的增强处理都是一样的。
- 目标类必须实现一个接口
- 代理的类不能为final,否则报错
- 目标对象的方法如果为final或static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
看看他生成后的代理对象内容是怎样,代码有点长
代理对象是直接继承自我们的目标对象的。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.hongcheng.dubbo_api; import java.lang.reflect.Method; import net.sf.cglib.core.ReflectUtils; import net.sf.cglib.core.Signature; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Factory; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class LandlordWoman$$EnhancerByCGLIB$$df42a67d extends LandlordWoman implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$rentHouse$0$Method; private static final MethodProxy CGLIB$rentHouse$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$privateMethod1$1$Method; private static final MethodProxy CGLIB$privateMethod1$1$Proxy; private static final Method CGLIB$equals$2$Method; private static final MethodProxy CGLIB$equals$2$Proxy; private static final Method CGLIB$toString$3$Method; private static final MethodProxy CGLIB$toString$3$Proxy; private static final Method CGLIB$hashCode$4$Method; private static final MethodProxy CGLIB$hashCode$4$Proxy; private static final Method CGLIB$clone$5$Method; private static final MethodProxy CGLIB$clone$5$Proxy; public LandlordWoman$$EnhancerByCGLIB$$df42a67d() { CGLIB$BIND_CALLBACKS(this); } static { CGLIB$STATICHOOK1(); } public final boolean equals(Object var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy); return var2 == null ? false : (Boolean)var2; } else { return super.equals(var1); } } public final String toString() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString(); } public final int hashCode() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy); return var1 == null ? 0 : ((Number)var1).intValue(); } else { return super.hashCode(); } } protected final Object clone() throws CloneNotSupportedException { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone(); } public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); LandlordWoman$$EnhancerByCGLIB$$df42a67d var10000 = new LandlordWoman$$EnhancerByCGLIB$$df42a67d; switch(var1.length) { case 0: var10000.(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; default: throw new IllegalArgumentException("Constructor not found"); } } public Object newInstance(Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); LandlordWoman$$EnhancerByCGLIB$$df42a67d var10000 = new LandlordWoman$$EnhancerByCGLIB$$df42a67d(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public Object newInstance(Callback var1) { CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1}); LandlordWoman$$EnhancerByCGLIB$$df42a67d var10000 = new LandlordWoman$$EnhancerByCGLIB$$df42a67d(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; } public void setCallback(int var1, Callback var2) { switch(var1) { case 0: this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2; default: } } public final void rentHouse() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$rentHouse$0$Method, CGLIB$emptyArgs, CGLIB$rentHouse$0$Proxy); } else { super.rentHouse(); } } public final void privateMethod1() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$privateMethod1$1$Method, CGLIB$emptyArgs, CGLIB$privateMethod1$1$Proxy); } else { super.privateMethod1(); } } public void setCallbacks(Callback[] var1) { this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0]; } public Callback getCallback(int var1) { CGLIB$BIND_CALLBACKS(this); MethodInterceptor var10000; switch(var1) { case 0: var10000 = this.CGLIB$CALLBACK_0; break; default: var10000 = null; } return var10000; } public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0}; } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); } final int CGLIB$hashCode$4() { return super.hashCode(); } final boolean CGLIB$equals$2(Object var1) { return super.equals(var1); } final Object CGLIB$clone$5() throws CloneNotSupportedException { return super.clone(); } final String CGLIB$toString$3() { return super.toString(); } public static MethodProxy CGLIB$findMethodProxy(Signature var0) { String var10000 = var0.toString(); switch(var10000.hashCode()) { case -1702390808: if (var10000.equals("privateMethod1()V")) { return CGLIB$privateMethod1$1$Proxy; } break; case -508378822: if (var10000.equals("clone()Ljava/lang/Object;")) { return CGLIB$clone$5$Proxy; } break; case 1194948270: if (var10000.equals("rentHouse()V")) { return CGLIB$rentHouse$0$Proxy; } break; case 1826985398: if (var10000.equals("equals(Ljava/lang/Object;)Z")) { return CGLIB$equals$2$Proxy; } break; case 1913648695: if (var10000.equals("toString()Ljava/lang/String;")) { return CGLIB$toString$3$Proxy; } break; case 1984935277: if (var10000.equals("hashCode()I")) { return CGLIB$hashCode$4$Proxy; } } return null; } static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; Class var0 = Class.forName("com.hongcheng.dubbo_api.LandlordWoman$$EnhancerByCGLIB$$df42a67d"); Class var1; Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$2$Method = var10000[0]; CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2"); CGLIB$toString$3$Method = var10000[1]; CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3"); CGLIB$hashCode$4$Method = var10000[2]; CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4"); CGLIB$clone$5$Method = var10000[3]; CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5"); var10000 = ReflectUtils.findMethods(new String[]{"rentHouse", "()V", "privateMethod1", "()V"}, (var1 = Class.forName("com.hongcheng.dubbo_api.LandlordWoman")).getDeclaredMethods()); CGLIB$rentHouse$0$Method = var10000[0]; CGLIB$rentHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "rentHouse", "CGLIB$rentHouse$0"); CGLIB$privateMethod1$1$Method = var10000[1]; CGLIB$privateMethod1$1$Proxy = MethodProxy.create(var1, var0, "()V", "privateMethod1", "CGLIB$privateMethod1$1"); } final void CGLIB$rentHouse$0() { super.rentHouse(); } private static final void CGLIB$BIND_CALLBACKS(Object var0) { LandlordWoman$$EnhancerByCGLIB$$df42a67d var1 = (LandlordWoman$$EnhancerByCGLIB$$df42a67d)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if (var10000 == null) { return; } } var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; } } final void CGLIB$privateMethod1$1() { super.privateMethod1(); } }
增强内部类的代码
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.hongcheng.dubbo_api; import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; final class CglibProxy$1 implements MethodInterceptor { CglibProxy$1() { } public Object intercept(Object proxyObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("代理开始"); System.out.println("中介公司 ====> 房子1000出租,我要赚200"); Object invoke = methodProxy.invokeSuper(proxyObj, args); System.out.println("代理结束,结果===>" + invoke); return invoke; } }
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
4、Aspectj
Aspectj实际上他是一种静态代理,因为Aspectj是在编译代码的时候,将你的增强代码写入你的业务代码中,而不是去创建一个代理类。
- Aspectj并不是动态的在运行时生成代理类,而是在编译的时候就植入代码到class文件
- 由于是静态织入的,所以性能相对来说比较好
- Aspectj不受类的特殊限制,不管方法是private、或者static、或者final的,都可以代理
- Aspectj不会代理除了限定方法之外任何其他诸如toString(),clone()等方法
Aspectj的使用方法我实在是找不到,等以后找到了再补上吧
注意,像我们下面这种常常在spring中写的aop代码,使用的是jdk代理或者cglib代理,虽然用了Aspectj的注解,但仍然用的是jdk动态代理,或者cglib动态代理。
//声明这是一个组件 @Component //声明这是一个切面Bean @Aspect @Slf4j public class ServiceAspect { //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点 @Pointcut("execution(* com.hdj.learn.spring.aop.service..*(..))") public void aspect() { } /* * 配置前置通知,使用在方法aspect()上注册的切入点 * 同时接受JoinPoint切入点对象,可以没有该参数 */ @Before("aspect()") public void before(JoinPoint joinPoint) { log.info("before " + joinPoint); } //配置后置通知,使用在方法aspect()上注册的切入点 @After("aspect()") public void after(JoinPoint joinPoint) { log.info("after " + joinPoint); } //配置环绕通知,使用在方法aspect()上注册的切入点 @Around("aspect()") public void around(JoinPoint joinPoint) { long start = System.currentTimeMillis(); try { ((ProceedingJoinPoint) joinPoint).proceed(); long end = System.currentTimeMillis(); log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!"); } catch (Throwable e) { long end = System.currentTimeMillis(); log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage()); } } //配置后置返回通知,使用在方法aspect()上注册的切入点 @AfterReturning("aspect()") public void afterReturn(JoinPoint joinPoint) { log.info("afterReturn " + joinPoint); } //配置抛出异常后通知,使用在方法aspect()上注册的切入点 @AfterThrowing(pointcut = "aspect()", throwing = "ex") public void afterThrow(JoinPoint joinPoint, Exception ex) { log.info("afterThrow " + joinPoint + "\t" + ex.getMessage()); } }
关于怎么获取动态代理生成的class对象的.class文件,参考下面这两篇文章吧:
https://www.cnblogs.com/dengrong/p/10622594.html
http://www.mamicode.com/info-detail-2242036.html
注意看怎么使用 java -classpath sa-jdi.jar "sun.jvm.hotspot.HSDB" 和 jps -l 就行了,其他方法使用了,都没卵用,就HSDB这个东西可以生成class文件