代理模式(Proxy Pattern)是Java设计模式中的一种结构型模式,它为其他对象提供了一种代理以控制对这个对象的访问。代理模式的主要思想是通过代理对象来代替实际对象执行某些操作,这样可以增强或控制实际对象的行为。
一、代理模式
1、什么是代理模式
代理模式提供了一个代理对象,代替实际对象处理请求。代理模式可以在不同的场景下使用,例如控制访问权限、延迟加载、提供日志记录等功能。代理模式的核心思想是:
- 代理对象:代表实际对象(真实对象)处理客户端请求。
- 真实对象:实际完成业务逻辑的对象。
2、代理模式的角色:
- Subject(抽象主题):定义代理类和真实类共同实现的接口。
- RealSubject(真实主题):实现具体的业务逻辑。
- Proxy(代理类):在不修改真实对象的前提下,通过代理对象对真实对象的操作进行控制或增强。
二、静态代理模式
1、什么是静态代理
静态代理是在编译期间就实现的代理,它通过创建代理类并实现相同的接口来代理真实对象。静态代理在代理类中增强了方法调用的功能,但这种方式会增加系统中类的数量,因为每一个需要代理的类都需要有一个代理类。
2、静态代理的流程图
- Client(客户端):调用代理对象的方法。
- Proxy(代理类):代理类实现了与真实类相同的接口,在代理类中,客户端的请求会先经过代理,然后再传递给真实对象。
- RealSubject(真实类):实现接口中的具体业务逻辑。
Client ---> Proxy ---> RealSubject
3、静态代理的执行流程
(1)、客户端通过代理对象调用接口中的方法。
(2)、代理对象会在调用接口方法时,增强功能(例如添加日志、权限检查等)。
(3)、代理对象最终会调用真实对象的对应方法来执行实际业务逻辑。
4、静态代理代码示例
1、定义接口
// 定义一个接口Subject
public interface Subject {
void request();
}
2、实现真实类
// 实现真实类RealSubject
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
3、实现代理类
// 实现代理类ProxySubject
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("Proxy: Logging before request.");
realSubject.request();
System.out.println("Proxy: Logging after request.");
}
}
4、编写测试类
public class StaticProxyTest {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxy = new ProxySubject(realSubject);
// 客户端通过代理对象调用方法
proxy.request();
}
}
运行结果:
Proxy: Logging before request.
RealSubject: Handling request.
Proxy: Logging after request.
通过静态代理,我们在调用真实对象的前后增加了日志记录的功能,而不需要修改真实类的代码。这是静态代理的核心优势。
三、动态代理
1、什么是动态代理
动态代理相比静态代理更加灵活,因为动态代理不需要提前编写代理类,而是在运行时通过反射机制动态生成代理对象。Java的java.lang.reflect.Proxy
类和InvocationHandler
接口提供了动态代理的支持。
动态代理常用于AOP(面向切面编程)中,例如在Spring框架中,动态代理经常用于实现事务、日志、权限等横切关注点。
2、动态代理的执行流程
(1)、动态生成代理类,不需要为每一个接口创建单独的代理类。
(2)、代理类实现接口,并在调用时通过InvocationHandler
调用实际对象的方法。
(3)、可以在方法调用前后插入增强功能。
3、动态代理的代码示例
1、定义接口和实现类
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
2、实现InvocationHandler
InvocationHandler
接口是Java动态代理的核心,所有的方法调用都会转发到InvocationHandler
的invoke
方法中。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyHandler implements InvocationHandler {
private Object realObject;
public ProxyHandler(Object realObject) {
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy: Logging before request.");
Object result = method.invoke(realObject, args);
System.out.println("Proxy: Logging after request.");
return result;
}
}
3、创建动态代理
通过Proxy.newProxyInstance()
方法来创建代理对象。
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
// 创建真实对象
RealSubject realSubject = new RealSubject();
// 创建动态代理
Subject proxyInstance = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new ProxyHandler(realSubject)
);
// 通过代理对象调用方法
proxyInstance.request();
}
}
运行结果:
Proxy: Logging before request.
RealSubject: Handling request.
Proxy: Logging after request.
动态代理让我们可以在运行时动态生成代理对象,并且无需手动编写代理类,极大地提高了代码的灵活性和可维护性。
四、代理和核心类的关系
在代理模式中,代理类和核心类(真实类)之间的关系可以总结为以下几点:
-
解耦:代理类与真实类通过接口解耦,客户端无需直接依赖真实类,可以通过代理类访问真实类的功能。
-
增强功能:代理类可以在不修改真实类代码的前提下,增强或改变真实类的行为,例如添加日志、事务管理等功能。
-
控制访问:代理类可以在调用真实类的方法前做一些控制,例如权限检查、延迟加载等。
代理类与核心类的关系可以通过下示进行描述:
Client ---> Proxy ----> RealSubject
(增强功能) (实际业务逻辑)
五、静态代理和动态代理的区别
特性 | 静态代理 | 动态代理 |
实现方式 | 编译时生成代理类 | 运行时动态生成代理类 |
灵活性 | 代理类与真实类紧耦合 | 更加灵活,不需要手动编写代理类 |
维护性 | 随着接口增多,代理类也增多 | 代理类由反射生成,维护成本低 |
使用场景 | 简单场景,代理类较少的场景 | 复杂场景,适用于AOP、事务管理等 |
六、总结
代理模式在Java中应用广泛,无论是静态代理还是动态代理,都能在不修改真实对象的前提下,增强或控制真实对象的行为。静态代理虽然结构简单,但缺乏灵活性;动态代理则利用反射机制,实现了高度的动态性和灵活性。
通过代理模式,我们可以轻松实现日志、权限、事务等功能,大大提高代码的可扩展性和可维护性。