动态代理初认识

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

相关推荐
寻月隐君20 分钟前
Rust 异步编程实践:从 Tokio 基础到阻塞任务处理模式
后端·rust·github
GO兔20 分钟前
开篇:GORM入门——Go语言的ORM王者
开发语言·后端·golang·go
Sincerelyplz26 分钟前
【Temproal】快速了解Temproal的核心概念以及使用
笔记·后端·开源
爱上语文27 分钟前
Redis基础(6):SpringDataRedis
数据库·redis·后端
Lemon程序馆27 分钟前
速通 GO 垃圾回收机制
后端·go
Aurora_NeAr32 分钟前
Spark SQL架构及高级用法
大数据·后端·spark
杰尼橙子32 分钟前
DPDK BPF:将eBPF虚拟机的灵活性带入到了DPDK的高性能用户态
后端·性能优化
代码老y1 小时前
Spring Boot + 本地部署大模型实现:优化与性能提升
java·spring boot·后端
guojl2 小时前
营销画像客群架构
后端
为神敬酒者2 小时前
从银行转账实践理解互斥和同步
后端