posts - 0,  comments - 6,  trackbacks - 0

代理模式的定义

    代理模式用于为某一个对象提供一个替身(一个代理对象),并通过这个替身控制客户代码对这个对象的访问。代理模式属于对象的结构模式。

    下面是一些使用代理模式的场合:

用远程代理控制访问远程对象

虚拟代理控制访问创建开销大的资源对象

保护代理基于权限控制对资源的访问


    首先,让我们来看一下代理模式的结构类图:


代理模式简单例子

业务接口

public interface IHello {

/** 假设这是一个业务方法*/

void sayHello(String name);

}

业务实现类

public class Hello implements IHello {

    public void sayHello(String name) {

        System.out.println("Hello " + name);

    }

    public static void main(String[] args) {

     IHello hello = new Hello();

hello.sayHello("郑州蜂鸟科技");

}

}

运行输出:Hello 郑州蜂鸟科技

    接下来,我们希望对所有该业务方法进行性能测试,即计算业务方法执行消耗的时间。要求不能修改原来的代码,但是可以扩展新的类。也就是要满足面向对象设计原则:开闭原则(对扩展开放,对修改关闭)。

public class StaticHelloProxy implements IHello {

    private IHello hello;

 

    public StaticHelloProxy(IHello hello) {

         this.hello = hello;

    }

 

    public void sayHello(String name) {

     long start = System.currentTimeMillis();

        System.out.println("sayHello method start time:"+start);

        hello.sayHello(name);

        long end = System.currentTimeMillis();

        System.out.println("sayHello method end time:"+end);

        System.out.println("Total time:"+(end - start));

    }

    

    public static void main(String[] args) {

IHello hello = new StaticHelloProxy(new Hello());

hello.sayHello("郑州蜂鸟科技");

}

}

输出结果:

sayHello method start time:1290077898984

Hello 郑州蜂鸟科技

sayHello method end time:1290077898984

Total time:0

    以上代理模式只是概念上的设计和实现,实用性比较差。它有很多问题,比如,我们有很多不同业务类的不同业务方法都需要进行性能测试,那么我们就需要些很多代理类,并为每个代理类设计很多方法。这简直是类和方法的爆炸。解决这一问题的方案是动态代理技术。

动态代理模式

针对接口的动态代理:JDK对动态代理模式的支持

    Java 在 java.lang.reflect 包中有自己对代理模式的支持,你可以在运行时动态创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类,我们称之为动态代理。请看结构类图:


Hello创建动态代理,而且它可以为任何实现了接口的类型创建代理:

public class DynamicJdkProxy implements InvocationHandler {

private Object proxyobj;

public DynamicJdkProxy(Object obj) {

proxyobj = obj;

}

public static Object factory(Object obj) {

Class cls = obj.getClass();

return Proxy.newProxyInstance(

cls.getClassLoader(),//加载目标对象类型的类加载器

cls.getInterfaces(), //目标对象类型实现的接口

new DynamicJdkProxy(obj));//代理对象

}

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

        System.out.println(proxy.getClass());

long start = System.currentTimeMillis();

        System.out.println("sayHello method start time:"+start);

if (args != null) {

for (int i = 0; i < args.length; i++) {

System.out.println(args[i] + "");

}

}

Object o = method.invoke(proxyobj, args);

long end = System.currentTimeMillis();

        System.out.println("sayHello method end time:"+end);

        System.out.println("Total time:"+(end - start));

return o;

}

}

测试代码:

public static void main(String[] args) {

IHello hello = (IHello) DynamicJdkProxy.factory(new Hello());

hello.sayHello("蜂鸟科技");

}

执行结果:

class $Proxy0

sayHello method start time:1284427181218

蜂鸟科技

Hello 蜂鸟科技

sayHello method end time:1284427181265

Total time:47

    使用JDK创建代理有一个限制,即它只能为接口创建代理,这一点我们从Proxy的接口方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)就看得很清楚,第三个入参interfaces就是为代理实例指定的实现接口。虽然,面向接口的编程被很多很有影响力人(包括Rod Johnson)的推崇,但在实际开发中,开发者也遇到了很多困惑:难道对一个简单业务表的操作真的需要创建5个类(领域对象类、Dao接口,Dao实现类,Service接口和Service实现类)吗?对于这一问题,我们还是留待大家进一步讨论。现在的问题是:对于没有通过接口定义业务方法的类,如何动态创建代理实例呢?JDK的代理技术显然已经黔驴技穷,CGLib作为一个替代者,填补了这个空缺。你可以从http://cglib.sourceforge.net/获取CGLib的类包,也可以直接从Spring的关联类库lib/cglib中获取类包。

针对类的动态代理:CGLIB动态代理库

    CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并在拦截方法相应地织入横切逻辑。下面,我们采用CGLib技术,编写一个可以为任何类创建织入性能监视横切逻辑的代理对象的代理器,如代码所示:

public class DynamicCglibProxy implements MethodInterceptor {

private Enhancer enhancer = new Enhancer();

public Object getProxy(Class clazz) {

enhancer.setSuperclass(clazz);// ① 设置需要创建子类的类

enhancer.setCallback(this);

return enhancer.create(); // ②通过字节码技术动态创建子类实例

}

public Object intercept(Object obj, Method method, Object[] args,

MethodProxy proxy) throws Throwable {

    System.out.println(obj.class);//看看动态生成的对象的类型;

long start = System.currentTimeMillis();

        System.out.println("sayHello method start time:"+start);

        

Object result = proxy.invokeSuper(obj, args); // ③ 通过代理类调用父类中的方法

long end = System.currentTimeMillis();

System.out.println("sayHello method end time:"+end);

        System.out.println("Total time:"+(end - start));

return result;

}

}

在上面代码中,你可以通过getProxy(Class clazz)为一个类创建动态代理对象,该代理对象是指定类clazz的子类。在这个代理对象中,我们织入性能监视的横切逻辑(粗体部分)。intercept(Object obj, Method method, Object[] args,MethodProxy proxy)CGLib定义的Inerceptor接口的方法,obj表示父类的实例,method为父类方法的反射对象,args为方法的动态入参,而proxy为代理类实例。

下面,我们通过CglibProxyForumServiceImpl类创建代理对象,并测试代理对象的方法,如代码所示:

public static void main(String[] args) {

DynamicCglibProxy proxy = new DynamicCglibProxy();

Hello h = (Hello) proxy.getProxy(Hello.class);

h.sayHello("蜂鸟科技");

}

执行结果:

class com.ntcsoft.proxy.noproxy.Hello$$EnhancerByCGLIB$$127f0ad5

sayHello method start time:1284427305921

Hello 蜂鸟科技

sayHello method end time:1284427305968

Total time:47

到这里我们简单掌握了代理模式。动态代理在struts2、hibernate和spring中都大量使用到了,这将对我们理解这些框架有很大意义。有时间我们来模拟实现这些框架的部分功能。

学软件开发,到蜂鸟科技!
超强的师资力量 、完善的课程体系 、超低的培训价格 、真实的企业项目。

网址:www.ntcsoft.com 
电话:0371-63839606 
郑州软件开发兴趣小组群:38236716

posted on 2010-11-26 01:01 whistler 阅读(301) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航:
 
<2018年11月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

留言簿(2)

我参与的团队

文章档案(22)

搜索

  •  

最新评论