动态代理初认识

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

相关推荐
聆风吟º4 分钟前
【Spring Boot 报错已解决】Error creating bean with entityManagerFactory 原因分析与解决方案
java·spring boot·后端
o***74174 分钟前
SpringBoot【十一】mybatis-plus实现多数据源配置,开箱即用!
spring boot·后端·mybatis
S***84885 分钟前
【spring boot】 IDEA 启动springboot项目报missing ServletWebServerFactory
spring boot·后端·intellij-idea
William_cl5 分钟前
【ASP.NET进阶】Controller 层 Action 返回值:HttpStatusCodeResult 状态码返回全解析
后端·asp.net
W***D4556 分钟前
SpringBoot + vue 管理系统
vue.js·spring boot·后端
Ankkaya11 分钟前
小白服务器踩坑
后端
VX:Fegn089513 分钟前
计算机毕业设计|基于springboot + vue毕业设计选题管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
h***673716 分钟前
springboot设置多环境配置文件
java·spring boot·后端
VX:Fegn089517 分钟前
计算机毕设|基springboot+Vue的校园打印系统设计与实现
java·前端·javascript·vue.js·spring boot·后端·课程设计
Dawn1118 分钟前
Nginx相关
后端