Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
  • Java语法

    • String
    • 面向对象
    • 反射
    • 泛型
    • 序列化
    • 动态代理
      • JDK动态代理
      • CGLIB动态代理
    • IO
    • 回调
    • SPI
    • 异常
    • 注解
  • Java容器

  • Java新特性

  • IDEA常用快捷键
  • 正则表达式
  • API
  • 场景题

  • JavaSE
  • Java语法
Nreal
2023-11-21
目录

动态代理

# JDK动态代理

代理类和被代理类都能转化为 接口类型,它们是平级关系;

核心:InvocationHandler 接口和 Proxy 类

Proxy类用于生成代理的方法:

  • 第一个参数:运行期间生成字节码,所以第一个参数需要类加载器加载运行期间动态生成的字节码;
  • 第二个参数:要实现哪个接口数组;
  • 第三个参数:具体代理类的实现由InvocationHandler实现;
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    ......
}
/*
loader :类加载器,用于加载代理对象。
interfaces : 被代理类实现的一些接口;
h : 实现了 InvocationHandler 接口的对象;
*/
1
2
3
4
5
6
7
8
9
10
11
12

当动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
/*
proxy :动态生成的代理类
method : 与代理类对象调用的方法相对应
args : 当前 method 方法的参数
*/
1
2
3
4
5
6
7
8

通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。

使用步骤:

  1. 定义一个接口及其实现类;
  2. 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;

案例:

  1. 自定义接口与实现类:

    public interface SmsService {
        String send(String message);
    }
    
    1
    2
    3
    public class SmsServiceImpl implements SmsService{
        @Override
        public String send(String message) {
            System.out.println("send message: " + message);
            return message;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
  2. 自定义JDK动态代理类

    public class DebugInvocationHandler implements InvocationHandler {
    
        /*类代理中的真实对象*/
        private final Object target;
    
        public DebugInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before method "+method.getName());
            Object result = method.invoke(target, args);
            System.out.println("after method "+method.getName());
            return result;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  3. 获取代理对象的工厂类

    public class JdkProxyFactory {
        public static Object getProxy(Object target){
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new DebugInvocationHandler(target)
            );
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  4. 测试类

    public class Test {
        public static void main(String[] args) {
            SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
            smsService.send("java");
        }
    }
    
    1
    2
    3
    4
    5
    6
    before method send
    send message:java
    after method send
    
    1
    2
    3

面试题:jdk动态代理为什么需要基于接口?

追问:jdk动态代理要求代理类实现InvocationHandler接口,重写invoke方法就可以实现动态代理,且构造函数注入Object即可,并无要求Object必须基于接口,而且重本质上说getProxy()获代理类(Object的原类),完全可以通过继承对象的方式?

源码剖析:

获取代理类的是:getProxyClass0

继续追get方法,最终看到调用到ProxyClassFactory类的apply()方法

在ProxyClassFactory的apply方法中真正的代理类的方法:generateProxyClass

发现只需要将saveGeneratedFiles设置true,即可本地查看生成的代理类:

修改测试代码:

public static void main(String[] args) {
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
    smsService.send("java");
}
1
2
3
4
5

本地多了一个代理类:$Proxy0本身就继承了一个Proxy类,由于java不支持多继承,因此无法实现动态代理

总结:生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现。

# CGLIB动态代理

通过父子继承关系创建代理,代理作为子类型,可以转换为父类型,父类和方法上都不能加final(代理毕竟是继承或重写);

被代理类

class UserDao{
    public void saveUser(){
        sout("新增用户");
    }
}
1
2
3
4
5

增强类

class MyAspect{
    public void check_permission(){
        sout("检查权限");
    }
    public void log(){
        sout("日志记录");
    }
}
1
2
3
4
5
6
7
8

代理类

class CglibProxy implements MethodInterceptor{
    //被代理类
    private Object target;
    public Object createProxy(Object target){
        this.target = target;
        //通过cglib增强代码
        Enhancer enhancer = new Enhancer();//生成代理的工厂类
        //生成目标类对象的子类进行增强
        //设置被代理类的类型type
        enhancer.setSuperclass(target.getClass());
        //增强后回调
        enhancer.setCallback(this);
        Object proxy = enhancer.create();
        return proxy;
    }
    //如何增强父类
    //proxy:代理对象 引用
    //method:被代理对象的方法描述引用
    //args:方法参数
    //methodProxy:代理对象 对目标对象的方法的描述
    @Override
    public Object intercept(Object proxy,Method method,Object[] args,MehtodProxy methodProxy) thorows Throwable{
        //增强代码
        MyAspect aspect = new MyAspect();
        aspect.check_permission();
        //调用原始方法
        //1.代理类对象进行调用
        Object invokeSuper = methodProxy.invokeSuper(proxy,args);
        //2.原对象
        //Object invoke = method.invoke(target,args);
        return invokeSuper;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

测试类

main{
    //原对象
    UserDao target = new UserDao();
    //new proxy工厂类实例
    CglibProxy cglibProxy = new CglibProxy();
    UserDao proxy = (UserDao)cglibProxy.createProxy(target);
    proxy.saveUser();
}
1
2
3
4
5
6
7
8
序列化
IO

← 序列化 IO→

Theme by Vdoing | Copyright © 2021-2024
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式