java代理模式--静态代理和动态代理

来源:互联网 发布:留学生落户北京 知乎 编辑:程序博客网 时间:2024/05/19 15:25

设计模式中的代理模式应用很广,网上这方面资料很多,客户端代码不想或不能够直接访问被调用对象时,代理对象可以在客户端和目标对象之间起到中介作用,常用的场景:

  1. 创建一个系统开销很大的对象(延迟创建或真正调用时再创建)
  2. 被调用对象在远程主机上
  3. 目标对象的功能还不足以满足需求(增强功能)

java很容易就可以实现代理模式,每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy)。那么有没有一种机制能够让系统在运行时动态创建代理类?从JDK 1.3开始,Java语言提供了对动态代理的支持。下面举例比较两种代理方式

一、静态代理
定义两个接口和对应的实现类
接口1:Account.java

package com.css.sword.proxy;public interface Account {public void query();public int sum();}

实现类:AccountImpl.java

package com.css.sword.proxy;public class AccountImpl implements Account {    @Override    public void query() {        // TODO Auto-generated method stub        System.out.println("开始查询账目……");    }    @Override    public int sum() {        // TODO Auto-generated method stub        System.out.println("开始合计……");        return 0;    }}

接口2:Cook.java

package com.css.sword.proxy;public interface Cook {public void fire();public void cut();}

实现类:CookImpl.java

package com.css.sword.proxy;public class CookImpl implements Cook {    @Override    public void fire() {        // TODO Auto-generated method stub        System.out.println("开始加火……");    }    @Override    public void cut() {        // TODO Auto-generated method stub        System.out.println("开始切菜……");    }}

代理类:每个接口需创建一个代理类,注意代理类要实现一样的接口
AccountProxy.java

package com.css.sword.proxy;public class AccountProxy implements Account {    private Account account;    AccountProxy(Account account){        this.account=account;    }    @Override    public void query() {        // TODO Auto-generated method stub        System.out.println("开始调用query方法");        account.query();        System.out.println("query方法调用结束");    }    @Override    public int sum() {        // TODO Auto-generated method stub        System.out.println("开始调用sum方法");        account.sum();        System.out.println("sum方法调用结束");        return 0;    }}

CookProxy.java

package com.css.sword.proxy;public class CookProxy implements Cook {    private Cook cook;    CookProxy(Cook cook)    {        this.cook=cook;    }    @Override    public void fire() {        // TODO Auto-generated method stub        System.out.println("开始调用fire方法");        cook.fire();        System.out.println("fire方法调用结束");    }    @Override    public void cut() {        // TODO Auto-generated method stub        System.out.println("开始调用cut方法");        cook.cut();        System.out.println("cut方法调用结束");    }}

测试类:TestStaticProxy.java

package com.css.sword.proxy;public class TestStaticProxy {    public static void main(String[] args)    {        AccountProxy accountProxy = new AccountProxy(new AccountImpl());        accountProxy.query();        accountProxy.sum();        System.out.println("-------------------------------------------------");        CookProxy cookProxy = new CookProxy(new CookImpl());        cookProxy.fire();        cookProxy.cut();    }}

结果:
这里写图片描述
这里可以看到静态代理每个接口都需要一个代理类,如果需要每个方法在执行前和后加入的是相同的操作,代理类中每个方法都需要加入相同的代码,这样程序中就有太多重复的代码。动态代理可以有效解决这个问题。
二、动态代理
接口及实现类不变,动态代理只需一个代理类:

package com.css.sword.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class DynamicProxy implements InvocationHandler {    private Object target;    public Object getProxy(Object target)    {        this.target=target;        return Proxy.newProxyInstance(target.getClass().getClassLoader(),                  target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)    }    @Override    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        // TODO Auto-generated method stub        String methodName = method.getName();        System.out.println("开始调用"+methodName+"方法");        Object result = method.invoke(target, args);        System.out.println(methodName+"方法调用结束");        return result;    }}

测试结果:
这里写图片描述
动态代理类需实现InvocationHandler接口,并实现其invoke方法,每当通过Proxy.newProxyInstance(……)方法获得某个接口的代理并调用方法时,程序就会回调invoke(……)方法。这样就不用为每个接口配置一个代理类,也不用为每个方法填写相同的增强代码。这里也可以看到jdk本身实现的动态代理有个弊端,那就是只能为接口生成代理,如果一个类没有实现接口,又想生成其代理类有什么办法?cglib可以解决这个问题
三、cglib动态代理
要想使用cglib需要下载相关包,网上一般会提供两种包,例如下图:
这里写图片描述
起初我一直不知道该用哪个包,后来在查阅相关资料和对两个包做出的对比,得出以下结论(但不确定是否正确,如果有什么不对希望大家批评指正):
cglib底层是用asm框架来处理字节码,如果使用cglib-x.x.x.jar的话需要再下载asm相关包,而cglib-nodep-x.x.x.jar中已包含asm需要的相关类,就不需要再下载其它包。经过测试得出的结果也是这样的。单独引cglib包需要注意的cglib版本和asm版本。在测试过程中总是因为这两个包的版本不能匹配而报错,最终测试通过的版本是cglib-2.2.3和asm-all-4.0.jar
新建一个类(不实现接口):AccountService.java

package com.css.sword.proxy;public class AccountService {    public void query() {        // TODO Auto-generated method stub        System.out.println("开始查询账目……");    }    public int sum() {        // TODO Auto-generated method stub        System.out.println("开始合计……");        return 0;    }}

代理类:CglibProxy.java

package com.css.sword.proxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {    private Object target;    public Object getProxy(Object target){        this.target = target;          Enhancer enhancer = new Enhancer();          enhancer.setSuperclass(this.target.getClass());          // 回调方法          enhancer.setCallback(this);          // 创建代理对象          return enhancer.create();    }    @Override    public Object intercept(Object obj, Method method, Object[] args,              MethodProxy proxy) throws Throwable {        // TODO Auto-generated method stub        String methodName = method.getName();        System.out.println("开始调用"+methodName+"方法");          proxy.invokeSuper(obj, args);          System.out.println(methodName+"方法调用结束");        return null;    }}

测试类:TestCglibProxy.java

package com.css.sword.proxy;public class TestCglibProxy {    public static void main(String[] args){        CglibProxy cProxy = new CglibProxy();        AccountService accountProxy = (AccountService) cProxy.getProxy(new AccountService());        accountProxy.query();        accountProxy.sum();    }}

测试结果:
这里写图片描述

0 0
原创粉丝点击