这篇文章主要讲解了“类代理的方式有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“类代理的方式有哪些”吧!
五种类代理的方式
我们先定义出一个接口和相应的实现类,方便后续使用代理类在方法中添加输出信息。
「定义接口」
public interface IUserApi { String queryUserInfo(); }
「实现接口」
public class UserApi implements IUserApi { public String queryUserInfo() { return "沉淀、分享、成长,让自己和他人都能有所收获!"; } }
好!接下来我们就给这个类方法使用代理加入一行额外输出的信息。
0. 先补充一点反射的知识
@Test public void test_reflect() throws Exception { Class<UserApi> clazz = UserApi.class; Method queryUserInfo = clazz.getMethod("queryUserInfo"); Object invoke = queryUserInfo.invoke(clazz.newInstance()); System.out.println(invoke); }
1. JDK代理方式
public class JDKProxy { public static <T> T getProxy(Class clazz) throws Exception { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); return (T) Proxy.newProxyInstance(classLoader, new Class[]{clazz}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + " 你被代理了,By JDKProxy!"); return "沉淀、分享、成长,让自己和他人都能有所收获!"; } }); } } @Test public void test_JDKProxy() throws Exception { IUserApi userApi = JDKProxy.getProxy(IUserApi.class); String invoke = userApi.queryUserInfo(); logger.info("测试结果:{}", invoke); } /** * 测试结果: * * queryUserInfo 你被代理了,By JDKProxy! * 19:55:47.319 [main] INFO org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获! * * Process finished with exit code 0 */
2. CGLIB代理方式
public class CglibProxy implements MethodInterceptor { public Object newInstall(Object object) { return Enhancer.create(object.getClass(), this); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("我被CglibProxy代理了"); return methodProxy.invokeSuper(o, objects); } } @Test public void test_CglibProxy() throws Exception { CglibProxy cglibProxy = new CglibProxy(); UserApi userApi = (UserApi) cglibProxy.newInstall(new UserApi()); String invoke = userApi.queryUserInfo(); logger.info("测试结果:{}", invoke); } /** * 测试结果: * * queryUserInfo 你被代理了,By CglibProxy! * 19:55:47.319 [main] INFO org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获! * * Process finished with exit code 0 */
3. ASM代理方式
public class ASMProxy extends ClassLoader { public static <T> T getProxy(Class clazz) throws Exception { ClassReader classReader = new ClassReader(clazz.getName()); ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); classReader.accept(new ClassVisitor(ASM5, classWriter) { @Override public MethodVisitor visitMethod(int access, final String name, String descriptor, String signature, String[] exceptions) { // 方法过滤 if (!"queryUserInfo".equals(name)) return super.visitMethod(access, name, descriptor, signature, exceptions); final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); return new AdviceAdapter(ASM5, methodVisitor, access, name, descriptor) { @Override protected void onMethodEnter() { // 执行指令;获取静态属性 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); // 加载常量 load constant methodVisitor.visitLdcInsn(name + " 你被代理了,By ASM!"); // 调用方法 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); super.onMethodEnter(); } }; } }, ClassReader.EXPAND_FRAMES); byte[] bytes = classWriter.toByteArray(); return (T) new ASMProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance(); } } @Test public void test_ASMProxy() throws Exception { IUserApi userApi = ASMProxy.getProxy(UserApi.class); String invoke = userApi.queryUserInfo(); logger.info("测试结果:{}", invoke); } /** * 测试结果: * * queryUserInfo 你被代理了,By ASM! * 20:12:26.791 [main] INFO org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获! * * Process finished with exit code 0 */
场景:全链路监控、破解工具包、CGLIB、Spring获取类元数据等
点评:这种代理就是使用字节码编程的方式进行处理,它的实现方式相对复杂,而且需要了解Java虚拟机规范相关的知识。因为你的每一步代理操作,都是在操作字节码指令,例如:Opcodes.GETSTATIC、Opcodes.INVOKEVIRTUAL,除了这些还有小200个常用的指令。但这种最接近底层的方式,也是最快的方式。所以在一些使用字节码插装的全链路监控中,会非常常见。
4. Byte-Buddy代理方式
public class ByteBuddyProxy { public static <T> T getProxy(Class clazz) throws Exception { DynamicType.Unloaded<?> dynamicType = new ByteBuddy() .subclass(clazz) .method(ElementMatchers.<MethodDescription>named("queryUserInfo")) .intercept(MethodDelegation.to(InvocationHandler.class)) .make(); return (T) dynamicType.load(Thread.currentThread().getContextClassLoader()).getLoaded().newInstance(); } } @RuntimeType public static Object intercept(@Origin Method method, @AllArguments Object[] args, @SuperCall Callable<?> callable) throws Exception { System.out.println(method.getName() + " 你被代理了,By Byte-Buddy!"); return callable.call(); } @Test public void test_ByteBuddyProxy() throws Exception { IUserApi userApi = ByteBuddyProxy.getProxy(UserApi.class); String invoke = userApi.queryUserInfo(); logger.info("测试结果:{}", invoke); } /** * 测试结果: * * queryUserInfo 你被代理了,By Byte-Buddy! * 20:19:44.498 [main] INFO org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获! * * Process finished with exit code 0 */
5. Javassist代理方式
public class JavassistProxy extends ClassLoader { public static <T> T getProxy(Class clazz) throws Exception { ClassPool pool = ClassPool.getDefault(); // 获取类 CtClass ctClass = pool.get(clazz.getName()); // 获取方法 CtMethod ctMethod = ctClass.getDeclaredMethod("queryUserInfo"); // 方法前加强 ctMethod.insertBefore("{System.out.println(\"" + ctMethod.getName() + " 你被代理了,By Javassist\");}"); byte[] bytes = ctClass.toBytecode(); return (T) new JavassistProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance(); } } @Test public void test_JavassistProxy() throws Exception { IUserApi userApi = JavassistProxy.getProxy(UserApi.class) String invoke = userApi.queryUserInfo(); logger.info("测试结果:{}", invoke); } /** * 测试结果: * * queryUserInfo 你被代理了,By Javassist * 20:23:39.139 [main] INFO org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获! * * Process finished with exit code 0 */
感谢各位的阅读,以上就是“类代理的方式有哪些”的内容了,经过本文的学习后,相信大家对类代理的方式有哪些这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是天达云,小编将为大家推送更多相关知识点的文章,欢迎关注!