一,代理模式
1.代理模式的定义
为其他对象提供一个代理,以控制该对象的访问。
2.代理模式的使用场景
总而言之,只要客户端代码不能或不想直接访问被调用对象——这种情况有很多原因,比如需要创建一个系统开销很大的对象,或者被调用对象在远程主机上,或者目标对象的功能还不足以满足需求…而是额外创建一个代理对象返回给客户端使用,那么这种设计方式就是代理模式。
3.代理模式的UML类图
4.代理模式的实现
/**
* Image接口,代表大图片对象所实现的接口
* @author Administrator
*
*/
public interface Image {
void show();
}
/**
* BigImage模拟一个很大的图片
* @author Administrator
*
*/
public class BigImage implements Image{
public BigImage() {
try {
//程序暂停3秒模拟系统开销
Thread.sleep(3000);
System.out.println("图片装载成功!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//实现Image里的show方法
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("绘制实际的大图片");
}
}
public class ImageProxy implements Image{
//组合一个image对象,作为被代理的对象
private Image image;
//使用抽象实体来初始化代理对象
public ImageProxy(Image image) {
this.image = image;
}
/**
* 重写Image接口的show方法
* 该方法用于控制对被代理对象的访问
* 并根据需要负责创建和删除被代理对象
*/
@Override
public void show() {
// TODO Auto-generated method stub
//只有当真正需要调用image的show方法时才创建被代理对象
if(null==image) {
image = new BigImage();
}
image.show();
}
}
/**
* 程序入口函数
* @author Administrator
*
*/
public class BigImageTest {
public static void main(String[] args) {
long start = System.currentTimeMillis();
//程序返回一个Image对象,该对象只是BigImage的代理对象
Image image = new ImageProxy(null);
System.out.println("系统得到Image对象的时间开销:"+
(System.currentTimeMillis()-start));
//只有当实际调用image代理的show()方法时,程序才会真正创建被代理对象
image.show();
}
}
5.总结
代理模式应用广泛,其他形式的结构型模式中,都可以看到代理模式的影子,有些模式单独作为一种设计模式,倒不如说是对代理模式的一种针对性优化。而且代理模式几乎没有什么缺点而言,它是细分化至很小的一种模式,要真的说一个缺点,那么就是所有设计模式的通病:对类的增加,不过在这种孰优孰劣的局势下,就算对类的稍微增加又何妨呢?
二,动态代理模式
除了上面处于性能考虑使用代理模式外,代理模式还有另一种常用场景:当目标对象的功能不足以满足客户端需求时,系统可以为改对象创建一个代理对象,而代理对象可以增强原目标对象的功能。
借助于Java提供的Proxy和InvocationHandler,可以实现在运行时生成动态代理的功能,而动态代理对象就可作为目标对象使用,而且增强了目标对象的功能。
public interface Dog {
//info()方法声明
void info();
//run()方法声明
void run();
}
public class GunDog implements Dog {
@Override
public void info() {
// TODO Auto-generated method stub
System.out.println("我是一只猎狗");
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我奔跑迅速");
}
}
public class TxUtil {
//第一个拦截器方法:模拟事务开始
public void beginTx() {
System.out.println("=======模拟事务开始======");
}
//第二个拦截器方法:模拟事务结束
public void endTx() {
System.out.println("=======模拟事务结束========");
}
}
JDK动态代理的关键就在于下面的MyInvocationHandler类,该类是一个InvocationHandler实现类,改实现类的invoke方法将会作为代理对象的方法实现。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler{
//需要被代理的对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
TxUtil tx = new TxUtil();
//执行TxUtil对象中的beginTx();
tx.beginTx();
//以target作为主调来执行method方法
Object result = method.invoke(target, args);
//执行TxUtil对象中的endTx().
tx.endTx();
return result;
}
}
下面提供的MyProxyFactory类,该对象专为指定的target生成动态代理实例。
import java.lang.reflect.Proxy;
public class MyProxyFactory {
//为指定target生成动态代理对象
public static Object getProxy(Object target)
throws Exception{
//创建一个MyInvocationHandler对象
MyInvocationHandler handler = new MyInvocationHandler();
//为MyInvocationHandler设置target对象
handler.setTarget(target);
//创建并返回一个动态代理
return Proxy.newProxyInstance(target.getClass().getClassLoader()
,target.getClass().getInterfaces(), handler);
}
}
/**
* 主程序测试动态代理
* @author Administrator
*
*/
public class Test {
public static void main(String[] args) throws Exception{
//创建一个原始的GunDog对象,作为target
Dog target = new GunDog();
//以指定的target来创建动态代理
Dog dog = (Dog)MyProxyFactory.getProxy(target);
//调用代理对象的info()和run()方法
dog.info();
dog.run();
}
}
这种动态代理在AOP(Aspect Orient Program,面向切面编程)里被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理的方法与目标对象的方法存在差异:AOP代理的方法可以在执行目标方法之前、之后插入一些通用处理。