Java 动态代理

文章目录

大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)

每天进步一点,今日再接再励~

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

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

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

所谓静态,也就是在程序运行前,就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就确定了。

而动态代理的源码,是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。

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

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

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

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

java 复制代码
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;整体结构有些类似装饰模式;

java 复制代码
class UserServiceProxy implements IUserService {
    private final IUserService target; // 被代理的对象
    public UserServiceProxy() {
        this.target = new UserService(); //被代理对象,是在代理类中生成
    }
    @Override
    public void findById(String uid) {
        try {
            before();
            target.findById(uid);    // 这里才实际调用真实对象的方法
            after();
        } catch ( Exception ex ) {
            exception(ex);
            throw ex;
        }
    }
    @Override
    public 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("提交回滚");
    }
}

客户端测试:

java 复制代码
    @Test
    public void demo1(){
        IUserService proxy = new UserServiceProxy();
        proxy.findById("1");
        System.out.println("");
        proxy.update(new Object(), "4");
    }

输出:

markdown 复制代码
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方法中编写方法调用的逻辑处理;

java 复制代码
class JdkProxyHandler implements InvocationHandler {
    private final Object target;  // 被代理的对象,实际的方法执行者
    public JdkProxyHandler(Object target) {
        this.target = target;
    }
    
    /**
     * @param proxy 代理对象实例 todo ①
     * @param method 被代理类的Interface中的方法; todo ②
     * @param args 
     * */
    @Override
    public 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方法:

java 复制代码
    @Test
    public 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版本影响
    }

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

java 复制代码
//Jdk动态生成的类片段
public final class UserServiceProxy extends Proxy implements IUserService {
    private static Method m3;
    private static Method m4;
	
	//注意这个入参,就是JdkProxyHandler
    public 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自动根据原对象生成的子类;

java 复制代码
class CglibProxyHandler implements MethodInterceptor {

    /**
     * @param target 代理子类对象;
     * @param method 被代理类的方法;
     * @param args 
     * @param methodProxy 被代理的句柄方法  todo ③
     * */
    @Override
    public 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("提交回滚");
    }
}

调用方式:

java 复制代码
    @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");
    }

动态生成类片段:

java 复制代码
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
java 复制代码
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的标签id
        mapper.put("findById", "select * from user where id = ?");
        mapper.put("update", "update user set name = ? where id = ?");
		
		//UserDao.class.getName(),对应到Mapper.xml的namespace
        mapperMap.put(UserDao.class.getName(), mapper); 
    }
    
    @Override
    public 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;
    }
}

    @Test
    public 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); //打印sql
        String update = userDao.update(new Object(), "4");
        System.out.println(update);
    }
低配Feign
java 复制代码
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:被代理类方法
	*/
    @Override
    public 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);
    }    
}

    @Test
    public 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 ,一般编写完之后,会统一存放在公共位置。如果业务模块想添加功能,势必需要修改到这部分核心代码;违背了支持扩展、封闭修改原则;

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

java 复制代码
//切入点对象
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;
    }

    @Override
    public 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);
    }
}

    
    @Test
    public void demo6() {
        MyInterceptor interceptor = new MyInterceptor(){
            @Override
            public 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~

附录代码
java?linenums 复制代码
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();
        }
    }



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


    @Test
    public 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");

    }
    
    
    @Test
    public 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); //打印sql
        String update = userDao.update(new Object(), "4");
        System.out.println(update);
    }
    
    
    @Test
    public 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);
    }
    
    
    @Test
    public void demo6() {
        MyInterceptor interceptor = new MyInterceptor(){
            @Override
            public 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(); //被代理对象,是在代理类中生成
    }
    @Override
    public void findById(String uid) {
        try {
            before();
            target.findById(uid);    // 这里才实际调用真实对象的方法
            after();
        } catch ( Exception ex ) {
            exception(ex);
            throw ex;
        }
    }
    @Override
    public 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 
     * */
    @Override
    public 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 ③
     * */
    @Override
    public 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; // 100
    public CglibProxyHandler1(List<MyProxyInterceptor> interceptors) {
        this.interceptors = interceptors;
    }

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //2
        List<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的标签id
        mapper.put("findById", "select * from user where id = ?");
        mapper.put("update", "update user set name = ? where id = ?");

        //UserDao.class.getName(),对应到Mapper.xml的namespace
        mapperMap.put(UserDao.class.getName(), mapper);
    }

    @Override
    public 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:被代理类方法
     */
    @Override
    public 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;
    }

    @Override
    public 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);
    }
}
相关推荐
Chrikk24 分钟前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*27 分钟前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue28 分钟前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man30 分钟前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟31 分钟前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity1 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天2 小时前
java的threadlocal为何内存泄漏
java
caridle2 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
萧鼎2 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸2 小时前
【一些关于Python的信息和帮助】
开发语言·python