动态代理

仅供学习交流,如有错误请指出,如要转载请加上出处,谢谢

代理模式

在代理模式中,角色分配如下:

  • Subject(1-k) : 委托对象所实现的所有接口(i>=1)
  • RealSubject : 委托对象,也就是被代理的对象
  • ProxySubject : 代理对象

在JDK实现的代理模式是面向接口的,不管是委托类还是代理类都应该实现相同的接口,这样才会保持行为的一致性,因为对于Client来说,它只要输出它所想要的结果,不会管你是谁实现的,所以为了减轻委托对象的压力,就必须要克隆出其他跟自己一样的帮手来帮助自己,就好像火车票代售点一样,代理对象就由此而生了。

静态代理

静态代理是指在JVM执行前就把你的代理类给定义好了,例子如下:

Subject

1
2
3
public interface Subject {
public void doSomething();
}

RealSubject

1
2
3
4
5
6
7
public class RealSubject implements Subject{
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("i am just do something!!!!!");
}
}

ProxySubject

1
2
3
4
5
6
7
8
9
10
public class ProxySubject implements Subject{
Subject realSubject = null ;
@Override
public void doSomething() {
//类似于Spring的@Before do something
realSubject = new RealSubject();
realSubject.doSomething();
//类似于Spring的@After do something
}
}

由上可知,其实调用代理类(ProxySubject)和调用委托类(RealSubject)是一样的效果,等于是代理类对委托类又重新封装了一层,但是为什么要这样做呢?

  1. 如果每次Client调用委托类来完成业务操作,那么每次委托类就得创建对象来完成业务,如果是大业务场景,消耗的内存是巨大的,这个时候,如果是代理类来实现,可以在代理类内对委托类实现缓存操作,这样就会减少很大的内存消耗。

  2. 如果你想对原生的委托类进行扩展(比如Spring中的@After和@Before的思想),你可以对委托类进行修改,但是这会影响原有已经实现的程序逻辑,如果使用代理类,在不影响原生的委托类情况下再进行逻辑的扩展,使程序变得更加健壮。

由上可知,代理类的作用使原生的委托类更加的灵活被运用,能够应付不同的不同应用场景,但是,如果委托类的一些应用方法的删减,实现接口的减少,对于代理类来说也要进行很大的修改,这样的改动有时使代理模式变得更加的复杂,变得不是那么的灵活,如果有一种代理类能够需要调用的时候才加载,不管接口的变动,代理类都会自动的更新,无需改变,那么这就是动态代理。

动态代理

动态代理是在静态代理的基础上对代理类进行了优化,利用java反射的原理来创建代理类,使代理类在JVM运行时创建。

如上图所示,这个代理类会根据现有的接口数或者实现的方法动态的去创建对象,对于业务频繁更替的场景下,不需要对代理类进行频繁的更改,更加的灵活,而在java中,对于动态代理的实现,有两种方法,一种是JDK形式的实现,一种是第三方包cglib的实现形式。

JDK动态代理

在JDK实现中,主要是一个类,一个接口:动态代理类Proxy和调用处理器InvocationHandler

1.Proxy

Proxy是动态创建代理类的类,它主要是利用接口的信息在类加载器中动态的生成代理类,而其中主要的生成代理类的方法就是newProxyInstance方法,实现如下:

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
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
// 检查调用处理器是否为空,为空抛异常
if (h == null) {
throw new NullPointerException();
}
// 获得与制定类装载器和一组接口相关的代理类类型对象
Class cl = getProxyClass(loader, interfaces);
// 通过反射获取构造函数对象并生成代理类实例
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}

由上就很容易发现,这是个简单的java反射创建对象,获取Class对象,再获取构造器,最后反射成对象,这是个静态方法,所以直接类本身就可以调用了。

Proxy还有很多的实现,比如利用一个HashMap来实现对代理类的缓存,key就是接口列表,value就是代理类的对象,还有关联调用处理器的方法等等。

2.InvocationHandler

InvocationHandler是调用处理器,是负责方法调用时,利用java反射机制在JVM运行时调用该方法,其中主要的方法就是invoke方法,有三个参数proxy,method,args,这是需要自己去实现的,以下是invoke方法的实现

1
2
3
4
5
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法反射
Object obj = method.invoke(proxyObj, args);
return obj;
}

3.代码实现

MyInvocationHandler

1
2
3
4
5
6
7
8
9
10
11
public class MyInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
////类似于Spring的@Before do something
//方法反射
Object obj = method.invoke(proxyObj, args);
//类似于Spring的@After do something
return obj;
}
}

JdkProxyFactory

1
2
3
4
5
6
7
8
9
10
11
public class JdkProxyFactory {
private Object proxyObj = null;//代理类
// 创建代理
public Object createProxy(Object targetObject,InvocationHandler myInvocationHandler) {
if (null != targetObject) {
this.proxyObj = targetObject;
}
return Proxy.newProxyInstance(this.proxyObj.getClass().getClassLoader(),
this.proxyObj.getClass().getInterfaces(), myInvocationHandler);
}
}

以上就是一个简单的JDK实现的代理类,直接用以下的代码调用就可以了

1
2
3
Subject proxySubject = (Subject)new JdkProxyFactory().createProxy(new RealSubject(),new MyInvocationHandler());
proxySubject.doSomething();

当我们调用doSomething方法时,你以为是调用RealSubject对象的doSomething方法,其实是调用代理类$ProxyN(生成代理类的名称,N从1开始)的doSomething方法,他会触发该方法的调用处理器invoke方法来调用doSomething方法。

cglib动态代理

CGLib是面向类的,而cglib动态代理的实现也是一个类和一个接口:Enhancer和MethodInterceptor,和JDK的Proxy和InvocationHandler职能是一样的,一个是负责创建类,一个是负责调用方法的处理

1.Enhancer

Enhancer可以说是CGLib的一个字节码增强器,它的作用通过委托类的子类来实现代理类的创建,创建过程如下:

  • 通过委托类创建它的子类,在子类中的每个方法设置回调方法,然后获取它的Class对象
  • 利用Class对象根据GeneratorStrategy.generate方法生成代理类的字节码
  • 通过反编译生成代理类的Class对象
  • 再通过反射机制创建代理类的对象

2.MethodInterceptor

MethodInterceptor是一个方法拦截器,它的作用就是在代理类每执行一个方法时执行拦截方法并返回,其中最主要的方法intercept实现如下

1
2
3
4
5
6
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//相当于调用了代理类本身的方法
Object result = proxy.invokeSuper(obj, args);
return result;
}

CGLib实现了Fastclass机制,对代理类的方法建立了索引,把方法存储在索引中,通过方法名和它的信息就可以获取该方法,不需要进行反射来进行方法的调用。

3.代码实现

MyMethodInterceptor

1
2
3
4
5
6
7
8
9
10
11
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
////类似于Spring的@Before do something
//方法反射
Object object = proxy.invokeSuper(obj, arg);
//类似于Spring的@After do something
return object;
}
}

CglibProxyFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CglibProxyFactory {
private Object proxyObj = null;//代理类
// 创建代理
public Object createProxy(Object targetObject,MethodInterceptor MyMethodInterceptor) {
if (null != targetObject) {
this.proxyObj = targetObject;
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.proxyObj.class);
enhancer.setCallback(MyMethodInterceptor);
return enhancer.create();
}
}

这是一段Cglib代理类的实现,调用代码如下:

1
2
Subject proxySubject = (Subject)new CglibProxyFactory().createProxy(new RealSubject(),new MethodInterceptor());
proxySubject.doSomething();

相比于JDK代理实现,Cglib的实现更快,它的不同之处在于

  • Cglib代理摒除了JDK代理利用反射来调用方法(反射的效率是很低的),利用索引来实现。
  • Cglib代理是面向extends的,意味着一些不能继承的类无法用Cglib来实现,而JDK代理是面向implements,这方面更规范。