Featured image of post Java 中的代理

Java 中的代理

分析 java 代理模式,jdk 动态代理以及 cglib 动态代理

最近在重新回顾以往的知识点,发现之前写的java动态代理部分并不是十分详细,有很多地方没有讲解地十分清楚,所以依靠这次机会,想着可以更加清晰地表达出这块的知识。

代理模式

要说到动态代理,我们不得不先去讲讲java设计模式中的代理模式。代理模式,顾名思义,主要起到代理的作用,即针对客户请求由代理来负责,代理不负责具体的业务逻辑,仅仅起到一个“中间商”的作用,其最终效果是让客户端觉得自己在访问服务端。在java设计模式中算是比较常见的一种设计模式,其基本的代码如下所示:

代码来源:javadoop.代理模式

 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
43
44
45
46
public interface FoodService {
    Food makeChicken();
    Food makeNoodle();
}

public class FoodServiceImpl implements FoodService {
    public Food makeChicken() {
          Food f = new Chicken()
        f.setChicken("1kg");
          f.setSpicy("1g");
          f.setSalt("3g");
        return f;
    }
    public Food makeNoodle() {
        Food f = new Noodle();
        f.setNoodle("500g");
        f.setSalt("5g");
        return f;
    }
}

// 代理要表现得“就像是”真实实现类,所以需要实现 FoodService
public class FoodServiceProxy implements FoodService {

    // 内部一定要有一个真实的实现类,当然也可以通过构造方法注入
    private FoodService foodService = new FoodServiceImpl();

    public Food makeChicken() {
        System.out.println("我们马上要开始制作鸡肉了");

        // 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的,
        // 代理只是在核心代码前后做些“无足轻重”的事情
        Food food = foodService.makeChicken();

        System.out.println("鸡肉制作完成啦,加点胡椒粉"); // 增强
          food.addCondiment("pepper");

        return food;
    }
    public Food makeNoodle() {
        System.out.println("准备制作拉面~");
        Food food = foodService.makeNoodle();
        System.out.println("制作完成啦")
        return food;
    }
}

从上面的代码中我们可以看到,代理类FoodServiceProxy仅仅做了一些边边角角的事情,具体的核心业务主要还是由FoodServiceImpl类来实现。

图片来源:javadoop.代理模式

proxy1.png 从上图中,我们可以发现,代理模式说白了就是方法的增强包装,客户端的调用最终还是要落实到具体的实现类中。这样的模式在Spring中有着很多的使用,与我们不同的是,Spring中不再需要我们手动的实现这样的代理模式,Spring中使用的是基于jdk动态代理和cglib动态代理来进行包装实现的。我们明白了这两种动态代理的原理,那么Sprig的面向切面编程也没有了很大的压力。

jdk动态代理

代码分析

老规矩,我们还是先看看代码,看看jdk动态代理到底是怎么实现的。

 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
public interface IHello {
    void say();
}

public class HelloImpl implements IHello {
    @Override
    public void say() {
        System.out.println("hello!");
    }
}

public class DynamicProxy implements InvocationHandler {

    private Object object;

    public Object bind(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("ready to say : ");
        return method.invoke(this.object, args);
    }
}

public class Main {
    public static void main(String[] args) {
        IHello hello = new HelloImpl();
        IHello iHello = (IHello) new DynamicProxy().bind(hello);
        iHello.say();
    }
}

可以看到在jdk动态代理中,我们先定义了一个接口IHello,之后通过创建一个类HelloImpl来实现了该接口,到此为止,我们应该还是可以看懂的,因为这和之前代理模式中我们使用的方式是一样的,后面创建一个DynamicProxy类,以及使用该类进行一系列的操作,可能比较模糊,那么我们现在着重分析分析这个类里面的两个方法bind()invoke()到底做了什么。

首先来看一下bind()方法

1
2
3
4
5
6
public Object bind(Object object) {
    this.object = object;
    return Proxy.newProxyInstance(object.getClass().getClassLoader(),
            object.getClass().getInterfaces(),
            this);
}

该方法中最后调用了Proxy.newProxyInstance()方法,我们进去看看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//jdk版本 10
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
    Objects.requireNonNull(h);

    final Class<?> caller = System.getSecurityManager() == null
                                ? null
                                : Reflection.getCallerClass();

    /*
        * 动态生成指定代理类和构造方法
        */
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

    return newProxyInstance(caller, cons, h);
}

我们接着查看getProxyConstructor()方法

 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
private static Constructor<?> getProxyConstructor(Class<?> caller,
                                                      ClassLoader loader,
                                                      Class<?>... interfaces)
{
    // 依据是否是单例
    if (interfaces.length == 1) {
        Class<?> intf = interfaces[0];
        if (caller != null) {
            checkProxyAccess(caller, loader, intf);
        }
        //java8写法 lambda表达式
        //先从缓存中获取,如果缓冲中不存在,则新build一个
        //我们这里肯定直接build一个
        return proxyCache.sub(intf).computeIfAbsent(
            loader,
            (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
        );
    } else {
        // interfaces cloned
        final Class<?>[] intfsArray = interfaces.clone();
        if (caller != null) {
            checkProxyAccess(caller, loader, intfsArray);
        }
        final List<Class<?>> intfs = Arrays.asList(intfsArray);
        return proxyCache.sub(intfs).computeIfAbsent(
            loader,
            (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
        );
    }
}

继续进入build()方法中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Constructor<?> build() {
    //关键点在这里,这里定义一个代理类
    Class<?> proxyClass = defineProxyClass(module, interfaces);
    final Constructor<?> cons;
    try {
        cons = proxyClass.getConstructor(constructorParams);
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            cons.setAccessible(true);
            return null;
        }
    });
    return cons;
}

再进入defineProxyClass()方法

 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
    String proxyPkg = null;     // package to define proxy class in
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

    /*
        * Record the package of a non-public proxy interface so that the
        * proxy class will be defined in the same package.  Verify that
        * all non-public proxy interfaces are in the same package.
        */
    for (Class<?> intf : interfaces) {
        int flags = intf.getModifiers();
        if (!Modifier.isPublic(flags)) {
            accessFlags = Modifier.FINAL;  // non-public, final
            String pkg = intf.getPackageName();
            if (proxyPkg == null) {
                proxyPkg = pkg;
            } else if (!pkg.equals(proxyPkg)) {
                throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
            }
        }
    }

    if (proxyPkg == null) {
        // all proxy interfaces are public
        proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
                                : PROXY_PACKAGE_PREFIX;
    } else if (proxyPkg.isEmpty() && m.isNamed()) {
        throw new IllegalArgumentException(
                "Unnamed package cannot be added to " + m);
    }

    if (m.isNamed()) {
        if (!m.getDescriptor().packages().contains(proxyPkg)) {
            throw new InternalError(proxyPkg + " not exist in " + m.getName());
        }
    }

    /*
        * Choose a name for the proxy class to generate.
        * 生成的动态代理类名字
        */
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg.isEmpty()
                            ? proxyClassNamePrefix + num
                            : proxyPkg + "." + proxyClassNamePrefix + num;

    ClassLoader loader = getLoader(m);
    trace(proxyName, m, loader, interfaces);

    /*
        * Generate the specified proxy class.
        * 生成动态代理类
        */
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
    try {
        Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
                                            0, proxyClassFile.length,
                                            loader, null);
        reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
        return pc;
    } catch (ClassFormatError e) {
        /*
            * A ClassFormatError here means that (barring bugs in the
            * proxy class generation code) there was some other
            * invalid aspect of the arguments supplied to the proxy
            * class creation (such as virtual machine limitations
            * exceeded).
            */
        throw new IllegalArgumentException(e.toString());
    }
}

defineProxyClass()方法中我们可以看到,在生成代理类之前,需要有很多的准备工作,包括生成的类的作用域,名称,修饰属性,类加载器等等。

好了,一切准备工作都已经完成,接下来我们就要看看generateProxyClass()方法是怎样动态生成代理类的

 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
static byte[] generateProxyClass(final String name,
                                     Class<?>[] interfaces,
                                     int accessFlags)
{
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    //真正解析生成二进制字节码的地方
    final byte[] classFile = gen.generateClassFile();
    //通过设置saveGeneratedFiles值,可以将字节码保存到文件
    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged(
        new java.security.PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int i = name.lastIndexOf('.');
                    Path path;
                    if (i > 0) {
                        Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                        Files.createDirectories(dir);
                        path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                    } else {
                        path = Paths.get(name + ".class");
                    }
		    //将二进制字节码写入文件中
                    Files.write(path, classFile);
                    return null;
                } catch (IOException e) {
                    throw new InternalError(
                        "I/O exception saving generated file: " + e);
                }
            }
        });
    }

    return classFile;
}

到此,我们知道了bind()方法起到的作用是动态地生成一个代理类,返回生成的代理类。但是我们还不清楚invoke()方法和bind()方法的关系,所以目前我们还有以下几点疑问:

  • 为什么生成的代理类可以去调用say()方法
  • 为什么调用say()方法会触发invoke()方法?

带着上面的疑问,我们继续往下看,我们知道,在生成动态类的方法里面有一个字段saveGeneratedFiles,它是用来决定是否生成代理类文件的,那么我们想办法将它设置为true,是不是就可以直接生成代理类的文件?

我们在之前的main()方法中添加下面一行代码,然后执行

1
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

运行之后便可以看到该文件已经存在于项目的com.sun.proxy包下,反编译之后便可以打开

 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.example.demo.web.IHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IHello {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.demo.web.IHello").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

从反编译的代码中我们可以看到,该动态代理类实现了IHello接口,同时继承了Proxy类,并且在其构造方法中传递了InvocationHandler参数到该动态类中。因为实现IHello接口的关系,该动态类中实现了say()方法,而在方法内部,调用了invoke()方法,至此我们明白了该动态代理类的原理。

总结

首先,我们自己定义的DynamicProxy类中的bind()方法主要生成了一个动态的代理类,该动态代理类继承了Proxy类,同时实现了IHello接口,其中Proxy类主要用来接收bind()方法中传递过来的InvocationHandler实例,IHello接口的实现让动态代理类中拥有了可以被调用的方法say(),这样在main()方法中动态代理类通过调用say()方法,达到调用invoke()方法,实现代理的功能。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus