一篇杂文,写到哪就说到哪。在一些框架中有所谓的截面(aop),这些应用主要就是使用了JAVA的动态代理机制。截面或者说是拦截器所做的事情就是把一些共同的功能代码进行了抽取,简化开发以及维护。比如说,某些业务需要记录日志,需要进行功能验证。
先把用到的代码做一些简单的说明:
Ø ImasterBusiness:主业务接口
Ø MasterBusiness:主业务接口的一个实现类
Ø MasterBusinessIntercepter:你叫拦截器也好,叫截面也好的一个类
Ø MasterBusinessHander:处理器
Ø MasterBusinessProxy:静态代理类
1. 静态代理
代理模式主要就是为了隐藏真实的业务主体,来达到一种封装的效果。静态代理模式是最简单的一种,说白了就是为每一个真实的业务主体都手动创建一个代理类。先看下面的代码
l 业务接口:
public interface IMasterBusiness {
public void moreMoney();
}
l 真实业务主体
public class MasterBusiness implements IMasterBusiness {
/* (non-Javadoc)
* @see test.IMasterBusiness#moreMoney()
*/
@Override
public void moreMoney() {
System.out.println("股票飘红!");
}
}
l 代理类
public class MasterBusinessProxy implements IMasterBusiness{
private IMasterBusiness imb;
public MasterBusinessProxy(IMasterBusiness imb){
this.imb = imb;
}
@Override
public void moreMoney() {
buy();
imb.moreMoney();
sale();
}
//buy,sale就是一些共同的功能代码,例如记录日志,功能验证等
private void buy(){
System.out.println("抄底");
}
private void sale(){
System.out.println("高抛");
}
}
l 使用代理:
public static void main(String[] args) {
IMasterBusiness imb = new MasterBusiness();
IMasterBusiness imbp = new MasterBusinessProxy(imb);
imbp.moreMoney();
}
l 运行结果:
抄底
股票飘红!
高抛
静态代理有一个问题就是需要我们手动创建代理类,如果业务方法不止一个,那么就要在每个业务方法中都加入buy()以及sale()方法。如果业务类不止一个,我们就要分别创建代理类。代码量增大,也为之后的维护增加了难度。解决的办法就是动态代理模式
动态代理模式
动态代理需要引用java中的Proxy类以及InvocationHandler接口。先把实现代码贴上
l 处理器
public class MasterBusinessHander implements InvocationHandler {
//被代理的对象
private Object masterBusinee;
//拦截器
private MasterBusinessIntercepter intercepter = new MasterBusinessIntercepter();
public MasterBusinessHander(Object masterBusinee){
this.masterBusinee = masterBusinee;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
* proxy:代理类
* method:要调用的业务方法
* args:也无方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result;
if(method.getName().equals("moreMoney")){
intercepter.buy();
result = method.invoke(masterBusinee, args);
intercepter.sale();
}else{
result = method.invoke(masterBusinee, args);
}
return result;
}
}
l 调用:
public static void main(String[] args) throws Exception {
//创建一个调用器
MasterBusinessHander hander = new MasterBusinessHander(new MasterBusiness());
//拆分生成代理的步骤,方便后面的介绍
Class<?> mbClass = Proxy.getProxyClass(MasterBusiness.class.getClassLoader(), MasterBusiness.class.getInterfaces());
Constructor<?> constructor = mbClass.getConstructor(new Class[]{InvocationHandler.class});
Object obj = constructor.newInstance(hander);
if(obj instanceof IMasterBusiness){
IMasterBusiness mb = (IMasterBusiness)obj;
mb.moreMoney();
}else{
System.out.println("代理失败");
}
}
Proxy:
Java 动态代理机制的主类,提供了2个静态方法来生成指定接口的代理类或者对象
//获取指定接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
//生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
InvocationHandler:
调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy, Method method, Object[] args)
拆分动态代理对象创建过程
1. 首先是new 一个InvocationHandler的实现类对象 new MasterBusinessHander(new MasterBusiness()) 需要为调用处理器指定一个真实的业务对象,我们调用代理类的方法最终还是要调用到真实业务对象的方法,就是我们在这里提供的业务对象。 2. 根据指定的接口生成一个代理类对象 Proxy.getProxyClass(MasterBusiness.class.getClassLoader(), MasterBusiness.class.getInterfaces()); 在getProxyClass这个方法里会对接口数组进行一些验证,而且会把创建的代理对象类缓存到一个map(暂称为proxymap)中,类加载器作为proxymap的key,proxymap的value又是一个map(暂称为valuemap),valuemap的key是接口数组生产的一个对象,value就是我们创建的代理类对象。这些可以看看Proxy的代码。 3. 获取代理类的构造器 mbClass.getConstructor(new Class[]{InvocationHandler.class}) 我之前一直没有弄明白这个mbClass到底是一个什么样的类,看到下面这张图就可以看得很明白了。$ProxyN就是我们生产的代理类($以及N是Proxy定义的一个明白规则),这个类实现业务接口A,B,X,同时继承了Proxy类。在Proxy类中定义了一个protect访问权限的构造器 protected Proxy(InvocationHandler h) { this.h = h; }
4. 通过构造函数对象创建动态代理类实例 constructor.newInstance(hander); 在创建代理对象的过程中用到了很多反射方面的知识。 |
待解决的问题
查看Proxy的源码,生成代理类的代码如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
try {
// 动态地定义新生成的代理类
proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
sun.misc.ProxyGenerator没有公开代码,proxy到底如何调用处理器InvocationHandler我们就无法得知了,一个牛人给出了一个推演
// 假设代理类为 SimulatorProxy, 其类声明将如下 final public class SimulatorProxy implements businessInterface { // 调用处理器对象的引用 protected InvocationHandler handler; // 以调用处理器为参数的构造函数 public SimulatorProxy(InvocationHandler handler){ this.handler = handler; } // 实现接口方法 public void businessMethod(int arg1, long arg2, String arg3) throws ExceptionA, ExceptionB { // 第一步是获取方法的 Method 对象 java.lang.reflect.Method method = null; try{ method = businessInterface.class.getMethod( " businessMethod ", new Class[] {int.class, long.class, String.class} ); } catch(Exception e) { } // 第二步是调用 handler 的 invoke 方法分派转发方法调用 Object r = null; try { // 对于原始类型参数需要进行装箱操作,高级别版本的JDK可以自动装箱 r = handler.invoke(this, method, new Object[] { arg1, arg2, arg3}); }catch(Throwable e) { } } } |
这个推演的思路个人认为是正确的,也解释了我的疑惑