目录
- [1. 代理模式简介](#1. 代理模式简介)
- [2. 静态代理](#2. 静态代理)
- [3. 动态代理](#3. 动态代理)
- [3.1 JDK动态代理](#3.1 JDK动态代理)
- [3.2 CGLIB动态代理](#3.2 CGLIB动态代理)
- [4. 面向切面编程(AOP)](#4. 面向切面编程(AOP))
- [5. 实战示例](#5. 实战示例)
- [6. 总结与最佳实践](#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 {
@Override
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("删除用户: " + username);
}
}
// 静态代理类
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
@Override
public void addUser(String username) {
System.out.println("开始添加用户...");
long startTime = System.currentTimeMillis();
userService.addUser(username);
long endTime = System.currentTimeMillis();
System.out.println("添加用户完成,耗时:" + (endTime - startTime) + "ms");
}
@Override
public void deleteUser(String username) {
System.out.println("开始删除用户...");
long startTime = System.currentTimeMillis();
userService.deleteUser(username);
long endTime = System.currentTimeMillis();
System.out.println("删除用户完成,耗时:" + (endTime - startTime) + "ms");
}
}
plain
开始添加用户...
添加用户完成,耗时: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;
}
@Override
public 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("张三");
}
}
plain
开始执行方法: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();
}
@Override
public 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("李四");
}
}
plain
开始执行方法: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
@Override
public void addUser(String username) {
// 业务逻辑
}
}
plain
方法 [addUser] 执行耗时:2ms
介绍 :该示例使用了自定义注解@LogExecutionTime
来标记需要记录执行时间的方法。Spring AOP通过切面对这些方法进行增强,输出了方法名称及其执行的耗时。
6. 总结与最佳实践
代理模式是一种非常实用的设计模式,它可以帮助我们在不修改原有代码的情况下,添加新的功能:
- 静态代理
- 简单直观,但是代理类需要手动编写
- 适用于代理类较少的场景
- JDK动态代理
- 只能代理实现了接口的类
- 运行时动态生成代理类
- 灵活性高
- CGLIB动态代理
- 可以代理未实现接口的类
- 通过继承实现
- 性能较好
- Spring AOP
- 提供了更高层次的抽象
- 支持声明式代理
- 结合了动态代理的优点
使用建议
- 如果目标类有接口,优先使用JDK动态代理
- 如果目标类没有接口,使用CGLIB动态代理
- 在Spring框架中,优先使用Spring AOP,它能自动选择合适的代理方式
注意事项
- 代理模式会增加系统的复杂度
- 动态代理可能会带来一定的性能开销
- 使用CGLib时注意final方法无法被代理
- 在使用Spring AOP时要注意代理对象的作用域
代理模式是Java开发中非常重要的一个设计模式,掌握好它可以帮助我们写出更加优雅和可维护的代码。