动态代理初认识

1. 什么是动态代理?

动态代理是一种实现代理模式的技术,允许在运行时创建代理对象,并将方法调用重定向到特定的处理器,说白了就是能在不破坏原有代码的基础上进行拓展,能在方法执行之前、执行之后或者其他时机做额外的操作。

2. 动态代理有哪些?

动态代理分为两种:

  • Cglib(基于继承的方式实现)
  • JdkProxy(基于接口的方式实现)

3. 如何实现动态代理?

公共依赖

XML 复制代码
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.1</version>
    </dependency>
</dependencies>

基础代码

UserService

JAVA 复制代码
public class UserService {

    public void addUser(String user) {
        System.out.println("添加user信息:" + user);
        this.printUser(user);
    }

    private void printUser(String user) {
        System.out.println("printUser...:" + user);
    }

}

PersonService接口

JAVA 复制代码
public interface PersonService {

    void addPerson(String person);
    
}

PersonService实现类

JAVA 复制代码
public class PersonServiceImpl implements PersonService {
    @Override
    public void addPerson(String person) {
        System.out.println("添加人的信息:" + person);
        this.printPerson(person);
    }

    private void printPerson(String person) {
        System.out.println("printPerson...:" + person);
    }
}

3.1 Cglib

  1. 首先定义一个Cglib的工具类
JAVA 复制代码
/**
 * 基于Cglib的代理,无需实现接口,基于继承实现
 */
public class CglibProxy implements MethodInterceptor {

    Class<?> target;

    public CglibProxy(Class<?> target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("begin intercept");
        methodProxy.invokeSuper(o, objects);
        System.out.println("end intercept");
        return o;
    }

}
  1. 编写测试代码
JAVA 复制代码
public class CglibTest {

    @Test
    public void test() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new CglibProxy(UserService.class));
        UserService userService = (UserService) enhancer.create();
        userService.addUser("张三");
    }

    @Test
    public void test2() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(PersonServiceImpl.class);
        enhancer.setCallback(new CglibProxy(PersonServiceImpl.class));
        PersonServiceImpl personService = (PersonServiceImpl) enhancer.create();
        personService.addPerson("张三");
    }
}

执行test()

JAVA 复制代码
begin intercept
添加user信息:张三
printUser...:张三
end intercept

执行test2()

JAVA 复制代码
begin intercept
添加人的信息:张三
printPerson...:张三
end intercept
  1. 结果分析

UserService类和PersonService实现类都可以成功被Cglib代理,并且方法调用的私有方法不会被再次代理

3.2 JdkProxy

  1. 编写工具类
JAVA 复制代码
/**
 * JDK动态代理,基于接口实现
 */
public class JdkProxy implements InvocationHandler {
    private final Object targetObject;

    public JdkProxy(Object targetObject) {
        this.targetObject = targetObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke...");
        Object invoke = method.invoke(targetObject, args);
        System.out.println("after invoke....");
        return invoke;
    }
}
  1. 编写测试代码
JAVA 复制代码
public class JdkProxyTest {

    @Test
    public void test() {
        PersonService personService = (PersonService) Proxy.newProxyInstance(PersonServiceImpl.class.getClassLoader(),
                PersonServiceImpl.class.getInterfaces(),
                new JdkProxy(new PersonServiceImpl()));
        personService.addPerson("张三");
    }

    /**
     * 执行失败
     */
    @Test
    public void test2() {
        UserService userService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
                UserService.class.getInterfaces(),
                new JdkProxy(new UserService()));
        userService.addUser("张三");
    }
}

执行test()

JAVA 复制代码
before invoke...
添加人的信息:张三
printPerson...:张三
after invoke....

执行test2()

JAVA 复制代码
java.lang.ClassCastException: com.sun.proxy.$Proxy4 cannot be cast to org.example.service.UserService
  1. 结果分析

基于jdk动态代理的方式,只有实现了接口的类才可以被成功代理,没有实现接口的类无法被代理,且被代理的方法调用内部方法时不会被再次代理。

  1. 分析原因

为什么内部方法不会被代理?

  • JDK 动态代理是基于接口的,它生成的代理对象是实现了目标类所实现的接口的。而私有方法是无法在接口中定义的,因此无法被代理。只有通过继承和实现接口的公共方法才能被动态代理。
  • Cglib 动态代理是通过继承目标类来创建代理对象的。当目标类的方法被代理时,Cglib 会创建一个子类并重写该方法,从而实现代理的功能。但是,私有方法并不会被继承到子类中,因此无法被重写和代理。
  • 总结来说,无论是 JDK 动态代理还是 Cglib 动态代理,私有方法都无法被代理。

UserService类里面的printUser()PersonServiceImpl类里面的printPerson()方法改为public试试

JAVA 复制代码
public void addUser(String user) {
    System.out.println("添加user信息:" + user);
    this.printUser(user);
}

public void printUser(String user) {
    System.out.println("printUser...:" + user);
}
JAVA 复制代码
@Override
public void addPerson(String person) {
    System.out.println("添加人的信息:" + person);
    this.printPerson(person);
}

public void printPerson(String person) {
    System.out.println("printPerson...:" + person);
}

再次调用代理,发现只有基于Cglib的代理可以生效,能过再次代理方法内调用的方法,基于JdkProxy的代理无法生效。因为JDK 动态代理的代理对象只会代理通过它进行的方法调用,而不会对目标对象内部的方法调用进行代理。

4. 总结

  • Cglib的可以代理没实现接口的类,也可以代理实现接口的类
  • JDKProxy只能代理实现了接口的类
  • Cglib和JDKProxy都无法代理方法中调用的private方法
  • Cglib在代理了方法的基础上,可以再次代理方法内调用的public方法

写完了个demo,对这两种代理的方式和场景起码是有了初步的认识,虽说在网上刷到过很多次,但确实都不如自己实践一次理解得深刻,更多的理解可能还是需要在工作中碰到才能进一步深挖,纸上得来终觉浅,绝知此事要躬行。

上述代码的链接:gitee.com/szq2021/pro...

相关推荐
oak隔壁找我3 小时前
MySQL中 SHOW FULL PROCESSLIST` 输出中 `State` 列的所有可能值
后端
上进小菜猪3 小时前
基于 YOLOv8 的面向文档智能处理的表格区域检测系统 [目标检测完整源码]
后端
oak隔壁找我4 小时前
JVM常用调优参数
java·后端
IT_陈寒7 小时前
React状态管理终极对决:Redux vs Context API谁更胜一筹?
前端·人工智能·后端
晨星shine8 小时前
GC、Dispose、Unmanaged Resource 和 Managed Resource
后端·c#
蝎子莱莱爱打怪8 小时前
OpenClaw 从零配置指南:接入飞书 + 常用命令 + 原理图解
java·后端·ai编程
倚栏听风雨9 小时前
【ES避坑指南】明明存的是 "CodingAddress",为什么 term 查询死活查不到?彻底搞懂 text 和 keyword
后端
程序员爱钓鱼9 小时前
Go 操作 Windows COM 自动化实战:深入解析 go-ole
后端·go·排序算法
回家路上绕了弯9 小时前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
子玖9 小时前
实现微信扫码注册登录-基于参数二维码
后端·微信·go