代理模式(Proxy Pattern)是一种结构型设计模式,它提供了一个代理对象,用来控制对另一个对象的访问。这种模式通常用于在访问对象时引入额外的功能,而不改变对象的接口。代理模式的核心思想是为其他对象提供一种代理,以控制对这个对象的访问。
在现实生活中,代理模式的典型例子是房屋中介。购房者并不会直接联系房主,而是通过中介进行房屋的购买,这个中介相当于代理。
代理模式的典型结构如下:
在 Java 中,代理模式常见的应用场景包括远程代理、虚拟代理、保护代理和智能引用代理。
类图如下:
代理模式可以分为静态代理和动态代理两种形式。
静态代理是在编译时确定代理类,通过在代理类中预先定义代理对象和目标对象之间的关系。实现相对简单,但扩展性和灵活性有一定局限。
假设在电商交易系统中,我们有一个订单处理服务接口OrderService
,实现类负责订单的具体处理。我们希望在订单处理前后记录日志,可以使用静态代理来实现。
// 定义订单处理服务接口
public interface OrderService {
void processOrder(String orderId);
}
// 订单处理服务实现类
public class OrderServiceImpl implements OrderService {
@Override
public void processOrder(String orderId) {
System.out.println("Processing order: " + orderId);
}
}
// 静态代理类
public class OrderServiceProxy implements OrderService {
private OrderServiceImpl orderService;
public OrderServiceProxy(OrderServiceImpl orderService) {
this.orderService = orderService;
}
@Override
public void processOrder(String orderId) {
// 在处理订单前记录日志
System.out.println("Logging: Before processing order " + orderId);
// 调用实际的订单处理方法
orderService.processOrder(orderId);
// 在处理订单后记录日志
System.out.println("Logging: After processing order " + orderId);
}
}
// 客户端调用示例
public class Main {
public static void main(String[] args) {
OrderServiceImpl orderService = new OrderServiceImpl();
OrderServiceProxy proxy = new OrderServiceProxy(orderService);
proxy.processOrder("12345");
}
}
执行上述代码,输出结果如下:
Logging: Before processing order 12345
Processing order: 12345
Logging: After processing order 12345
通过静态代理,我们成功在订单处理前后插入了日志记录的逻辑。
动态代理是在运行时创建代理类,能够动态处理目标对象的方法调用。Java中的动态代理主要依赖java.lang.reflect.Proxy
类(JDK动态代理)和CGLIB库(CGLIB动态代理)实现。相比静态代理,动态代理更加灵活。
继续以电商交易系统为例,我们希望在订单处理前后记录日志,但这次使用动态代理来实现。
JDK动态代理只能代理实现了接口的类。它通过Proxy.newProxyInstance
方法在运行时生成代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 动态代理处理器
public class LoggingInvocationHandler implements InvocationHandler {
private Object target;
public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用前记录日志
System.out.println("Logging: Before method " + method.getName());
// 调用实际的目标对象方法
Object result = method.invoke(target, args);
// 在方法调用后记录日志
System.out.println("Logging: After method " + method.getName());
return result;
}
}
// 客户端调用示例
public class Main {
public static void main(String[] args) {
// 创建目标对象
OrderServiceImpl orderService = new OrderServiceImpl();
// 创建动态代理
OrderService proxy = (OrderService) Proxy.newProxyInstance(
orderService.getClass().getClassLoader(),
orderService.getClass().getInterfaces(),
new LoggingInvocationHandler(orderService)
);
// 调用代理对象的方法
proxy.processOrder("12345");
}
}
CGLIB动态代理通过生成目标类的子类来创建代理对象,因此即使目标类没有实现接口,也可以使用CGLIB进行代理。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// CGLIB动态代理处理器
public class LoggingInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在方法调用前记录日志
System.out.println("Logging: Before method " + method.getName());
// 调用实际的目标对象方法
Object result = proxy.invokeSuper(obj, args);
// 在方法调用后记录日志
System.out.println("Logging: After method " + method.getName());
return result;
}
}
// 客户端调用示例
public class Main {
public static void main(String[] args) {
// 创建目标对象
OrderServiceImpl orderService = new OrderServiceImpl();
// 创建CGLIB动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new LoggingInterceptor());
OrderServiceImpl proxy = (OrderServiceImpl) enhancer.create();
// 调用代理对象的方法
proxy.processOrder("12345");
}
}
无论是JDK动态代理还是CGLIB动态代理,执行上述代码后的输出结果类似:
Logging: Before method processOrder
Processing order: 12345
Logging: After method processOrder
动态代理在运行时生成代理对象,并在方法执行前后插入日志逻辑。与静态代理不同的是,动态代理更具灵活性,特别是当接口或目标类有多个方法或复杂逻辑时。
java.lang.reflect.Proxy
类,性能较CGLIB略高,但在处理大量方法时会受到一定限制。1. 代理模式 vs 装饰器模式
示例:
// 代理模式中的订单处理
class LoggingOrderProxy implements OrderService {
private RealOrderService realOrderService;
public LoggingOrderProxy(RealOrderService realOrderService) {
this.realOrderService = realOrderService;
}
public void processOrder(String orderId) {
logOrder(orderId);
realOrderService.processOrder(orderId);
}
private void logOrder(String orderId) {
System.out.println("Logging order: " + orderId);
}
}
// 装饰器模式中的订单处理
class DiscountOrderDecorator extends RealOrderService {
private RealOrderService realOrderService;
public DiscountOrderDecorator(RealOrderService realOrderService) {
this.realOrderService = realOrderService;
}
public void processOrder(String orderId) {
applyDiscount(orderId);
realOrderService.processOrder(orderId);
}
private void applyDiscount(String orderId) {
System.out.println("Applying discount to order: " + orderId);
}
}
在这个示例中,代理模式和装饰器模式都扩展了订单处理功能,但它们的设计意图和应用方式有所不同。
2. 代理模式 vs 中介者模式
3. 代理模式 vs 外观模式
优点:
缺点:
在Java的开源框架中,代理模式(Proxy Pattern)被广泛应用,其中最为典型的就是Spring AOP(面向切面编程)。Spring AOP通过代理对象拦截方法调用,注入横切关注点(如事务管理、日志记录等),从而实现业务逻辑与非功能性需求的解耦。
Spring AOP在实现代理时,主要依赖两种方式:JDK动态代理和CGLIB代理。
以下我们通过一个实际的Spring AOP示例,来详细讲解代理模式在Spring中的应用。
首先,我们定义一个简单的服务接口及其实现类。
public interface UserService {
void createUser(String username);
}
public class UserServiceImpl implements UserService {
@Override
public void createUser(String username) {
System.out.println("User " + username + " has been created.");
}
}
接下来,我们定义一个切面类,用于在方法执行前后添加日志。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.UserService.createUser(..))")
public void logBefore() {
System.out.println("Before creating user");
}
@After("execution(* com.example.UserService.createUser(..))")
public void logAfter() {
System.out.println("After creating user");
}
}
在这个切面类中,我们使用了@Before
和@After
注解来定义在createUser
方法执行前后的日志操作。
为了使Spring能够识别并应用切面类,我们需要进行相应的配置。
在这个XML配置文件中,我们使用了和
标签来配置AOP,指定在哪些方法调用前后执行切面逻辑。
当我们通过Spring容器获取UserService
的代理对象并调用createUser
方法时,AOP代理会拦截方法调用,并执行我们在切面类中定义的日志操作。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.createUser("JohnDoe");
执行上面的代码,输出结果如下:
Before creating user
User JohnDoe has been created.
After creating user
从输出结果可以看出,createUser
方法的执行被代理对象拦截,且在方法执行前后插入了日志操作。通过代理模式,Spring AOP实现了业务逻辑与日志记录的解耦,使得代码更加模块化和可维护。
在Spring AOP中使用代理模式,开发者可以非常方便地将横切关注点(如事务、日志、安全性)独立出来,避免这些非功能性需求侵入核心业务逻辑。代理模式使得代码更加清晰,并且能够在不修改业务代码的情况下,灵活地添加或移除横切关注点。
在这个示例中,LoggingAspect
使用了 AOP 代理来在订单处理之前记录日志。这种方式极大地简化了业务逻辑的实现,同时保持了代码的整洁性和可维护性。
代理模式在 Java 开发中是一个非常实用的模式,尤其是在权限控制、日志记录、性能优化等方面。通过代理模式,开发者可以在不修改原始代码的情况下,动态地增加功能,同时保持系统的稳定性和扩展性。
在设计模式中,代理模式、装饰器模式、中介者模式和外观模式有着各自的应用场景和设计意图。理解它们之间的区别,并在实际项目中合理应用,是提高系统设计质量的重要一步。