在Java中,代理模式允许一个对象充当另一个对象的代表,以控制对该对象的访问。代理模式通常有两种实现方式:静态代理和动态代理。以下是它们之间的主要区别和特点:
编译时生成:
代理类定义:
使用场景:
灵活性:
以下是一个简单的静态代理的Java示例。在这个示例中,我们将创建一个接口Subject
,然后创建一个目标类RealSubject
和一个代理类ProxySubject
,代理类将在目标类的方法前后执行额外的操作。
java// 接口
interface Subject {
void doSomething();
}
// 目标类
class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
// 代理类
class ProxySubject implements Subject {
private Subject realSubject;
public ProxySubject(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public void doSomething() {
// 在调用目标类方法前执行额外操作
System.out.println("Before calling RealSubject.doSomething()");
// 调用目标类的方法
realSubject.doSomething();
// 在调用目标类方法后执行额外操作
System.out.println("After calling RealSubject.doSomething()");
}
}
public class StaticProxyExample {
public static void main(String[] args) {
// 创建目标类实例
RealSubject realSubject = new RealSubject();
// 创建代理类实例,将目标类实例传入代理类构造函数
ProxySubject proxy = new ProxySubject(realSubject);
// 通过代理类调用目标类的方法
proxy.doSomething();
}
}
在上述示例中,Subject
是一个接口,RealSubject
是目标类,实现了Subject
接口的方法。ProxySubject
是代理类,也实现了Subject
接口,它接受一个RealSubject
对象作为构造函数的参数。
当我们通过代理类调用doSomething
方法时,代理类在调用目标类的方法前后分别执行了额外的操作。这使得代理类可以在不修改目标类代码的情况下增加新的功能或控制访问。
静态代理的优点是简单易懂,但缺点是每个代理类只能代理一个具体的目标类,因此如果有多个目标类需要代理,就需要创建多个代理类。此外,静态代理在编译时生成代理类,不够灵活。在实际应用中,通常会选择使用动态代理来解决这些问题。
运行时生成:
代理类定义:
使用场景:
灵活性:
Java提供了两种动态代理的实现方式:基于接口的代理和基于类的代理。基于接口的代理使用java.lang.reflect.Proxy
类,它要求目标类实现一个或多个接口。基于类的代理使用字节码生成库,如CGLIB,可以代理没有实现接口的类。这两种动态代理方式都允许在运行时创建代理类,但使用不同的底层技术。
综上所述,静态代理和动态代理都有各自的优势和适用场景。静态代理适用于已知目标类和代理类的情况,而动态代理适用于不知道目标类的具体类型或需要在运行时灵活控制代理行为的情况。选择哪种代理方式取决于您的需求和项目的特点。
以下是一个使用JDK动态代理的简单示例。在这个示例中,我们将创建一个接口Subject
,然后使用JDK动态代理来创建代理对象,代理对象将在目标方法调用前后执行额外的操作。
javaimport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 接口
interface Subject {
void doSomething();
}
// 目标类
class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
// InvocationHandler实现类
class MyInvocationHandler implements InvocationHandler {
private Subject realSubject;
public MyInvocationHandler(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用目标方法前执行额外操作
System.out.println("Before calling RealSubject.doSomething()");
// 调用目标方法
Object result = method.invoke(realSubject, args);
// 在调用目标方法后执行额外操作
System.out.println("After calling RealSubject.doSomething()");
return result;
}
}
public class DynamicProxyExample {
public static void main(String[] args) {
// 创建目标类实例
RealSubject realSubject = new RealSubject();
// 创建InvocationHandler实现类实例,传入目标类实例
MyInvocationHandler handler = new MyInvocationHandler(realSubject);
// 使用Proxy类创建代理对象
Subject proxy = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class<?>[] { Subject.class },
handler
);
// 通过代理对象调用目标类的方法
proxy.doSomething();
}
}
在上述示例中,我们首先定义了一个接口Subject
和一个目标类RealSubject
,然后创建了一个MyInvocationHandler
类,实现了InvocationHandler
接口。MyInvocationHandler
类负责在调用目标方法前后执行额外的操作。
接着,我们使用Proxy.newProxyInstance
方法创建了代理对象,该方法接受三个参数:类加载器、目标类实现的接口数组以及一个InvocationHandler
实例。通过代理对象调用doSomething
方法时,MyInvocationHandler
中的invoke
方法将会被调用,从而实现了对目标方法的拦截和增强。
JDK动态代理适用于目标类实现了接口的情况,并且在运行时创建代理对象。它允许您在不修改目标类代码的情况下,通过代理对象添加新的功能或控制访问。这种方式非常适合于实现AOP(面向切面编程)等场景。
以下是一个使用CGLIB动态代理的简单示例。在这个示例中,我们将创建一个目标类RealSubject
,然后使用CGLIB动态代理来创建代理对象,代理对象将在目标方法调用前后执行额外的操作。
首先,您需要添加CGLIB库到您的项目中。通常,您可以使用Maven或Gradle等构建工具来添加依赖项。以下是Maven的依赖项配置:
xml<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 或者您选择的版本 -->
</dependency>
接下来,让我们创建一个目标类和一个代理类:
javaimport net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
// 目标类
class RealSubject {
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
// 代理类
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在调用目标方法前执行额外操作
System.out.println("Before calling RealSubject.doSomething()");
// 调用目标方法
Object result = proxy.invokeSuper(obj, args);
// 在调用目标方法后执行额外操作
System.out.println("After calling RealSubject.doSomething()");
return result;
}
}
public class CglibProxyExample {
public static void main(String[] args) {
// 创建Enhancer对象,用于生成代理类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class); // 设置目标类
enhancer.setCallback(new MyMethodInterceptor()); // 设置方法拦截器
// 创建代理对象
RealSubject proxy = (RealSubject) enhancer.create();
// 通过代理对象调用目标类的方法
proxy.doSomething();
}
}
在上述示例中,RealSubject
是目标类,它并没有实现任何接口。然后,我们使用Enhancer
类来生成代理类,设置目标类和方法拦截器。MyMethodInterceptor
是一个实现了CGLIB的MethodInterceptor
接口的类,它拦截了目标类的方法调用,并在方法调用前后执行额外的操作。
最后,我们创建了代理对象并通过代理对象调用了目标类的doSomething
方法。代理对象会在调用前后执行额外操作,实现了对目标方法的拦截和增强。
CGLIB动态代理适用于目标类没有实现接口的情况,它通过字节码生成库生成代理类,通常比JDK动态代理更快,但生成代理类的过程可能会更慢。这种方式非常适合于需要代理没有实现接口的类的场景。