Java基础学习: 代理模式(Proxy Pattern)

文章目录

一、简介

1、介绍

代理模式

2、代理模式在Java中的应用

  • 统一异常处理。
  • Mybatis使用了代理。
  • Spring aop实现代理。
  • 日志框架。

二、实现方式

1、静态代理

在Java中,静态代理是一种代理模式,它涉及为另一个对象(目标对象)提供一个代理对象,并由代理对象控制对目标对象的访问。这种代理模式在编译时就已经确定代理类,因此被称为"静态代理"。

静态代理通常包含以下角色:

  • 目标对象(Target):需要被代理的真实对象。
  • 代理对象(Proxy):持有目标对象的引用,并控制对目标对象的访问。代理对象与目标对象实现了相同的接口(或在Java中,它们可能是同一个类的子类)。
  • 接口(Interface):定义了目标对象和代理对象都需要实现的方法。

静态代理的基本实现步骤

  • 定义一个接口,该接口声明了目标对象与代理对象共有的方法。
  • 创建目标对象,实现接口并定义真实业务逻辑。
  • 创建代理对象,同样实现该接口,并在方法内部调用目标对象的方法,同时可以在调用前后添加额外的逻辑。
  • 客户端代码通过代理对象来调用目标对象的方法。

示例代码

// 接口  
public interface UserService {  
    void doSomething();  
}  
  
// 目标对象  
public class UserServiceImpl implements UserService {  
    @Override  
    public void doSomething() {  
        System.out.println("执行真正的业务逻辑");  
    }  
}  
  
// 代理对象  
public class UserServiceProxy implements UserService {  
    private UserService userService;  
  
    public UserServiceProxy(UserService userService) {  
        this.userService = userService;  
    }  
  
    @Override  
    public void doSomething() {  
        System.out.println("在目标方法执行前执行的逻辑");  
        userService.doSomething();  
        System.out.println("在目标方法执行后执行的逻辑");  
    }  
}  
  
// 客户端代码  
public class Client {  
    public static void main(String[] args) {  
        UserService userService = new UserServiceImpl();  
        UserService proxy = new UserServiceProxy(userService);  
        proxy.doSomething(); // 调用代理对象的方法,实际上执行了目标对象的方法,并添加了额外逻辑  
    }  
}

静态代理灵活性较差,每当接口增加或修改方法时,代理类也需要相应地进行修改。对于不同的接口,需要编写不同的代理类,这可能导致"类爆炸"的问题。

2、动态代理

动态代理 是一种在运行时动态地创建代理类及其实例的技术。与静态代理不同,动态代理不需要手动编写代理类的代码,而是在运行时根据目标对象的接口或父类动态地生成代理类的字节码,并加载到JVM中,然后创建代理类的实例。这种代理方式的优点在于其高度的灵活性和可扩展性。

在Java中,实现动态代理主要有两种方式

  • JDK动态代理:基于Java的反射机制实现。JDK动态代理要求目标对象必须实现一个或多个接口,因为代理类是通过继承java.lang.reflect.Proxy类并实现与目标对象相同的接口来创建的。因此,JDK动态代理主要用于对接口进行代理。
  • CGLIB动态代理:基于ASM(一个通用的Java字节码操作和分析框架)库实现对类的字节码操作。与JDK动态代理不同,CGLIB动态代理是通过继承目标类来创建代理类的,因此它主要用于对没有实现接口的类进行代理。由于CGLIB是通过继承目标类来创建代理的,因此不能代理final类(因为final类不能被继承),同时目标类中的final方法也会被忽略(因为final方法不能被重写)。

动态代理的主要应用场景包括AOP(面向切面编程)、远程方法调用(RMI)等。在这些场景中,动态代理可以方便地为目标对象添加额外的功能(如日志记录、性能监控、事务管理等),而无需修改目标对象的代码。

三、动态代理

1、JDK

JDK动态代理

2、Cglib

CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以在运行时扩展Java类和实现接口,而无需修改字节码。CGLIB被广泛用于AOP(面向切面编程)框架中,如Spring AOP,以在运行时动态地创建代理对象。

CGLIB与JDK动态代理的区别

  • JDK动态代理:基于接口实现,代理类必须是某个接口的实现。通过反射机制生成代理类的class字节码,并将其加载到Java虚拟机中。然后基于该接口动态生成代理实例,在调用具体方法前调用InvokeHandler来处理。
  • CGLIB动态代理:基于类实现,代理类继承被代理类(通过字节码技术继承)。CGLIB动态代理利用ASM库来转换字节码并生成新的类,比使用Java反射API生成代理类的速度更快。

CGLIB动态代理的工作原理

  • 创建Enhancer对象:Enhancer是CGLIB的核心类,用于创建一个子类对象(即代理对象)。
  • 设置回调函数:通过Enhancer的setCallback()或setCallbacks()方法设置当调用代理对象方法时被调用的方法。
  • 创建代理对象:通过Enhancer的create()方法创建代理对象。
  • 调用代理对象的方法:当调用代理对象的方法时,会先调用Enhancer中设置的回调函数(Callback),然后再调用被代理对象的方法。

CGLIB动态代理的示例

import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
import java.lang.reflect.Method;  
  
public class CglibProxyDemo {  
  
    public static void main(String[] args) {  
        // 创建Enhancer对象,用于创建目标类的子类  
        Enhancer enhancer = new Enhancer();  
        // 设置父类,即目标类  
        enhancer.setSuperclass(RealSubject.class);  
        // 设置回调函数  
        enhancer.setCallback(new MethodInterceptor() {  
            @Override  
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
                // 在调用目标方法之前执行的操作  
                System.out.println("Before method " + method.getName());  
                // 调用目标方法  
                Object result = proxy.invokeSuper(obj, args);  
                // 在调用目标方法之后执行的操作  
                System.out.println("After method " + method.getName());  
                return result;  
            }  
        });  
        // 创建代理对象  
        RealSubject proxyObj = (RealSubject) enhancer.create();  
        // 调用代理对象的方法  
        proxyObj.doSomething();  
    }  
  
    // 目标类  
    static class RealSubject {  
        public void doSomething() {  
            System.out.println("RealSubject: doSomething");  
        }  
    }  
}

在这个示例中,我们使用了CGLIB来创建一个RealSubject类的子类(即代理类),并在调用doSomething()方法时添加了额外的操作(打印方法调用前后的日志)。

3、总结

参考

相关推荐
码上有前18 分钟前
Java高级教程:数据结构、集合框架、ArrayList与LinkedList深度解析
java·数据结构·python
榕树子29 分钟前
[java] 什么是 Apache Felix
java·开发语言·apache
极客先躯31 分钟前
高级java每日一道面试题-2024年11月23日-JVM篇-什么时候会出发FullGC?
java·jvm·fullgc·jvm篇·老年代内存不足·system.gc·减少full gc的策略
美式小田36 分钟前
Altium Designer学习笔记 21.PCB板框的评估及叠层设置
笔记·嵌入式硬件·学习·ad
飞滕人生TYF1 小时前
斐波那契数列 相关问题 详解
java·数学·动态规划·递归·斐波那契数列
无限大.1 小时前
C++ String
java·开发语言·c++
NightCyberpunk1 小时前
请求响应(学习笔记)
笔记·学习
paj1234567891 小时前
JDK1.8 Stream流使用
java·windows
企业通用软件开发1 小时前
大语言模型提示词工程学习--写小说系列(文心一言&豆包&通义千问):2~确定核心谜团
学习·语言模型·ai应用·ai编写长篇小说·大语言模型应用
南山十一少2 小时前
Spring Boot 实战:基于 Validation 注解实现分层数据校验与校验异常拦截器统一返回处理
java·前端·spring boot·后端