静态代理、JDK动态代理、Cglib动态代理

本文将介绍代理模式,进而介绍代理模式的两种实现方式静态代理和动态代理的概念。接着用代码模拟静态代理,以及动态代理。动态代理有 JDK 实现的动态代理,CGlib 动态代理两种。Spring AOP 即是通过动态代理来实现的,所以在本文最后会介绍 Spring AOP 的源码。

代理、静态代理、动态代理

代理模式(Proxy Pattern)

  • why:
    • 根据开闭原则,我们不应该直接修改一个类,然而我们想要为其增减一些功能
    • 类定义在 jar 中,甚至无法修改
  • what:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
  • how:根据代理类创建时期不同,可以将其分为静态代理、动态代理。
  • how good:通过代理,可以在目标对象的基础上,减少或者增加功能。

静态代理

前面介绍到实现代理模式有两种方法,静态代理和动态代理,两者的主要区别在于创建代理类的时间不同。静态代理类的 .class 文件在程序运行前就已经存在了;动态代理类的 .class 是在程序运行时创建的。

  • why:静态代理比动态代理更容易理解和实现,简单的代理可以使用静态代理完成
  • what:代理类在编译器就确定
  • how:静态代理即程序员实现编写好代理类,实现对目标对象的代理。目标对象与代理对象同时实现目标对象的接口,并在代理对象中用构造函数注入目标对象
  • how good:
    • 通过代理对象来增加或减少目标对象的功能,控制目标对象原有的访问权限
    • 用小对象代理大对象,减少内存占用,提高系统运行效率

动态代理

用静态代理的方式来实现代理模式存在一些弊端:代理新的类就需要修改代理类,违背开闭原则;程序员需要写很多重复的代码。

动态代理中的代理类并不要求在编译期就确定,而是可以在运行期动态生成,从而实现对目标对象的代理功能。

  • why:动态代理可以解决静态代理的弊端,不侵入性地修改代码,用更高效的方式实现代理模式
  • what:代理类在运行期动态生成
  • how:
    • JDK动态代理:java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口
    • Cglib动态代理:Cglib是第三方代码生成类库,运行时在内存中动态生成一个子类对象
  • how good:AOP、过滤器、拦截器都使用动态代理在运行期生成代理类

静态代理实现

静态代理的实现包括目标对象接口、目标对象、代理对象三部分,其中目标对象和代理对象同时实现目标对象接口,并且在代理对象中用注入目标对象,实现对目标对象的增强。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 目标对象接口
public interface HelloSerivice {
    public void say();
}

// 目标对象
public class HelloSeriviceImpl implements HelloSerivice{
    @Override
    public void say() {
        System.out.println("hello world");
    }
}

// 代理对象
public class HelloSeriviceProxy implements HelloSerivice{
    private HelloSerivice target;
    public HelloSeriviceProxy(HelloSerivice target) {
        this.target = target;
    }
    @Override
    public void say() {
      	// 对目标对象的扩展
        System.out.println("记录日志");
        target.say();
				// 对目标对象的扩展
        System.out.println("清理数据");
    }
}

public class Main {
    @Test
    public void testProxy(){
        //目标对象
        HelloSerivice target = new HelloSeriviceImpl();
        //代理对象
        HelloSeriviceProxy proxy = new HelloSeriviceProxy(target);
        proxy.say();
    }
}
// 记录日志
// hello world
// 清理数据

JDK 动态代理

JDK动态代理:java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口

JDK 动态代理

和静态代理一样,我们任然创建了一个接口,以及其具体实现,如下所示:

1
2
3
4
5
6
7
8
9
10
11
public interface Target {
    public String execute();
}

public class TargetImpl implements Target {

    public String execute() {
        System.out.println("TargetImpl execute!");
        return "execute";
    }
}

与静态代理不同的是我们的代理类没有实现目标对象的接口,而是采用实现 InvocationHandler 接口,关键在于实现了 invoke() 接口,这个方法在后面说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class DynamicProxyHandler implements InvocationHandler {

    private Target target;

    public DynamicProxyHandler(Target target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("========before==========");
        Object result = method.invoke(target,args);
        System.out.println("========after===========");
        return result;
    }
}

我们获取代理的方式则是采用 java.lang.reflect.Proxy 包下 Proxy 类的 newProxyInstance() 方法:

1
2
3
4
5
6
7
8
9
public class Test {
    public static void main(String[] args) {
        Target target = new TargetImpl();
        DynamicProxyHandler handler = new DynamicProxyHandler(target);
        Target proxySubject = (Target) Proxy.newProxyInstance(TargetImpl.class.getClassLoader(),TargetImpl.class.getInterfaces(),handler);
        String result = proxySubject.execute();
        System.out.println(result);
    }
}

这里的关键问题就在于 invoke()Proxy.newProxyInstance()以及proxySubject.execute()

InvocationHandler 接口

java.lang.reflect 包中的 InvocationHandler 接口

1
2
3
4
5
6
7
8
9
10
11
public interface InvocationHandler {

    /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

我们对于被代理的类的操作都会由该接口中的 invoke 方法实现,其中的参数的含义分别是:

  1. proxy:目标类的实例;
  2. method:调用目标类的方法;
  3. args:该方法需要的参数。

newProxyInstance 方法

java.lang.reflect 包中的 Proxy 类中的 newProxyInstance 方法

1
2
3
4
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
  1. loader:被代理的类的类加载器;
  2. 2、interfaces:被代理类的接口数组;
  3. 3、invocationHandler:调用处理器类的对象实例。

该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。

Cglib 动态代理

Cglib动态代理:Cglib是第三方代码生成类库,运行时在内存中动态生成一个子类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Target {
    public String execute() {
        String message = "-----------test------------";
        System.out.println(message);
        return message;
    }
}

public class MyMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(">>>>MethodInterceptor start...");
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println(">>>>MethodInterceptor ending...");
        return "result";
    }
}

public class CglibTest {
    public static void  main(String ... args) {
        System.out.println("***************");
        CglibTest test = new CglibTest();
        Target proxyTarget = (Target) test.createProxy(Target.class);
        String res = proxyTarget.execute();
        System.out.println(res);
    }

    public Object createProxy(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new MyMethodInterceptor());
        return enhancer.create();
    }
}

代理对象的生成过程由Enhancer类实现,大概步骤如下:

  1. 生成代理类 Class 的二进制字节码;
  2. 通过 Class.forName 加载二进制字节码,生成 Class 对象;
  3. 通过反射机制获取实例构造,并初始化代理类对象。

JAVA中的静态代理、动态代理以及CGLIB动态代理

所有和Java中代理有关的知识点都在这了。

发布了191 篇原创文章 · 获赞 17 · 访问量 6万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览