首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议106:动态代理可以使代理模式更加灵活

关灯直达底部

Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发。我们知道一个静态代理是通过代理主题角色(Proxy)和具体主题角色(Real Subject)共同实现抽象主题角色(Subject)的逻辑的,只是代理主题角色把相关的执行逻辑委托给了具体主题角色而已,一个简单的静态代理如下所示:


//抽象主题角色

interface Subject{

//定义一个方法

public void request();

}

//具体主题角色

class RealSubject implements Subject{

//实现方法

public void request(){

//业务逻辑处理

}

}

//代理主题角色

class Proxy implements Subject{

//要代理哪个实现类

private Subject subject=null;

//默认被代理者

public Proxy(){

subject=new RealSubject();

}

//通过构造函数传递被代理者

public Proxy(Subject_subject){

subject=_subject;

}

//实现接口中定义的方法

public void request(){

before();

subject.request();

after();

}

//预处理

private void before(){

//do something

}

//善后处理

private void after(){

//do something

}

}


这是一个简单的静态代理。Java还提供了java.lang.reflect.Proxy用于实现动态代理:只要提供一个抽象主题角色和具体主题角色,就可以动态实现其逻辑的,其示例代码如下:


//抽象主题角色

interface Subject{

//定义一个方法

public void request();

}

//具体主题角色

class RealSubject implements Subject{

//实现方法

public void request(){

//业务逻辑处理

}

}

class SubjectHandler implements InvocationHandler{

//被代理的对象

private Subject subject;

public SubjectHandler(Subject_subject){

subject=_subject;

}

//委托处理方法

public Object invoke(Object proxy, Method method, Objectargs)

throws Throwable{

//预处理

System.out.println("预处理");

//直接调用被代理类的方法

Object obj=method.invoke(subject, args);

//后处理

System.out.println("后处理");

return obj;

}

}


注意看,这里没有了代理主题角色,取而代之的是SubjectHandler作为主要的逻辑委托处理,其中invoke方法是接口InvocationHandler定义必须实现的,它完成了对真实方法的调用。

我们来详细了解一下InvocationHanlder接口,动态代理是根据被代理的接口生成所有方法的,也就是说给定一个(或多个)接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那各位读者想想看,动态代理怎么才能实现代理接口中的方法呢?在默认情况下所有方法的返回值都是空的,是的,虽然代理已经实现了它,但是没有任何的逻辑含义,那怎么办?好办,通过InvocationHandler接口的实现类来实现,所有方法都是由该Handler进行处理的,即所有被代理的方法都由InvocationHandler接管实际的处理任务。

我们接着来看动态代理的场景类,代码如下:


public static void main(Stringargs){

//具体主题角色,也就是被代理类

Subject subject=new RealSubject();

//代理实例的处理Handler

InvocationHandler handler=new SubjectHandler(subject);

//当前加载器

ClassLoader cl=subject.getClass().getClassLoader();

//动态代理

Subject proxy=(Subject)Proxy.newProxyInstance(cl, subject.getClass().

getInterfaces(),handler);

//执行具体主题角色方法

proxy.request();

}


此时就实现了不用显式创建代理类即实现代理的功能,例如可以在被代理角色执行前进行权限判断,或者执行后进行数据校验。

动态代理很容易实现通用的代理类,只要在InvocationHandler的invoke方法中读取持久化数据即可实现,而且还能实现动态切入的效果,这也是AOP(Aspect Oriented Programming)编程理念。