解析Java代理

1. 代理模式

1.1定义

代理模式(Proxy)为另一个对象提供一个替身或占位符以控制对这个对象的访问,简而言之就是用一个对象来代表另一个对象。他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

1.2 结构图


1.3 示例

1.3.1 接口类Acount,.java

public interface Account {
    // 查看账户方法
    public void queryAccount();
    // 修改账户方法
    public void updateAccount();
}

1.3.2 实现类AccountImpl.java

public class AccountImpl implements Account {
    @Override
    public void queryAccount() {
        System.out.println("查看账户方法……");
    }
    @Override
    public void updateAccount() {
        System.out.println("修改账户方法……");
    }
}

1.3.3 代理类AccoutProxy.java

/**
 * 这是一个代理类(增强CountImpl实现类)
 *
 * @author Administrator
 *
 */
public class AccountProxy implements Account {
    private Account count;
    /**
     * 覆盖默认构造器
     *
     * @param count
     */
    public AccountProxy(Account count) {
        this.count = count;
    }
    @Override
    public void queryAccount() {
        System.out.println("静态代理:事务处理之前");
        // 调用委托类的方法;
        count.queryAccount();
        System.out.println("静态代理:事务处理之后");
    }
    @Override
    public void updateAccount() {
        System.out.println("静态代理:事务处理之前");
        // 调用委托类的方法;
        count.updateAccount();
        System.out.println("静态代理:事务处理之后");
    }
}

1.3.4 Test

    AccountImpl countImpl = new AccountImpl();
    Account count = new AccountProxy(countImpl);
    count.updateAccount();
    count.queryAccount();

1.3.5 结果

静态代理:事务处理之前

修改账户方法……

静态代理:事务处理之后

静态代理:事务处理之前

查看账户方法……

静态代理:事务处理之后

2 Java动态代理

2.1 定义

静态代理如1.3节所示,一个代理类只能代理一个接口的类,我们有多少个接口需要代理则要创建多少个代理类,而动态代理的意思是一个代理类可以动态的代理多个接口的类,这样,我们只需要有一个动态代理类就可以代理无限个接口或类。

Java有两种方法实现动态代理:通过JDK本身的方法以及cglib包实现的方法。通过JDK实现的动态代理需要被代理的类必须实现接口,而cglib包方法则没有要求。这两种方法都是通过Java的反射机制实现的。

2.2 JDK实现

2.2.1 示例

public class DynamicProxy implements InvocationHandler{
    private Object target;
    DynamicProxy(Object target){
        this.target = target;
    }
    DynamicProxy(){
    }
    /**
     * 绑定需要代理的对象
     * @param target
     * @return
     */
    public void bind(Object target) {
        this.target = target;
    }
    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy(){
        //取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
    }
    @Override
    /**
     * 调用方法
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result=null;
        //..... advice()-->前置通知
        System.out.println("动态代理:事务开始");
        try {
            result = method.invoke(target, args);
            // afteradvice() -->后置通知
            System.out.println("动态代理:事务结束");
        } catch (RuntimeException e) {
            System.out.println("动态代理:意外情况");
            //exceptionadvice()--> 例外通知
        }finally{
            System.out.println("动态代理:最终通知");
            //finallyadvice(); -->最终通知
        }
        return result;
    }
}

2.2.2 说明

DynamicProxy类有两个功能:1、实现InvocationHandler接口;2、创建动态代理。其中创建代理最重要的是getProxy()方法的代码:

Proxy.newProxyInstance(target.getClass().getClassLoader(),

                target.getClass().getInterfaces(), this);   

target为被代理的对象,该方法有三个参数:1、被代理类加载器;2、被代理对象所实现的接口;3、实现了InvocationHandler接口的对象。

通过分析JDK源码可知该句代码所创建代理类(部分参考自http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/),假设为AccountProxy的情况如下图所示:


所创建的AccountProxy类继承java.lang.reflect.Proxy类,并且实现了代理对象AccountProxy所实现的接口Account,注意,这个接口可以不止一个。对被代理对象AccountImpl方法的实现调用了InvocationHandler子类的invoke方法。由此可知,由此可知,当我们需要动态代理时,需要有两个条件:

1、被代理对象必须实现某个接口。Proxy.newProxyInstance创建的代理类继承了Proxy类,Java的单一继承机制决定了不能再继承代理对象,只能转为实现被代理对象的接口,并且引用被代理对象。因此DynamicProxy 类所绑定的代理对象target必须有实现的接口。

2、实现InvocationHandler接口的对象。对代理类函数的调用都转为对InvocationHandler实现子类的invoke方法的调用。因此DynamicProxy 类实现了InvocationHandler接口。

该类的测试代码如下:

DynamicProxy dynamicProxy = new DynamicProxy(new AccountImpl());
Account account = (Account)dynamicProxy.getProxy();
account.queryAccount();
account.updateAccount();

需要说明的是,dynamicProxy.getProxy()获取了通过JDK创建的代理对象,它实现了Account接口,所以我们可以向上转型为Account类型。

2.3 CGLIB实现

2.3.1 示例

/**
 * 依赖的jar包有:cglib-nodep.jar和asm.jar
 */
public class CglibProxy implements MethodInterceptor {
    private Object target;
    CglibProxy(Object target){
       this.target = target;
    }
    CglibProxy(){
    }
    /**
     * 绑定需要代理的对象
     * @param target
     * @return
     */
    public void bind(Object target) {
        this.target = target;
    }
    public Object getProxy() {
        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 {
        System.out.println("事物开始");
        proxy.invokeSuper(obj, args);
        System.out.println("事物结束");
        return null;
    }
}

2.3.2 说明

CglibProxy类有两个功能:1、实现MethodInterceptor 接口;2、创建动态代理。其中创建代理最重要的是getProxy()方法,它通过Enhancer 类创建动态代理,它需要两个参数:1、被代理类;2、实现MethodInterceptor 接口的类。依据代码推断所创建的动态代理,假设为AccountProxy,其类图如下图所示。由于AccountProxy是通过继承方式创建的,因此对被代理对象没有任何要求。


测试代码如下:

        CglibProxy cglib = new CglibProxy(new AccountImpl());
        AccountImpl accountCglib = (AccountImpl)cglib.getProxy();
        accountCglib.queryAccount();


文章所示代码下载自:http://download.csdn.net/detail/wyuan8913/5033601

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值