Java API 之 动态代理
一、代理模式
如图:由于某些原因我们希望对该实现类进行访问控制、功能增强等,那么加入一层代理层,用代理层来调用实现类是一个很好的方式来解决这个问题,我们可以在调用实现类功能前后进行校验或者加入一些功能来达到控制、增强等效果。
你可以将代理模式简单理解为:不直接面向实现类,而是面向代理类,通过代理类调用实现类这一中间过程达到控制和增强的目的。
二、JDK动态代理
JDK提供了动态的代理实例创建的方法,你会用到:
在java.lang.reflect包下:
1)InvocationHandler代理处理程序接口
2)Proxy代理创建类
你需要使用步骤如下:
1)创建接口
2)创建接口实现类
3)实现代理处理程序InvocationHandler
4)Proxy创建代理
1、创建接口
/** * 接口 */ interface Person { void say(); }
2、创建接口实现类
/** * 实现类 */ class Lay implements Person { @Override public void say() { System.out.println("the weather is very nice"); } }
3、创建代理处理程序
/** * 代理处理实现 */ class InvokeHandlerImpl implements InvocationHandler { private Object subject; public InvokeHandlerImpl(Object subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置处理"); Object result = method.invoke(subject, args); System.out.println("后置处理"); return result; } }
注意:
1)InvokeHanlderImpl(Object subject); 构造方法传入了需要被代理的实现类实例对象;
2)proxy是代理实例对象;
3)method是被调用的方法;
4)args是method的参数;
4、创建代理
public class ProxyTest { public static void main(String[] args) { Person lay = new Lay(); Person person = (Person) Proxy.newProxyInstance(lay.getClass().getClassLoader(), lay.getClass().getInterfaces(), new InvokeHandlerImpl(lay)); person.say(); } }
注意:
1)Proxy.newProxyInstance(...)方法创建了lay实例对象的代理;
2)第一个参数是类加载器;
3)第二个参数是相应声明的接口;
4)第三个参数是之前的代理处理程序;
控制台打印如下:
前置处理
the weather is very nice
后置处理
上面这个示例展示了JDK动态代理如何使用的,那么我们可以回想一下Spring框架中AOP、拦截器、事务等是不是也是一样的处理逻辑呢?所以Spring其实利用代理来做了很多事情。
三、为什么JDK动态代理基于接口
在上面的示例中,我们可能会疑惑,为什么一定要写一个Person接口,为什么在创建代理的时候需要传入接口呢?
打开newProxyInstance(...)源码你会看到(省略了代码):
... Class<?> cl = getProxyClass0(loader, intfs); ... final Constructor<?> cons = cl.getConstructor(constructorParams); ... return cons.newInstance(new Object[]{h});
getProxyClass0方法通过加载器和接口获取了一个新类,这个类是接口的的实现类,而后通过构造方法创建了一个实例对象,这个实例对象就是代理对象。
我们可以这么理解代理对象和实现类之间的关系:
所以,代理对象和实现类的对象是两个对象,因此你可以知道如果你把代理对象强转成实现类是会报错的,因而你需要向上转型为接口。