JDK动态代理

JDK动态代理是Spring动态代理的默认实现方法。如果我们的类实现了一个接口,那么Spring就会使用这种方法,因为使用JDK动态代理的一个缺陷就是代理的类必须实现接口。

JDK实现动态代理需要两个组件,首先第一个就是InvocationHandler接口。我们在使用JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写invoke方法,这个方法其实就是我们提供的代理方法。

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
/**
* 这里是一个动态的代理类,只需要传入对应的类,就会执行对应类的对应方法
* 也就是说我们只用实现这一个代理类就行了,我们所有相同的代理实现都可以用该类
*/
public class MyInvocationHandler implements InvocationHandler {

// 目标对象
private Object target;

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

/**
* 这里的Method,我们通过new Proxy创建的一个代理对象,调用它的哪个方法,这里传过来的就是哪个
*/
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
// 执行相应的目标方法,rs是方法的返回值
// 这里是执行被代理对象的方法。由于被代理对象是传进来的,所以一个方法就可以了。
Object rs = method.invoke(target, args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}

然后JDK动态代理需要使用的第二个组件就是Proxy这个类,我们可以通过这个类的newProxyInstance方法,返回一个代理对象。生成的代理类实现了原来那个类的所有接口,并对接口的方法进行了代理,我们通过代理对象调用这些方法时,底层将通过反射,调用我们实现的invoke方法。

1
2
3
4
5
IHello iHello2 = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), // 加载接口的类加载器
new Class[]{IHello.class}, // 一组接口
new MyInvocationHandler(new HelloImpl())); // 自定义的InvocationHandle
// 这里执行哪个方法,传给MyInvocationHandler的Method就是哪个方法
iHello2.sayHello();

JDK动态代理的要求:

  • 委托类和代理类实现的公共接口
  • 实现公共接口的具体委托类
  • InvocationHandler接口被Proxy类回调处理,一般实现 InvocationHandler 接口的类具有委托类引用,接口方法 invoke 中添加公共代码并调用委托类的接口方法
  • JDK提供生成动态代理类的核心类Proxy

JDK的动态代理,是创建了一个匿名类来继承Proxy类,然后实现了需要被代理的接口。因为Java只能单继承,所以代理类不能通过继承来实现。

CGLib动态代理

若需要代理的类没有实现接口,此时JDK的动态代理将没有办法使用,于是Spring会使用CGLib的动态代理来生成代理对象。

CGLib是基于继承的,如果对应的类无法被继承,或者对应方法无法被重写,那么CGLib也无法生成代理对象。