动态代理初认识

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...

相关推荐
程序员cxuan8 分钟前
本地跑一个 Qwen 3.8,你将拥有一个 Opus 4.6
人工智能·后端·程序员
凤山老林1 小时前
高保真集成测试:Spring Boot 结合 Testcontainers 的工程落地
spring boot·后端·集成测试
步行cgn1 小时前
MyBatis <trim> 标签完全解析:动态 SQL 的终极武器
后端
XPoet1 小时前
AI 编程工程化:实战——从 0 到 1 搭建 AI 编程工作流
前端·后端·ai编程
掘金者阿豪1 小时前
向量数据库不是终点,企业AI真正需要的是融合数据库
后端
程序员cxuan1 小时前
OpenAI Linux 版来了!
人工智能·后端·程序员
大黄评测1 小时前
Angular 表单:响应式表单高级用法,Typed Forms 类型化表单实战
后端
大勇前进1 小时前
Angular 变更检测深度讲解:OnPush 策略什么时候用、踩过哪些坑
后端
拾光师1 小时前
Python 解析 JSON 日志:从一行数据到一份报告
后端
小强19881 小时前
RxJS 在 Angular 项目最佳实践:彻底告别内存泄漏,用好 asyncPipe
后端