目录
一、定义与原理
- 定义:代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户和目标对象之间起到中介的作用。
- 原理:代理模式涉及三个角色------抽象角色(定义了代理和被代理对象的共同接口)、代理角色(代理对象持有被代理对象的引用,在代理对象上实现客户请求的方法,并通过调用被代理对象的方法来实现客户请求)、真实角色(被代理的对象,负责执行具体的业务逻辑)。
二、优点
- 可扩展目标对象的功能:通过代理对象可以在不修改目标对象的前提下,增加额外的功能。
- 降低系统耦合度:客户端通过代理对象与目标对象进行交互,降低了客户端与目标对象之间的直接依赖,从而提高了系统的扩展性和灵活性。
- 保护目标对象:代理对象可以对客户端的请求进行过滤、验证等处理,从而保护目标对象不受非法请求的影响。
三、缺点
- 类的数量增加:使用代理模式会增加系统中的类数量,从而增加了系统的复杂度。
- 请求处理速度变慢:由于客户端的请求需要经过代理对象处理后再转发给目标对象,因此可能会增加请求处理的时间。
- 增加了系统的复杂度:代理模式的实现需要额外的类和接口,增加了系统的理解和维护成本。
四、实现方式
代理模式可以分为静态代理和动态代理两种实现方式:
- 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。静态代理在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
- 动态代理:在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。动态代理的实现方式主要有JDK原生动态代理和CGLIB动态代理。JDK原生动态代理基于接口实现,而CGLIB动态代理基于继承实现。
五、应用场景
代理模式在软件开发中有着广泛的应用,包括但不限于以下几个方面:
- 远程代理:为位于不同地址空间的对象提供局部代表,从而隐藏远程调用的细节。
- 虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化对象的引用。
- 安全代理:控制对原始对象的访问权限,增加访问控制的安全性。
- 智能指引:调用者通过代理引用间接调用目标对象的方法,代理引用可以在将调用请求提交给目标对象前后执行一些附加操作。
总之,代理模式是一种非常有用的设计模式,它可以在不修改目标对象的前提下,增加额外的功能、降低系统耦合度、保护目标对象等。在实际开发中,我们可以根据具体需求选择合适的代理模式实现方式。
六、举例
静态代理
静态代理通常是在编译时就已经确定代理类的具体实现。这种方式需要程序员手动编写代理类的代码。
假设我们有一个接口Subject
和一个实现了该接口的类RealSubject
,我们想要为RealSubject
创建一个代理类StaticProxy
。
Subject.java
java
public interface Subject {
void request();
}
RealSubject.java
java
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("Executing real request.");
}
}
StaticProxy.java
java
public class StaticProxy implements Subject {
private RealSubject realSubject;
public StaticProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 在调用真实对象的方法之前,可以添加一些自定义操作
beforeRequest();
realSubject.request();
// 在调用真实对象的方法之后,可以添加一些自定义操作
afterRequest();
}
private void beforeRequest() {
System.out.println("Before real request.");
}
private void afterRequest() {
System.out.println("After real request.");
}
}
Client.java
java
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject proxy = new StaticProxy(realSubject);
proxy.request();
}
}
JDK动态代理
动态代理是在运行时动态生成代理类的字节码,并加载到JVM中。Java提供了两种主要的动态代理机制:基于接口的JDK动态代理和基于类的CGLIB动态代理。这里以JDK动态代理为例。
Subject.java(与静态代理示例相同)
InvocationHandler.java (实现InvocationHandler
接口)
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;
public InvocationHandlerImpl(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用真实对象的方法之前,可以添加一些自定义操作
beforeRequest();
// 调用真实对象的方法
Object result = method.invoke(target, args);
// 在调用真实对象的方法之后,可以添加一些自定义操作
afterRequest();
return result;
}
private void beforeRequest() {
System.out.println("Before method invocation.");
}
private void afterRequest() {
System.out.println("After method invocation.");
}
}
Client.java(使用JDK动态代理)
java
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
// 创建InvocationHandler实例
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
// 获取与RealSubject同接口的代理实例
Subject proxy = (Subject) Proxy.newProxyInstance(
RealSubject.class.getClassLoader(),
new Class[]{Subject.class},
handler);
// 通过代理实例调用方法
proxy.request();
}
}
在这个动态代理的例子中,我们并没有显式地编写代理类,而是通过Proxy.newProxyInstance()
方法在运行时动态地创建了代理类的实例。这个代理实例实现了与RealSubject
相同的接口,并在调用接口方法时,通过InvocationHandler
的invoke
方法添加了额外的逻辑。
CGLIB动态代理
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,用于在运行时扩展Java类和实现接口。它通常用于实现基于类的动态代理,而不需要目标类实现任何接口。以下是一个使用CGLIB动态代理的例子。
首先,确保你的项目中包含了CGLIB的依赖。如果你使用Maven,可以在pom.xml
中添加如下依赖:
java
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
接下来,我们定义一个不需要实现任何接口的类RealSubject
,然后创建一个实现了MethodInterceptor
接口的CGLIB代理拦截器,最后通过CGLIB的Enhancer
类来创建代理对象。
RealSubject.java
java
public class RealSubject {
public void someMethod() {
System.out.println("Executing real method.");
}
}
MethodInterceptorImpl.java
java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MethodInterceptorImpl implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在调用真实对象的方法之前,可以添加一些自定义操作
beforeMethod();
// 调用真实对象的方法
Object result = proxy.invokeSuper(obj, args);
// 在调用真实对象的方法之后,可以添加一些自定义操作
afterMethod();
return result;
}
private void beforeMethod() {
System.out.println("Before method execution.");
}
private void afterMethod() {
System.out.println("After method execution.");
}
}
Client.java
java
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 创建目标对象
RealSubject realSubject = new RealSubject();
// 创建Enhancer对象,并设置其父类(即目标类)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
// 设置回调(即拦截器)
enhancer.setCallback(new MethodInterceptorImpl());
// 创建代理对象
RealSubject proxy = (RealSubject) enhancer.create();
// 通过代理对象调用方法
proxy.someMethod();
}
}
在这个例子中,RealSubject
类没有实现任何接口,但我们仍然能够通过CGLIB为它创建一个代理对象。我们通过实现MethodInterceptor
接口并定义intercept
方法来定义在调用目标方法前后要执行的操作。然后,我们使用Enhancer
类来生成代理对象,并指定目标类和拦截器。最后,我们通过代理对象调用方法,intercept
方法中的逻辑就会在调用真实方法之前和之后被执行。