Java 动态代理

文章目录

          • 静态代理
          • Jdk动态代理
          • cglib动态代理
          • 使用案例
            • 低配Mybatis
            • 低配Feign
            • 拦截器
          • 附录代码



大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)
每天进步一点,今日再接再励~


动态代理在Java中有着广泛的应用,比如Spring AOP、Mybatis数据查询、RPC远程调用、性能监控,甚至事务处理等。


代理模式,根据代码的生成时机,分为两种:

  • 静态代理:代码块在源码阶段已存在,经过编译之后生成在class文件中;
  • 动态代理:代码块在运行过程中,根据运行环境参数决定代码如何生成,自动生成class字节码,然后再加载到JVM中;

所谓静态,也就是在程序运行前,就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就确定了。
而动态代理的源码,是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。


而根据动态生成字节码的技术手段,又分两种:

  • Jdk动态代理
  • cglib动态代理



静态代理

先了解静态代理,然后理解静态代理的优缺点缺点,再来学习动态代理;

编写一个接口IUserService,以及该接口的一个实现类UserService

interface IUserService {void findById(String uid);void update(Object user, String uid);
}class UserService implements IUserService {public void findById(String uid) {System.out.println("查询 findById");}public void update(Object user, String uid) {System.out.println("更新 update");}
}

通过静态代理对IUserService已存在的实例,进行功能增强;在调用findByIdupdate之前记录一些日志,模拟开启事务。
写一个代理类UserServiceProxy,代理类需要实现IUserService;整体结构有些类似装饰模式;

class UserServiceProxy implements IUserService {private final IUserService target; // 被代理的对象public UserServiceProxy() {this.target = new UserService(); //被代理对象,是在代理类中生成}@Overridepublic void findById(String uid) {try {before();target.findById(uid);    // 这里才实际调用真实对象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}@Overridepublic void update(Object user, String uid) {try {before();target.update(user, uid);    // 这里才实际调用真实对象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() {     // 在执行方法之前执行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("开启事务");}private void after() {      // 在执行方法之后执行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事务");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滚");}
}

客户端测试:

    @Testpublic void demo1(){IUserService proxy = new UserServiceProxy();proxy.findById("1");System.out.println("");proxy.update(new Object(), "4");}

输出:

log start time [Sat Jun 17 09:34:05 CST 2023] 
开启事务
查询 selectById
log end time [Sat Jun 17 09:34:05 CST 2023] 
提交事务log start time [Sat Jun 17 09:34:05 CST 2023] 
开启事务
更新 update
log end time [Sat Jun 17 09:34:05 CST 2023] 
提交事务

通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。

静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来:

  1. 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
    1.1 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大;
    1.2 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

  2. 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。


如何改进?

可以发现,代理类的代码块流程绝大部分是相似的:在执行真实方法之前执行before,在执行真实方法成功之后执行after,发生异常执行exception

类似固定的模板,就可以使用程序来自动编写代码,用程序自动写程序,也就是动态代理。

哪些类可以动态生成代码?EnhancerInterfaceMakerBeanGenerator 有机会再单独写一篇


实现动态代理的思考方向:
为了让生成的代理类与目标对象保持一致性,从现在开始将介绍以下两种最常见的方式:

通过实现接口的方式:JDK动态代理
通过继承类的方式:CGLIB动态代理




Jdk动态代理

JDK动态代理主要涉及两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

编写一个调用逻辑处理器JdkProxyHandler类,提供日志增强功能,并实现InvocationHandler接口;
JdkProxyHandler中维护一个目标对象,这个对象是被代理的对象;在invoke方法中编写方法调用的逻辑处理;

class JdkProxyHandler implements InvocationHandler {private final Object target;  // 被代理的对象,实际的方法执行者public JdkProxyHandler(Object target) {this.target = target;}/*** @param proxy 代理对象实例 todo ①* @param method 被代理类的Interface中的方法; todo ②* @param args * */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {before();//反射调用 target 的 method 方法。//proxy是代理对象实例,因此在反射调用的时候,需要替换成被代理类target对象;Object result = method.invoke(target, args);  after();return result; // 返回方法的执行结果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() {     // 在执行方法之前执行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("开启事务");}private void after() {      // 在执行方法之后执行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事务");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滚");}
}

编写客户端,获取动态生成的代理类的对象须借助java.lang.reflect.Proxy类的newProxyInstance方法:

    @Testpublic void demo2(){// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");// 1. 创建被代理的对象。实际代码中,可以使用到对象工厂IUserService userService = new UserService();// 2. 代理类请求处理器:拦截处理代理对象上的所有的方法调用。// 和demo1中的UserServiceProxy类相似。// 由于JdkProxyHandler可以复用,被代理类(userService)可以使多例,所以JdkProxyHandler也应该是多例,被代理类应该显示传入。// 主要JdkProxyHandler构造器使用Interface作为入参,因此JdkProxyHandler可以代理一切Interface的实现类。InvocationHandler proxyHandler = new JdkProxyHandler(userService);// 3. 获取对应的 ClassLoader。类加载机制,如果搞错ClassLoader,可能会导致动态生成的代理类,无法被加载:提示 ClassNotFoundException;保持和被代理类在一个ClassLoader中ClassLoader classLoader = userService.getClass().getClassLoader();// 4. 获取所有接口的Class,这里的UserService只实现了一个接口IUserService,Class[] interfaces = userService.getClass().getInterfaces();/*5.根据上面提供的信息,创建代理对象 在这个过程中,a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等相关字节码;b.然后根据相应的字节码转换成对应的class加载到JVM中;c.然后调用newInstance()创建代理实例;*/IUserService proxy = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);//输出动态代理之后的class代码//generateClassFile(proxy.getClass(), "UserServiceProxy"); //在代码在文章最后// 调用代理的方法proxy.findById("3");System.out.println("");proxy.update(new Object(), "4");//JDK动态代理,底层本质是使用方法反射;性能瓶颈受Jdk版本影响}

执行之后可以查看动态生成的代理类:

//Jdk动态生成的类片段
public final class UserServiceProxy extends Proxy implements IUserService {private static Method m3;private static Method m4;//注意这个入参,就是JdkProxyHandlerpublic UserServiceProxy(InvocationHandler var1) throws  {super(var1);}public final void findById(String var1) throws  {try {//注意第一个参数this,即代表JdkProxyHandler.invoke的第一个入参,是代理类本身; todo ①//第二个参数m4,是从cn.bugcat.code.IUserService中获取 todo ②super.h.invoke(this, m4, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void update(Object var1, String var2) throws  {try {super.h.invoke(this, m3, new Object[]{var1, var2});} catch (RuntimeException | Error var4) {throw var4;} catch (Throwable var5) {throw new UndeclaredThrowableException(var5);}}static {try {//findById、update方法,都是从Interface(IUserService)中获取  todo ②m4 = Class.forName("cn.bugcat.code.IUserService").getMethod("findById", Class.forName("java.lang.String"));m3 = Class.forName("cn.bugcat.code.IUserService").getMethod("update", Class.forName("java.lang.Object"), Class.forName("java.lang.String"));} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}
  • UserServiceProxy继承了Proxy类,并且实现了被代理的所有接口,以及equalshashCodetoString等方法;
  • 由于UserServiceProxy继承了Proxy类,所以每个代理类都会关联一个InvocationHandler方法调用处理器;
  • 类和所有方法都被public final修饰,所以代理类只可被使用,不可以再被继承;
  • 每个方法都有一个Method对象来描述,Method对象在static静态代码块中创建,以m + 数字的格式命名;
  • 调用方法的时候通过super.h.invoke(this, m1, new Object[]{});调用,其中的super.h.invoke实际上是在创建代理的时候传递给Proxy.newProxyInstanceJdkProxyHandler对象,它继承InvocationHandler类,负责实际的调用处理逻辑;
  • JdkProxyHandlerinvoke方法接收到methodargs等参数后,进行一些处理,然后通过反射让被代理的对象target执行方法;

仔细观察JdkProxyHandler,构造器中入参使用Object类型接受,也就是意味JdkProxyHandler类不光可以传入UserService实现类,也可以传入其他任何对象实例;
Proxy.newProxyInstance是根据第二个参数Interfaces动态生成方法,而这些方法恰好UserService也实现了,代理类和具体类,通过interfaces联系到一起;
最终执行JdkProxyHandler构造器 入参对象的所有方法 ,都会统一执行JdkProxyHandler.invoke代码。




cglib动态代理

spring 框架中内置了cglib相关Jar包内容,spring项目可以直接使用:

被代理类:原对象;
代理子类:由cglib自动根据原对象生成的子类;

class CglibProxyHandler implements MethodInterceptor {/*** @param target 代理子类对象;* @param method 被代理类的方法;* @param args * @param methodProxy 被代理的句柄方法  todo ③* */@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {try {before();//注意这里是调用invokeSuper而不是invoke,否则死循环。 todo ③//method是被代理的方法,但是由于tagert是代理子类,执行method.invoke,实际上是表示执行代理子类的方法,代理子类又会继续执行MethodInterceptor.intercept方法,导致又回到此处代码,造成死循环;//此处应该直接执行invokeSuper,表示直接调用被代理类的句柄方法;Object result = methodProxy.invokeSuper(target, args); after();return result; // 返回方法的执行结果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() {     // 在执行方法之前执行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("开启事务");}private void after() {      // 在执行方法之后执行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事务");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滚");}
}

调用方式:

    @Test    public void demo3(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new CglibProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);  // 设置超类,cglib是通过继承来实现的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create();   // 创建代理类userService.findById("4");userService.update(new Object(), "4");}

动态生成类片段:

public class UserService$$EnhancerByCGLIB$$a1b35990 extends UserService implements Factory {final void CGLIB$update$0(Object var1, String var2) {super.update(var1, var2); //调用父类方法 => UserService.update}public final void update(Object var1, String var2) { //代理类暴露的方法,等价于重写了父类UserService的update方法MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;  // 实际上是我们自定义的CglibProxyHandler对象if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {// 第一个参数,代理类对象  todo ③// CGLIB$update$0$Method 父类对应的方法// CGLIB$update$0$Proxy当前代理的 CGLIB$update$0方法,该方法会继续调用父类的对应方法var10000.intercept(this, CGLIB$update$0$Method, new Object[]{var1, var2}, CGLIB$update$0$Proxy);} else {super.update(var1, var2);}}
}	

Jdk动态代理与cglib动态代理对比

  • Jdk动态代理:基于Java反射机制实现,必须要实现了接口的类,才能用这种办法生成代理对象。
  • cglib动态代理:基于ASM机制实现,通过操作字节码生成子类,代理代码块集成在子类上,实际调用被代理类的方法时,和原生调用效率一样;
  • cglib由于使用继承被代理实现,因此如果是类、方法被final修饰,则无法使用!被cglib代理的类,无法再次被代理!
  • Jdk不受final关键词影响;被代理之后,仍然可以被Jdk再次代理;
  • cglib在创建代理子类的时候,可以通过CallbackFilter,可以为每个方法创建单独的MethodInterceptor;后续调用时,方法与方法之间,代码在物理层面隔离,互相不影响;
  • Jdk只能在InvocationHandler实现类中,需要自行做分支处理;




使用案例
低配Mybatis
interface UserDao {String findById(String uid);String update(Object user, String uid);
}class MapperProxyHandler implements MethodInterceptor {private static Map<String, Map<String, String>> mapperMap = new HashMap<>();static {//初始化,模拟Mapper.xml文件Map<String, String> mapper = new HashMap<>();//Map的key,及为UserDao中的方法名,以及Mapper.xml的标签idmapper.put("findById", "select * from user where id = ?");mapper.put("update", "update user set name = ? where id = ?");//UserDao.class.getName(),对应到Mapper.xml的namespacemapperMap.put(UserDao.class.getName(), mapper); }@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {String methodName = method.getName();Class<?> mapperClass = method.getDeclaringClass();Map<String, String> mapper = mapperMap.get(mapperClass.getName());if( mapper != null ){return mapper.get(methodName);}return null;}
}@Testpublic void demo4(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new MapperProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class);  // 设置超类;此时UserDao只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;enhancer.setInterfaces(new Class[]{UserDao.class});enhancer.setCallback(proxyHandler);UserDao userDao = (UserDao)enhancer.create();   // 创建代理类String findById = userDao.findById("4");System.out.println(findById); //打印sqlString update = userDao.update(new Object(), "4");System.out.println(update);}



低配Feign

interface MyFeign {@RequestMapping(value = "http://127.0.0.1:8080/aq", method = RequestMethod.POST)ResponseEntity<Void> aq();@RequestMapping(value = "https://www.baidu.com/s", method = RequestMethod.GET)String baidu(@RequestParam("wd") String keyword);}class FeignProxyHandler implements MethodInterceptor {/*** target:代理子类* method:被代理类方法*/@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//method是被代理类中的方法,因此可以通过method获取到:方法上注解;入参列表;入参上的注解;方法的返回类型RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);RequestMethod[] requestMethods = requestMapping.method();String[] paths = requestMapping.value();Type returnType = method.getGenericReturnType();Parameter[] parameters = method.getParameters();Map<String, Integer> argsIndex = new HashMap<>();for ( int idx = 0; idx < parameters.length; idx++ ) {Parameter parameter = parameters[idx];RequestParam requestParam = AnnotationUtils.findAnnotation(parameter, RequestParam.class);argsIndex.put(requestParam.name(), idx);}Map<String, Object> reqMap = new HashMap<>();argsIndex.forEach((pname, idx) -> {reqMap.put(pname, args[idx]);});if( requestMethods[0] == RequestMethod.GET ){String resp = HttpFactory.mutate().doGet().send(paths[0], reqMap);return resultType(resp, returnType);} else if (requestMethods[0] == RequestMethod.POST ){String resp = HttpFactory.mutate().doPost().send(paths[0], reqMap);return resultType(resp,returnType);}return null;}private <R> R resultType(String resp, Type type){if( resp == null ){return null;}if( type instanceof Class ){if( String.class.isAssignableFrom((Class) type) ){return (R) resp;}}return JSONObject.parseObject(resp, type);}    
}@Testpublic void demo5(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new FeignProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class);  // 设置超类;此时MyFeign只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;enhancer.setInterfaces(new Class[]{MyFeign.class});enhancer.setCallback(proxyHandler);MyFeign feign = (MyFeign)enhancer.create();   // 创建代理类ResponseEntity<Void> aq = feign.aq();System.out.println(JSONObject.toJSONString(aq));String baidu = feign.baidu("csdn");System.out.println(baidu);}

拦截器

拦截器,不属于sevletfilter这一类物理层面存在的组件,而是通过代码逻辑出来的一种代码结构;通过对动态代理学习,我们可以在Object result = method.invoke(target, args); Object result = methodProxy.invokeSuper(target, args); 代码的前后,添加自定义的代码,增强被代理类方法;

但是,这部分属于核心代码JdkProxyHandler CglibProxyHandler,一般编写完之后,会统一存放在公共位置。如果业务模块想添加功能,势必需要修改到这部分核心代码;违背了支持扩展、封闭修改原则;

因此在此基础之上,逻辑出一套拦截器模式,对业务模块开放修改;

//切入点对象
class Point {private final Object target;private final Method method;private final Object[] args;private final MethodProxy methodProxy;public Point(Object target, Method method, Object[] args, MethodProxy methodProxy) {this.target = target;this.method = method;this.args = args;this.methodProxy = methodProxy;}public Object postHandle() throws Throwable{Object result = methodProxy.invokeSuper(target, args);return result;}
}//拦截器接口
interface MyInterceptor {default Object doInterceptor(Point point) throws Throwable {return point.postHandle();}
}class MyProxyHandler implements MethodInterceptor {private final MyInterceptor interceptor;public MyProxyHandler(MyInterceptor interceptor) {this.interceptor = interceptor;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//注意,本应该在此处执行的「Object result = methodProxy.invokeSuper(target, args);」,被转移到Point#postHandle方法内了 todo ⑤Point point = new Point(target, method, args, methodProxy);return interceptor.doInterceptor(point);}
}@Testpublic void demo6() {MyInterceptor interceptor = new MyInterceptor(){@Overridepublic Object doInterceptor(Point point ) throws Throwable {try {System.out.println("befor");Object resp = point.postHandle(); //在此处,才真正执行被代理类的方法  todo ⑤;//本应该在MethodInterceptor子类中执行的代码,被转移到MyInterceptor子类了;MethodInterceptor子类只能有一个,但是MyInterceptor子类,结合责任链设计模式,却可以有很多个!System.out.println("success");return resp;} catch ( Throwable e ) {System.out.println("error");throw e;} finally {System.out.println("finally");}}};MethodInterceptor proxyHandler = new MyProxyHandler(interceptor);Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);  // 设置超类,cglib是通过继承来实现的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create();   // 创建代理类userService.findById("4");userService.update(new Object(), "4");}



可用来动态生成代码的有很多,这里推荐cglib的EnhancerInterfaceMakerBeanGeneratorClassWriter类;
毕竟现在基本上都是spring全家桶,而且cglib也被spring收编,只要是spring项目就可以直接使用;

动态代理也是面试必考:涉及到AOP、拦截器;而AOP的运用又是不胜枚举,拦截器结合责任链模式,也是在各种场所都用运用;

以上,就酱~

~the end~

附录代码

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import sun.misc.ProxyGenerator;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class ProxyDemo {private static String paths = "E:\\tmp\\proxy\\";/*** 将根据类信息动态生成的二进制字节码保存到硬盘中,默认的是clazz目录下* params: clazz 需要生成动态代理类的类* proxyName: 为动态生成的代理类的名称*/public static void generateClassFile(Class clazz, String proxyName) {try {File file = new File(paths);FileUtils.deleteDirectory(new File(paths));file.mkdirs();} catch ( IOException e ) {e.printStackTrace();}// 根据类信息和提供的代理类名称,生成字节码byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());try (FileOutputStream out = new FileOutputStream(paths + proxyName + ".class")){out.write(classFile);out.flush();} catch (Exception e) {e.printStackTrace();}}@Testpublic void demo1(){IUserService proxy = new UserServiceProxy();proxy.findById("1");System.out.println("");proxy.update(new Object(), "4");}@Testpublic void demo2(){// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");// 1. 创建被代理的对象。实际代码中,可以使用到对象工厂IUserService userService = new UserService();// 2. 代理类请求处理器:拦截处理代理对象上的所有的方法调用。// 和demo1中的UserServiceProxy类相似。// 由于JdkProxyHandler可以复用,被代理类(userService)可以使多例,所以JdkProxyHandler也应该是多例,被代理类应该显示传入。// 主要JdkProxyHandler构造器使用Interface作为入参,因此JdkProxyHandler可以代理一切Interface的实现类。InvocationHandler proxyHandler = new JdkProxyHandler(userService);// 3. 获取对应的 ClassLoader。类加载机制,如果搞错ClassLoader,可能会导致动态生成的代理类,无法被加载:提示 ClassNotFoundException;保持和被代理类在一个ClassLoader中ClassLoader classLoader = userService.getClass().getClassLoader();// 4. 获取所有接口的Class,这里的UserService只实现了一个接口IUserService,Class[] interfaces = userService.getClass().getInterfaces();/*5.根据上面提供的信息,创建代理对象 在这个过程中,a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等相关字节码;b.然后根据相应的字节码转换成对应的class加载到JVM中;c.然后调用newInstance()创建代理实例;*/IUserService proxy = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);//输出动态代理之后的class代码//generateClassFile(proxy.getClass(), "UserServiceProxy"); //在代码在文章最后// 调用代理的方法proxy.findById("3");System.out.println("");proxy.update(new Object(), "4");//JDK动态代理,底层本质是使用方法反射;性能瓶颈受Jdk版本影响}@Test    public void demo3(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new CglibProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);  // 设置超类,cglib是通过继承来实现的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create();   // 创建代理类userService.findById("4");userService.update(new Object(), "4");}@Testpublic void demo4(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new MapperProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class);  // 设置超类;此时UserDao只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;enhancer.setInterfaces(new Class[]{UserDao.class});enhancer.setCallback(proxyHandler);UserDao userDao = (UserDao)enhancer.create();   // 创建代理类String findById = userDao.findById("4");System.out.println(findById); //打印sqlString update = userDao.update(new Object(), "4");System.out.println(update);}@Testpublic void demo5(){System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, paths);MethodInterceptor proxyHandler = new FeignProxyHandler();Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class);  // 设置超类;此时MyFeign只是一个Interface,没有任何具体的实现类,因此超类设置为默认的Object;enhancer.setInterfaces(new Class[]{MyFeign.class});enhancer.setCallback(proxyHandler);MyFeign feign = (MyFeign)enhancer.create();   // 创建代理类ResponseEntity<Void> aq = feign.aq();System.out.println(JSONObject.toJSONString(aq));String baidu = feign.baidu("csdn");System.out.println(baidu);}@Testpublic void demo6() {MyInterceptor interceptor = new MyInterceptor(){@Overridepublic Object doInterceptor(Point point ) throws Throwable {try {System.out.println("befor");Object resp = point.postHandle(); //在此处,才真正执行被代理类的方法  todo ⑤;//本应该在MethodInterceptor子类中执行的代码,被转移到MyInterceptor子类了;MethodInterceptor子类只能有一个,但是MyInterceptor子类,结合责任链设计模式,却可以有很多个!System.out.println("success");return resp;} catch ( Throwable e ) {System.out.println("error");throw e;} finally {System.out.println("finally");}}};MethodInterceptor proxyHandler = new MyProxyHandler(interceptor);Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);  // 设置超类,cglib是通过继承来实现的enhancer.setCallback(proxyHandler);IUserService userService = (IUserService)enhancer.create();   // 创建代理类userService.findById("4");userService.update(new Object(), "4");}}interface IUserService {void findById(String uid);void update(Object user, String uid);
}class UserService implements IUserService {public void findById(String uid) {System.out.println("查询 findById");}public void update(Object user, String uid) {System.out.println("更新 update");}
}class UserServiceProxy implements IUserService {private final IUserService target; // 被代理的对象public UserServiceProxy() {this.target = new UserService(); //被代理对象,是在代理类中生成}@Overridepublic void findById(String uid) {try {before();target.findById(uid);    // 这里才实际调用真实对象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}@Overridepublic void update(Object user, String uid) {try {before();target.update(user, uid);    // 这里才实际调用真实对象的方法after();} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() {     // 在执行方法之前执行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("开启事务");}private void after() {      // 在执行方法之后执行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事务");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滚");}
}class JdkProxyHandler implements InvocationHandler {private final Object target;  // 被代理的对象,实际的方法执行者public JdkProxyHandler(Object target) {this.target = target;}/*** @param proxy 代理对象实例 todo ①* @param method 被代理类的Interface中的方法; todo ②* @param args * */@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {before();//反射调用 target 的 method 方法。//proxy是代理对象实例,因此在反射调用的时候,需要替换成被代理类target对象;Object result = method.invoke(target, args);after();return result; // 返回方法的执行结果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() {     // 在执行方法之前执行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("开启事务");}private void after() {      // 在执行方法之后执行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事务");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滚");}
}class CglibProxyHandler implements MethodInterceptor {/*** @param target 代理子类对象;* @param method 被代理类的方法;* @param args * @param methodProxy 被代理的句柄方法  todo ③* */@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {try {before();//注意这里是调用invokeSuper而不是invoke,否则死循环。 todo ③//method是被代理的方法,但是由于tagert是代理子类,执行method.invoke,实际上是表示执行代理子类的方法,代理子类又会继续执行MethodInterceptor.intercept方法,导致又回到此处代码,造成死循环;//此处应该直接执行invokeSuper,表示直接调用被代理类的句柄方法;Object result = methodProxy.invokeSuper(target, args);after();return result; // 返回方法的执行结果} catch ( Exception ex ) {exception(ex);throw ex;}}private void before() {     // 在执行方法之前执行System.out.println(String.format("log start time [%s] ", new Date()));System.out.println("开启事务");}private void after() {      // 在执行方法之后执行System.out.println(String.format("log end time [%s] ", new Date()));System.out.println("提交事务");}private void exception(Exception ex){System.out.println(String.format("log error [%s] ", ex.getMessage()));System.out.println("提交回滚");}
}interface MyProxyInterceptor {boolean preHandler();void before();void after();void exception(Exception ex);
}class CglibProxyHandler1 implements MethodInterceptor {private final List<MyProxyInterceptor> interceptors; // 100public CglibProxyHandler1(List<MyProxyInterceptor> interceptors) {this.interceptors = interceptors;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//2List<MyProxyInterceptor> interceptors = this.interceptors.stream().filter(interceptor -> interceptor.preHandler()).collect(Collectors.toList());try {interceptors.forEach(intercept -> {intercept.before();});Object result = methodProxy.invokeSuper(target, args);interceptors.forEach(intercept -> {intercept.after();});return result; // 返回方法的执行结果} catch ( Exception ex ) {interceptors.forEach(intercept -> {intercept.exception(ex);});throw ex;}}
}interface UserDao {String findById(String uid);String update(Object user, String uid);
}class MapperProxyHandler implements MethodInterceptor {private static Map<String, Map<String, String>> mapperMap = new HashMap<>();static {//初始化,模拟Mapper.xml文件Map<String, String> mapper = new HashMap<>();//Map的key,及为UserDao中的方法名,以及Mapper.xml的标签idmapper.put("findById", "select * from user where id = ?");mapper.put("update", "update user set name = ? where id = ?");//UserDao.class.getName(),对应到Mapper.xml的namespacemapperMap.put(UserDao.class.getName(), mapper);}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {String methodName = method.getName();Class<?> mapperClass = method.getDeclaringClass();Map<String, String> mapper = mapperMap.get(mapperClass.getName());if( mapper != null ){return mapper.get(methodName);}return null;}
}interface MyFeign {@RequestMapping(value = "http://127.0.0.1:8080/aq", method = RequestMethod.POST)ResponseEntity<Void> aq();@RequestMapping(value = "https://www.baidu.com/s", method = RequestMethod.GET)String baidu(@RequestParam("wd") String keyword);}class FeignProxyHandler implements MethodInterceptor {/*** target:代理子类* method:被代理类方法*/@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//method是被代理类中的方法,因此可以通过method获取到:方法上注解;入参列表;入参上的注解;方法的返回类型RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);RequestMethod[] requestMethods = requestMapping.method();String[] paths = requestMapping.value();Type returnType = method.getGenericReturnType();Parameter[] parameters = method.getParameters();Map<String, Integer> argsIndex = new HashMap<>();for ( int idx = 0; idx < parameters.length; idx++ ) {Parameter parameter = parameters[idx];RequestParam requestParam = AnnotationUtils.findAnnotation(parameter, RequestParam.class);argsIndex.put(requestParam.name(), idx);}Map<String, Object> reqMap = new HashMap<>();argsIndex.forEach((pname, idx) -> {reqMap.put(pname, args[idx]);});RestTemplate rest = new RestTemplate();if( requestMethods[0] == RequestMethod.GET ){String resp = rest.getForObject(paths[0], String.class, reqMap);return resultType(resp, returnType);} else if (requestMethods[0] == RequestMethod.POST ){String resp = rest.postForObject(paths[0], reqMap, String.class);return resultType(resp,returnType);}return null;}private <R> R resultType(String resp, Type type){if( resp == null ){return null;}if( type instanceof Class ){if( String.class.isAssignableFrom((Class) type) ){return (R) resp;}}return JSONObject.parseObject(resp, type);}
}class Point {private final Object target;private final Method method;private final Object[] args;private final MethodProxy methodProxy;public Point(Object target, Method method, Object[] args, MethodProxy methodProxy) {this.target = target;this.method = method;this.args = args;this.methodProxy = methodProxy;}public Object postHandle() throws Throwable{Object result = methodProxy.invokeSuper(target, args);return result;}
}//拦截器接口
interface MyInterceptor {default Object doInterceptor(Point point) throws Throwable {return point.postHandle();}
}class MyProxyHandler implements MethodInterceptor {private final MyInterceptor interceptor;public MyProxyHandler(MyInterceptor interceptor) {this.interceptor = interceptor;}@Overridepublic Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//注意,本应该在此处执行的[Object result = methodProxy.invokeSuper(target, args);],被转移到Point#postHandle方法内了 todo ⑤Point point = new Point(target, method, args, methodProxy);return interceptor.doInterceptor(point);}
}





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/41084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“深入探索JVM内部机制:解密Java虚拟机“

标题&#xff1a;深入探索JVM内部机制&#xff1a;解密Java虚拟机 摘要&#xff1a;本篇博客将深入剖析Java虚拟机&#xff08;JVM&#xff09;的内部机制&#xff0c;包括类加载、内存管理、垃圾回收、即时编译等关键组成部分。通过对JVM内部机制的解密&#xff0c;我们可以更…

团团代码生成器V1.0:一键生成完整的CRUD功能(提供Gitee源码)

前言&#xff1a;在日常开发的中&#xff0c;经常会需要重复写一些基础的增删改查接口&#xff0c;虽说不难&#xff0c;但是会耗费我们一些时间&#xff0c;所以我自己开发了一套纯SpringBoot实现的代码生成器&#xff0c;可以为我们生成单条数据的增删改查&#xff0c;还可以…

中远麒麟堡垒机 SQL注入漏洞复现

0x01 产品简介 中远麒麟依托自身强大的研发能力,丰富的行业经验&#xff0c;自主研发了新一代软硬件一体化统一安全运维平台一-iAudit 统一安全运维平台。该产品支持对企业运维人员在运维过程中进行统一身份认证、统一授权、统一审计、统一监控&#xff0c;消除了传统运维过程中…

实现Python脚本录制功能

要实现Python脚本录制功能&#xff0c;可以使用Python的内置模块pyautogui和opencv。 首先&#xff0c;需要安装这两个模块&#xff1a; pip install pyautogui opencv-python 然后&#xff0c;可以编写以下代码来实现脚本录制功能&#xff1a; import cv2 import numpy as …

CentOS7.6安装配置MySQL 5.7及常用命令汇总

一、MySQL安装&#xff08;rpm安装&#xff09; 1、检查没有安装过mysql或mariadb rpm -qa | grep -i mysql rpm -qa | grep -i mariadb 返回空值的话&#xff0c;就说明没有安装 MySQL。注意&#xff1a;在新版本的CentOS7中&#xff0c;默认的数据库已更新为了Mariadb&#…

你需要需求管理解决方案的三个原因

我们最近研究了一份 Forrester Research 的报告&#xff0c;得出如下结论&#xff1a;高度监管的行业可以从敏捷需求管理方法中受益。在本文中&#xff0c;我们将深入探讨所有行业的客户如何从一个协作平台中受益&#xff0c;该平台如何帮助他们在复杂的开发周期中管理需求。 …

【支付宝小程序】支付宝小程序自定义组件技术教程

&#x1f996;我是Sam9029&#xff0c;一个前端 Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 **&#x1f431;‍&#x1f409;&#x1f431;‍&#x1f409;恭喜你&#xff0c;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c…

接口设置了responseType:‘blob‘后,接收不到后端错误信息

下载文件流&#xff0c;需要接口设置responseType:blob&#xff0c;接口设置了responseType:blob后&#xff0c;拿不到后端接口的异常信息&#xff0c;我们只需要添加如下代码&#xff1a; const service axios.create({baseURL: ***, // url base url request url// withC…

影视公司技术流程设计之服务器搭建

在影视公司&#xff0c;硬件的投入占相当大的比例&#xff0c; 大到存储&#xff0c; 服务器&#xff0c;工作站&#xff0c; 小到主机CPU&#xff0c;内存&#xff0c;显卡&#xff0c;手绘板。 而存储又是硬件上的大头&#xff0c;一套合理的存储解决方案&#xff0c;优为关键…

【kubernetes】持久卷PV、PVC

目录 PV和PVC之间的相互作用遵循这个生命周期 根据这 5 个阶段&#xff0c;PV 的状态有以下 4 种 一个PV从创建到销毁的具体流程如下 静态PV创建 1、配置nfs存储 2、定义PV 3、定义PVC 4、测试访问 动态PV创建 1、在stor01节点上安装nfs&#xff0c;并配置nfs服务 2…

计算机视觉之三维重建(二)(摄像机标定)

标定示意图 标定目标 P ′ M P w K [ R T ] P w P^{}MP_wK[R \space T]P_w P′MPw​K[R T]Pw​ 其中 K K K为内参数&#xff0c; [ R T ] [R \space T] [R T]为外参数。该式子需要使用至少六对内外点对进行求解内外参数&#xff08;11个未知参数&#xff09;。 其中 R 3 3 …

windows系统丢失mfc120u.dll的解决方法

1.mfc120u.dll是什么 mfc120u.dll是Windows操作系统中的一个动态链接库&#xff08;Dynamic Link Library&#xff0c;简称DLL&#xff09;文件。它包含了一些用于运行C程序的函数和其他资源。这个特定的DLL文件是Microsoft Foundation Classes&#xff08;MFC&#xff09;库的…

freeswitch的mod_xml_curl模块动态获取configuration

概述 freeswitch是一款简单好用的VOIP开源软交换平台。 mod_xml_curl模块支持从web服务获取xml配置&#xff0c;本文介绍如何动态获取acl配置。 环境 centos&#xff1a;CentOS release 7.0 (Final)或以上版本 freeswitch&#xff1a;v1.6.20 GCC&#xff1a;4.8.5 web…

第2章 性能测量

理解应用程序性能的第一步是学会对它进行测量。 与绝大多数功能问题相比&#xff0c;性能问题通常很难跟踪和复现。 任何关注过性能评估的人可能都知道公允地进行性能测量并从中得到准确结论是多么困难。 因为在测量中存在误差&#xff0c;性能分析通常需要统计方法进行处理…

ThreadLocal(超详细介绍!!)

关于ThreadLocal&#xff0c;可能很多同学在学习Java的并发编程部分时&#xff0c;都有所耳闻&#xff0c;但是如果要仔细问ThreadLocal是个啥&#xff0c;我们可能也说不清楚&#xff0c;所以这篇博客旨在帮助大家了解ThreadLocal到底是个啥&#xff1f; 1.ThreadLocal是什么&…

Android设备通过蓝牙HID技术模拟键盘实现

目录 一&#xff0c;背景介绍 二&#xff0c;技术方案 2.1 获取BluetoothHidDevice实例 2.2 注册/解除注册HID实例 2.3 Hid report description描述符生成工具 2.4 键盘映射表 2.5 通过HID发送键盘事件 三&#xff0c;实例 一&#xff0c;背景介绍 日常生活中&#xff0…

AndroidStudio中修改打包生成的apk名称

1.配置手机架构 splits {abi {enable truereset()include armeabi-v7a,arm64-v8auniversalApk false} } 2.多渠道 productFlavors {normal {applicationId "*****"manifestPlaceholders [appName: "string/app_name_normal"]}driver {applicationId &qu…

图片转换成pdf格式?这几种转换格式方法了解一下

图片转换成pdf格式&#xff1f;将图片转换成PDF格式的好处有很多。首先&#xff0c;PDF格式具有通用性&#xff0c;可以在几乎任何设备上查看。其次&#xff0c;PDF格式可以更好地保护文件&#xff0c;防止被篡改或者复制。此外&#xff0c;PDF格式还可以更好地压缩文件大小&am…

使用Kaptcha生成验证码

说明&#xff1a;验证码&#xff0c;是登录流程中必不可少的一环&#xff0c;一般企业级的系统&#xff0c;使用都是专门制作验证码、审核校验的第三方SDK&#xff08;如极验&#xff09;。本文介绍&#xff0c;使用谷歌提供的Kaptcha技术&#xff0c;制作一个简单的验证码。 …

sqlserver数据库导出到mysql

爱到分才显珍贵&#xff0c;很多人都不懂珍惜拥有&#xff0c;只到失去才看到&#xff0c;其实那最熟悉的才最珍贵的。 这里只介绍一种方式&#xff0c;有很多的方式。 1.使用Navicat 安装 下载 2.工具 数据传输 3.选择源和目标 然后开始 4.最好导入前备份一下库