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

本文详细介绍了设计模式中的代理模式,包括静态代理、JDK动态代理和Cglib动态代理三种实现方式,并通过具体示例展示了每种方式的特点和应用场景。

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

  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();
    }

}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值