2023-09-05
JAVA
0

目录

静态代理:
动态代理:
Java的动态代理实现:
JDK代理
CGLIB代理

在Java中,代理模式允许一个对象充当另一个对象的代表,以控制对该对象的访问。代理模式通常有两种实现方式:静态代理和动态代理。以下是它们之间的主要区别和特点:

静态代理:

  1. 编译时生成

    • 静态代理是在编译时创建的代理类。代理类是预先定义好的,它和目标类实现了相同的接口或继承了相同的父类。
  2. 代理类定义

    • 静态代理的代理类需要手动编写或生成,通常在开发阶段就已经存在。
  3. 使用场景

    • 静态代理适用于已知目标类和代理类的情况,且目标类的接口或父类已经定义好。
  4. 灵活性

    • 静态代理相对不够灵活,因为代理类的行为在编译时已经确定,如果需要增加新的代理功能,通常需要修改代理类的代码。

以下是一个简单的静态代理的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方法时,代理类在调用目标类的方法前后分别执行了额外的操作。这使得代理类可以在不修改目标类代码的情况下增加新的功能或控制访问。

静态代理的优点是简单易懂,但缺点是每个代理类只能代理一个具体的目标类,因此如果有多个目标类需要代理,就需要创建多个代理类。此外,静态代理在编译时生成代理类,不够灵活。在实际应用中,通常会选择使用动态代理来解决这些问题。

动态代理:

  1. 运行时生成

    • 动态代理是在运行时生成的代理类。代理类在程序运行时动态创建,而不需要预先定义。
  2. 代理类定义

    • 动态代理的代理类由Java提供的Proxy类或第三方库生成,无需手动编写代理类。
  3. 使用场景

    • 动态代理适用于不知道目标类的具体类型或接口的情况,或者需要在运行时添加、修改代理功能的情况。
  4. 灵活性

    • 动态代理非常灵活,可以在运行时决定代理类的行为。通过动态代理,可以实现通用的代理逻辑,而不需要修改代理类的代码。

Java的动态代理实现:

Java提供了两种动态代理的实现方式:基于接口的代理和基于类的代理。基于接口的代理使用java.lang.reflect.Proxy类,它要求目标类实现一个或多个接口。基于类的代理使用字节码生成库,如CGLIB,可以代理没有实现接口的类。这两种动态代理方式都允许在运行时创建代理类,但使用不同的底层技术。

综上所述,静态代理和动态代理都有各自的优势和适用场景。静态代理适用于已知目标类和代理类的情况,而动态代理适用于不知道目标类的具体类型或需要在运行时灵活控制代理行为的情况。选择哪种代理方式取决于您的需求和项目的特点。

JDK代理

以下是一个使用JDK动态代理的简单示例。在这个示例中,我们将创建一个接口Subject,然后使用JDK动态代理来创建代理对象,代理对象将在目标方法调用前后执行额外的操作。

java
import 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代理

以下是一个使用CGLIB动态代理的简单示例。在这个示例中,我们将创建一个目标类RealSubject,然后使用CGLIB动态代理来创建代理对象,代理对象将在目标方法调用前后执行额外的操作。

首先,您需要添加CGLIB库到您的项目中。通常,您可以使用Maven或Gradle等构建工具来添加依赖项。以下是Maven的依赖项配置:

xml
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> <!-- 或者您选择的版本 --> </dependency>

接下来,让我们创建一个目标类和一个代理类:

java
import 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动态代理更快,但生成代理类的过程可能会更慢。这种方式非常适合于需要代理没有实现接口的类的场景。