动态代理和静态代理的区别

代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问,将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类的方法。

区别:

  • 静态代理:由程序员创建或者是由特定工具创建,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;
  • 动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类

实现动态代理的方式?

Java动态代理是一种在运行时动态生成代理类的机制,用于代理其他对象的访问。与静态代理不同,动态代理无需事先定义代理类,而是在程序运行时根据指定的接口和处理器生成代理类

Java动态代理主要分为两种类型:

  • 基于接口的代理(JDK动态代理): 这种类型的代理要求目标对象必须实现至少一个接口。Java动态代理会创建一个实现了相同接口的代理类,然后在运行时动态生成该类的实例。这种代理的实现核心是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。每一个动态代理类都必须实现InvocationHandler接口,并且每个代理类的实例都关联到一个handler。当通过代理对象调用一个方法时,这个方法的调用会被转发为由InvocationHandler接口的invoke()方法来进行调用。

  • 基于类的代理(CGLIB动态代理): CGLIB(Code Generation Library)是一个强大的高性能的代码生成库,它可以在运行时动态生成一个目标类的子类。CGLIB代理不需要目标类实现接口,而是通过继承的方式创建代理类。因此,如果目标对象没有实现任何接口,可以使用CGLIB来创建动态代理。

使用动态代理可以避免为每个目标类编写具体的代理类,提高代码的灵活性和可维护性。它可以在运行时根据需要动态地生成代理类,适用于代理多个目标类的情况。

使用JDK动态代理的代码:

Java 中提供了一个 java.lang.reflect.Proxy 类,用于创建动态代理对象。该类提供了一个静态方法 newProxyInstance(),通过指定目标类的加载器、目标类实现的接口、以及一个 InvocationHandler 来创建代理对象。

1、创建接口

1
2
3
public interface HelloService { 
void sayHello(String name);
}

2、创建实现类

1
2
3
4
5
6
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}

3、创建一个类实现InvocationHandler接口,重写invoke方法,在里面调用目标类的方法,并在调用前后实现自定义的逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class HelloInvocationHandler implements InvocationHandler {
private Object target; // 被代理的目标对象

public HelloInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invocation"); // 在方法调用前添加的逻辑
Object result = method.invoke(target, args); // 调用目标对象的实际方法
System.out.println("After method invocation"); // 在方法调用后添加的逻辑
return result;
}
}

4、使用Proxy.newProxyInstance()方法,传入响应参数,获得目标类的代理对象,并调用目标类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.reflect.Proxy;

public class Main {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
// 创建InvocationHandler实例,传入要代理的目标对象
HelloInvocationHandler handler = new HelloInvocationHandler(helloService);
// 通过Proxy类的静态方法创建代理对象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
helloService.getClass().getClassLoader(),
helloService.getClass().getInterfaces(),
handler);
proxy.sayHello("World");
}
}

CGlib是怎么实现的?

CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是 final 的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

CGLIB 底层:采用ASM字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。

CGLib(Code Generation Library)是一个强大的字节码生成库,它可以在运行时动态地生成字节码,用于创建代理对象。与 JDK 动态代理不同,CGLib 不要求目标对象必须实现接口,它是通过继承目标类来实现代理功能的。

原理核心步骤:

  • 字节码生成

    • CGLib 通过使用Enhancer类来创建代理对象。Enhancer是 CGLib 的核心类,它可以动态地生成一个目标类的子类。例如,有一个普通的 Java 类TargetClass,CGLib 会在运行时创建一个TargetClass的子类,这个子类就是代理类。
    • 在字节码生成过程中,CGLib 会使用ASM(一个 Java 字节码操作框架)来操作字节码。ASM可以直接生成字节码或者对已有的字节码进行修改。CGLib 利用ASM的功能,根据目标类的结构来生成代理类的字节码。
  • 方法拦截机制

    • CGLib 通过实现MethodInterceptor接口来实现方法拦截。在代理类中,每个被重写的方法都会调用MethodInterceptor接口的intercept方法。例如,当调用代理对象的一个方法时,实际上是调用了代理类中重写后的方法,而这个重写后的方法会在内部调用intercept方法。
    • intercept方法中,可以在调用目标类的原始方法之前和之后添加自定义的逻辑。这个intercept方法有四个参数:Object类型的obj(代表代理对象本身)、Method类型的method(代表被调用的方法)、Object[]类型的args(代表方法的参数)和MethodProxy类型的methodProxy(代表代理方法)。可以通过methodProxy.invokeSuper来调用目标类的原始方法,
  • 假设我们有一个简单的类SampleClass,没有实现任何接口,如下:

    1
    2
    3
    4
    5
    public class SampleClass {
    public void sampleMethod() {
    System.out.println("Original method in SampleClass");
    }
    }
  • 我们可以使用 CGLib 来创建代理对象,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CglibProxyExample implements MethodInterceptor {
public Object getProxy(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Before method call");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("After method call");
return result;
}
}
  • main方法中使用代理对象:
1
2
3
4
5
public static void main(String[] args) {
CglibProxyExample proxyExample = new CglibProxyExample();
SampleClass sampleClassProxy = (SampleClass) proxyExample.getProxy(SampleClass.class);
sampleClassProxy.sampleMethod();
}
  • 在这个示例中,CglibProxyExample类实现了MethodInterceptor接口,通过Enhancer类生成SampleClass的代理对象。当调用代理对象的sampleMethod时,会执行intercept方法中的逻辑,在调用原始方法前后添加了自定义的打印语句。

实现静态代理和动态代理的代码

一、静态代理

静态代理是由程序员创建或工具生成代理类的代码,再对其编译。在程序运行前,代理类的.class 文件就已经存在了。
实现步骤如下:

  1. 定义一个业务接口,包含要代理的方法。
1
2
3
public interface Subject {
void doSomething();
}
  1. 实现业务接口的真实业务类。
1
2
3
4
5
6
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
  1. 创建一个代理类,同样实现业务接口,在代理类中持有一个真实业务对象的引用,并在代理方法中调用真实业务对象的方法,同时可以添加一些额外的逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StaticProxy implements Subject {
private RealSubject realSubject;

public StaticProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}

@Override
public void doSomething() {
System.out.println("Before calling real subject.");
realSubject.doSomething();
System.out.println("After calling real subject.");
}
}
  1. 使用代理类。
1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject proxy = new StaticProxy(realSubject);
proxy.doSomething();
}
}

二、动态代理

动态代理是在程序运行时,通过反射机制动态地生成代理类的对象。Java 中主要有两种方式实现动态代理:基于 JDK 动态代理和基于 CGLIB 动态代理。

  1. 基于 JDK 动态代理
  • JDK 动态代理要求被代理的对象必须实现一个接口。
  • 实现步骤如下:
    • 定义一个业务接口,和静态代理中的接口一样。
    • 实现业务接口的真实业务类。
    • 创建一个动态代理类,实现InvocationHandler接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkDynamicProxy implements InvocationHandler {
private Object target;

public JdkDynamicProxy(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before calling real subject.");
Object result = method.invoke(target, args);
System.out.println("After calling real subject.");
return result;
}

public static Object createProxy(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new JdkDynamicProxy(target));
}
}
  • 使用代理类。
1
2
3
4
5
6
7
public class Main {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject proxy = (Subject) JdkDynamicProxy.createProxy(realSubject);
proxy.doSomething();
}
}
  1. 基于 CGLIB 动态代理
  • CGLIB 可以代理没有实现接口的类。
  • 实现步骤如下:
    • 引入 CGLIB 的依赖。
    • 实现业务接口的真实业务类,或者不实现接口直接创建一个普通类。
    • 创建一个方法拦截器类,实现MethodInterceptor接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibDynamicProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before calling real subject.");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After calling real subject.");
return result;
}

public static Object createProxy(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new CglibDynamicProxy());
return enhancer.create();
}
}
  • 使用代理类。
1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
// 如果 RealSubject 没有实现接口,可以直接使用 CGLIB 代理
RealSubject proxy = (RealSubject) CglibDynamicProxy.createProxy(RealSubject.class);
proxy.doSomething();
}
}