动态代理初认识

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

相关推荐
GetcharZp2 分钟前
Hermes Agent:一个真正“会成长”的开源 AI Agent,正在改变 AI 自动化玩法
后端
Gopher_HBo20 分钟前
Go依赖管理
后端
ltl25 分钟前
Layer Normalization:为什么 Transformer 用 LN,不用 BN
后端
ltl33 分钟前
title: 【Transformer 与注意力机制】24|
后端
范什么特西35 分钟前
Spring 动态代理 静态代理
java·后端·spring
醇氧36 分钟前
Spring 动态注册 Bean 深度解析:从源码到实践
java·后端·spring
zb2006412038 分钟前
Laravel7.x十大核心特性解析
spring boot·后端·laravel
明月_清风1 小时前
FastAPI 从入门到实战:3 分钟构建高性能异步 API
后端·python·fastapi
小村儿1 小时前
连载10-Sub-agents 深度解析:从源码理解 Claude Code 的分身术
前端·后端·ai编程
他们叫我阿冠1 小时前
Day5学习--SpringBoot详解
spring boot·后端·学习