简介

我所理解的动态代理,最后的运行方式其实是一个静态代理加上一些反射机制,请注意,我说的是最后的运行方式

静态代理

抛开动态代理先不说,先看一下什么是静态代理,明白了静态代理,那么动态代理最后是如何运行的读完本篇文章基本上也就明白了

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 interface Service {
public void doSomething(String context);
}

public class ServiceImpl implements Service {
@Override
public void doSomething(String context) {
System.out.println("do " + context);
}
}

public class StaticProxy {

private Service service;

public StaticProxy(Service service) {
this.service = service;
}

public void doSomething(String content) {
System.out.println("do pre");
service.doSomething(content);
System.out.println("do post");
}
}


public class StaticMain {
public static void main(String[] args) {
StaticProxy proxy = new StaticProxy(new ServiceImpl());
proxy.doSomething("coding");
}
}

这就是一个简单的静态代理的例子,把ServiceImpl的执行交给了StaticProxy来执行,在执行的前后可以添加do pre和do post的业务逻辑。
StaticProxy会在编译期就被编译成一个class文件被加载(这与动态代理有很大的区别)

动态代理

再来看动态代理,先不说动态代理的实现过程,上面说到是否编译成class文件是静态代理与动态代理的一个很大的区别。那这就表明,动态代理是没有class文件的,那它是怎么实现的呢?其实,我个人理解动态代理之所以叫做动态的,就是这个生成的过程是不定的。在运行时生成一份代理类的字节码并加载到jvm中。

先来看一下最后生成的字节码类,就以上面的类型为例

1
2
3
4
5
6
7
8
9
10
11
12
public final class $Proxy1 extends Proxy implements Service {
private InvocationHandler h; // 这是java提供的一个接口
private $Proxy1() {}
private $Proxy1(InvocationHandler h) {
this.h = h;
}

public void doSomething(String content) {
Method method = Service.class.getMethod("doSomething", new Class[]{String.class});
return h.invoke(this, method, new Object[]{new String(content)});
}
}

看一下$Proxy1这个类与上面的StaticProxy的结构是不是类似,

  • 都实现了Service
  • 都是将接口的实现注入到构造函数中,为接口提供具体的实现类
  • 最后都调用doSomething来调用具体的业务,只不过在动态代理里面通过反射来调用

看完$Proxy1,个人感觉就是一个静态代理的一个变种,把这个结果看明白了,那后面其实都是一些模板代码,目的就是为了生成这个类

有人会问$Proxy1,为什么会叫这个名字,我只能说我不知道,jvm就是这么做的,以\$开头,以数字标号来结尾。我现在不关心,我很开心我揭开动态代理的一层神秘的面纱,至少,不再像以前那样让人感觉高不可攀了(其实,还有很多层没揭开)

那下面就来看一下那些模板代码把

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
public interface Service {
public void doSomething(String context);
}

public class ServiceImpl implements Service {
@Override
public void doSomething(String context) {
System.out.println("do " + context);
}
}

public class ServiceInvocationHandler implements InvocationHandler {

private Object object;

public ServiceInvocationHandler(Object object) {
this.object = object;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do pre");
method.invoke(object, args);
System.out.println("do post");
return null;
}
}

public class DynamicMain {

public static void main(String[] args) {

Service service = new ServiceImpl();
ServiceInvocationHandler handler = new ServiceInvocationHandler(service);

Service proxyService = (Service) Proxy.newProxyInstance(handler.getClass().getClassLoader(), service.getClass().getInterfaces(), handler);
proxyService.doSomething("coding");
}
}

Service与ServiceImpl与静态代理没什么区别,将静态代理的代理类换成了InvocationHandler处理器的实现类。再有就是调用方式变了一下。

这里从main函数开始看,先指定Service的实现类,并构造Handler实现类。下面是重点,就是下面这行代码,用来生成$Proxy1这个字节码类的

Proxy的newProxyInstance方法,首先传递一个classloader,用这个classloader来加载生成的字节码类到jvm中;第二个参数是ServiceImpl实现的接口(即Service),使生成的字节码类也实现这个接口;第三个参数就是这个自定义的Handler了,将自定义的ServiceInvocationHandler传递给了$Proxy1中的InvocationHandler接口。

proxyService.doSomething,其实就是调用的$Proxy1的doSomething方法,然后再调用invoke方法。

怎么样,现在是不是已经对动态代理的理解又加深了一步呢!!!🙂