深入理解代理模式(Proxy):静态代理、动态代理与AOP

news/2024/12/13 3:12:24/

目录

  • 1. 代理模式简介
  • 2. 静态代理
  • 3. 动态代理
    • 3.1 JDK动态代理
    • 3.2 CGLIB动态代理
  • 4. 面向切面编程(AOP)
  • 5. 实战示例
  • 6. 总结与最佳实践

1. 代理模式简介

代理模式是一种结构型设计模式,它允许我们提供一个代理来控制对其他对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。

代理模式的UML类图

«interface»
Subject
+request()
RealSubject
+request()
Proxy
-realSubject: RealSubject
+request()

代理模式的主要角色

  • Subject(抽象主题):定义代理类和真实主题的共同接口
  • RealSubject(真实主题):定义代理所代表的真实对象
  • Proxy(代理):包含对真实主题的引用,从而可以操作真实主题

2. 静态代理

静态代理是最简单的代理模式实现方式。代理类在编译时就已经确定,在程序运行前就已经存在代理类的 .class 文件。

示例代码

java">// 抽象主题接口
public interface UserService {void addUser(String username);void deleteUser(String username);
}// 真实主题类
public class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("添加用户: " + username);}@Overridepublic void deleteUser(String username) {System.out.println("删除用户: " + username);}
}// 静态代理类
public class UserServiceProxy implements UserService {private UserService userService;public UserServiceProxy(UserService userService) {this.userService = userService;}@Overridepublic void addUser(String username) {System.out.println("开始添加用户...");long startTime = System.currentTimeMillis();userService.addUser(username);long endTime = System.currentTimeMillis();System.out.println("添加用户完成,耗时:" + (endTime - startTime) + "ms");}@Overridepublic void deleteUser(String username) {System.out.println("开始删除用户...");long startTime = System.currentTimeMillis();userService.deleteUser(username);long endTime = System.currentTimeMillis();System.out.println("删除用户完成,耗时:" + (endTime - startTime) + "ms");}
}
开始添加用户...
添加用户完成,耗时:1ms
开始删除用户...
删除用户完成,耗时:1ms

介绍:静态代理通过代理类为目标方法添加了额外的功能(如记录执行时间),并且不需要修改目标类的代码。上述输出表示在添加用户和删除用户时,代理类能够控制方法执行并统计耗时。

静态代理的优缺点

优点:

  • 可以在不修改目标对象的前提下,实现对目标对象的功能扩展
  • 实现简单,容易理解

缺点:

  • 为每个服务都需要创建代理类,工作量大
  • 不易维护,一旦接口增加方法,所有代理类都需要修改

3. 动态代理

动态代理是在程序运行时,通过反射机制动态创建代理类的代理模式实现方式。

3.1 JDK动态代理

JDK动态代理只能代理实现了接口的类。

java">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 动态代理处理器
public class LogHandler implements InvocationHandler {private Object target;public LogHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开始执行方法:" + method.getName());long startTime = System.currentTimeMillis();Object result = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println("方法执行完成,耗时:" + (endTime - startTime) + "ms");return result;}// 获取代理对象public static Object getProxy(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new LogHandler(target));}
}// 使用示例
public class DynamicProxyDemo {public static void main(String[] args) {UserService userService = new UserServiceImpl();UserService proxy = (UserService) LogHandler.getProxy(userService);proxy.addUser("张三");proxy.deleteUser("张三");}
}
开始执行方法:addUser
方法执行完成,耗时:1ms
开始执行方法:deleteUser
方法执行完成,耗时:1ms

介绍:使用JDK动态代理,代理对象的行为由InvocationHandler接口的invoke方法决定。这里我们添加了日志记录功能,输出了执行方法的时间统计。

3.2 CGLIB动态代理

CGLIB可以代理没有实现接口的类,通过继承被代理类来实现代理。

java">import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {private Object target;public CglibProxy(Object target) {this.target = target;}public Object getProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("开始执行方法:" + method.getName());long startTime = System.currentTimeMillis();Object result = proxy.invokeSuper(obj, args);long endTime = System.currentTimeMillis();System.out.println("方法执行完成,耗时:" + (endTime - startTime) + "ms");return result;}
}// 使用示例
public class CglibProxyDemo {public static void main(String[] args) {UserService userService = new UserServiceImpl();UserService proxy = (UserService) new CglibProxy(userService).getProxy();proxy.addUser("李四");proxy.deleteUser("李四");}
}
开始执行方法:addUser
方法执行完成,耗时:1ms
开始执行方法:deleteUser
方法执行完成,耗时:1ms

介绍:CGLIB动态代理通过继承目标类来生成代理类,它不要求目标类实现接口。类似于JDK动态代理,这里输出的日志同样记录了方法执行的时间。

4. 面向切面编程(AOP)

AOP是一种编程范式,它通过预编译方式和运行期动态代理实现程序功能的统一维护。

Spring AOP示例

java">import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect
@Component
public class LoggingAspect {@Around("execution(com.example.service..(..))")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed();long endTime = System.currentTimeMillis();System.out.println(joinPoint.getSignature() + " 执行耗时:" + (endTime - startTime) + "ms");return result;}@Before("execution(com.example.service..(..))")public void logBefore() {System.out.println("方法执行前的处理...");}@After("execution(com.example.service..(..))")public void logAfter() {System.out.println("方法执行后的处理...");}
}

5. 实战示例

下面是一个完整的实战示例,展示如何使用代理模式实现方法执行时间统计和日志记录。

java">// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
}// 切面类
@Aspect
@Component
public class MethodExecutionAspect {private static final Logger logger = LoggerFactory.getLogger(MethodExecutionAspect.class);@Around("@annotation(LogExecutionTime)")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed();long endTime = System.currentTimeMillis();logger.info("方法 [{}] 执行耗时:{}ms",joinPoint.getSignature().getName(), (endTime - startTime));return result;}
}// 使用示例
@Service
public class UserServiceImpl implements UserService {@LogExecutionTime@Overridepublic void addUser(String username) {// 业务逻辑}
}
方法 [addUser] 执行耗时:2ms

介绍:该示例使用了自定义注解@LogExecutionTime来标记需要记录执行时间的方法。Spring AOP通过切面对这些方法进行增强,输出了方法名称及其执行的耗时。

6. 总结与最佳实践

代理模式是一种非常实用的设计模式,它可以帮助我们在不修改原有代码的情况下,添加新的功能:

  1. 静态代理
    • 简单直观,但是代理类需要手动编写
    • 适用于代理类较少的场景
  2. JDK动态代理
    • 只能代理实现了接口的类
    • 运行时动态生成代理类
    • 灵活性高
  3. CGLIB动态代理
    • 可以代理未实现接口的类
    • 通过继承实现
    • 性能较好
  4. Spring AOP
    • 提供了更高层次的抽象
    • 支持声明式代理
    • 结合了动态代理的优点

使用建议

  1. 如果目标类有接口,优先使用JDK动态代理
  2. 如果目标类没有接口,使用CGLIB动态代理
  3. 在Spring框架中,优先使用Spring AOP,它能自动选择合适的代理方式

注意事项

  1. 代理模式会增加系统的复杂度
  2. 动态代理可能会带来一定的性能开销
  3. 使用CGLib时注意final方法无法被代理
  4. 在使用Spring AOP时要注意代理对象的作用域

代理模式是Java开发中非常重要的一个设计模式,掌握好它可以帮助我们写出更加优雅和可维护的代码。


http://www.ppmy.cn/news/1554653.html

相关文章

yarn 安装问题

Couldn’t find package “regenerator-runtime” on the “npm” registry. Error: Couldn’t find package “watch-size” on the “npm” regist 标题Error: Couldn’t find package “babel-helper-vue-jsx-merge-props” on the “npm” registry. Error: Couldn’t f…

go语言的成神之路-标准库篇-os标准库

一、权限 在操作系统(OS)中,标准库的权限管理是非常重要的,它确保了不同用户和进程能够安全地访问系统资源。以下是一些常见的权限概念和说明: 1.用户权限 用户ID(UID):每个用户在…

福昕PDF低代码平台

福昕PDF低代码平台简介 福昕PDF 低代码平台是一款创新的工具,旨在简化PDF处理和管理的流程。通过这个平台,用户可以通过简单的拖拽界面上的按钮,轻松完成对Cloud API的调用工作流,而无需编写复杂的代码。这使得即使没有编程经验的…

计算机毕设-基于springboot的高校电动车租赁服务业务系统的设计与实现(附源码+lw+ppt+开题报告)

博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

webstorm开发uniapp(从安装到项目运行)

1、下载uniapp插件 下载连接:Uniapp Tool - IntelliJ IDEs Plugin | Marketplace (结合自己的webstorm版本下载,不然解析不了) 将下载到的zip文件防在webstorm安装路径下,本文的地址为: 2、安装uniapp插…

springBoot中的日志级别在哪里配置

在Spring Boot中,日志级别的配置可以通过多种方式来实现,主要包括在配置文件中设置、使用自定义的logback配置文件,以及在代码中动态配置等。以下是一些具体的配置方法: 一、在配置文件中设置日志级别 Spring Boot默认使用appli…

枫清科技高雪峰:从数据到知识,重塑产业智能化的核心驱动力

2024 年 12 月 5 日,由智东西主办的“2024 中国生成式 AI 大会”在上海盛大开幕,汇聚了全球 AI 领域的顶尖专家、行业领袖与技术创新者。枫清科技(Fabarta)创始人兼 CEO 高雪峰应邀出席,并在大会上发表主题演讲&#x…

详细解析RNNoise:基于深度学习的语音噪声抑制技术

引言 在语音通信、语音识别以及音频处理领域,噪声抑制是一个至关重要的任务。环境噪声,如风声、交通声和人群声,通常会影响语音的清晰度和质量,特别是在远程通信和在线会议中。为了提高语音质量,许多噪声抑制技术应运…