使用
代理模式的使用广泛见于Spring AOP中,核心功能就是在不修改原来的类的代码的基础上,为类添加新的功能和属性。 比如用作性能分析和日志记录的切面,实质上就是一个代理类。它要代理的主题类就是AOP中的切点。
基本模式
一个接口ISubject, 实体对象RealSubject实现它。
代理对象Proxy同样实现ISubject接口,但实现的方式是通过直接调用内部持有的RealSubject对象的request()方法。
客户端或是使用者(Client), 只通过Proxy来执行对应的request()。
Java实现:
public interface ISubject{
void request();
}
public class RealSubject implements ISubject{
@Override
void request();
}
public class Proxy implements ISubject{
private RealSubject realSubject;
@Override
void request(){
preRequest();
realSubject.request();
postRequest();
}
}
动态代理
上面的基本模式其实是一种静态代理,每一个代理类和一个主题类一一对应。
答案是不需要。
这就是动态代理。
JDK动态代理
考虑这样一个应用:两种渠道接收信件。电子邮件和邮局。 对到来的信件都要登记。 现在要利用代理模式为登记增加计数功能,对不同方式到来的信件分别统计。
类图:
- 抽象主题IRegist
public interface IRegist {
void regist();
}
- 两个具体主题
public class fromEmail implements IRegist {
@Override
public void regist() {
System.out.println("from Email");
}
}
public class fromPost implements IRegist {
@Override
public void regist() {
System.out.println("from Post");
}
}
- 定义计数实现类CountInvoke
//InvocationHandler是java反射包中的接口
//实现该接口以供后面生成代理对象
public class CountInvoke implements InvocationHandler {
private int count = 0;
//具体主题对象
//可以推广到任意主题对象的计数
private Object obj;
public CountInvoke(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
count++;
method.invoke(obj, args);
return null;
}
public int getCount(){ return count; }
}
- 创建GenricProxy
public class GenericProxy {
public static Object createProxy(Object obj, InvocationHandler invokeObj){
//代理obj类的所有接口方法
Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), invokeObj);
return proxy;
}
}
- 测试类Test
public class Test {
public static void main(String[] args) {
IRegist email = new fromEmail();
IRegist post = new fromPost();
//同一个切面实现类
//类内部实现了invoke方法
//可以处理两个不同的对象
CountInvoke emailInvoke = new CountInvoke(email);
CountInvoke postInvoke = new CountInvoke(post);
//通过GenericProxy工厂类生成不同的proxy对象
IRegist emailProxy = (IRegist) GenericProxy.createProxy(email,
emailInvoke);
IRegist postProxy = (IRegist) GenericProxy.createProxy(post,
postInvoke);
//只调用了regist()
//却实现了计数
for (int i = 0; i < 5; i++) {
emailProxy.regist();
}
for (int i = 0; i < 10; i++) {
postProxy.regist();
}
//email count = 5
System.out.println("email count = " + emailInvoke.getCount());
//post count = 10
System.out.println("post count = " + postInvoke.getCount());
}
}
虚拟代理
如果需要创建一个资源消耗很大的对象,先创建一个相对较小的对象来表示。 真实对象只有在需要时才会被真正创建。 当用户请求一个“大”对象时,虚拟代理在该对象真正被创建出来之前扮演着替身的角色。 当该对象被创建出来之后,虚拟代理将用户的需求直接委托给该对象。
- 主题接口
public interface IItem {
int getId();
String getName();
//大对象
BigObject getBigObject();
void itemFill();
}
- 具体主题
public class RealItem implements IItem{
private int id;
private String name;
private BigObject bigObject;
@Override
public int getId() {
return 0;
}
@Override
public String getName() {
return null;
}
@Override
public BigObject getBigObject() {
return null;
}
//填充大对象
@Override
public void itemFill() {
bigObject = new BigObject();
}
}
- 代理类
public class ProxyItem implements IItem {
private RealItem realItem;
//关键的标志位
private boolean isFill;
public ProxyItem(RealItem realItem) {
this.realItem = realItem;
}
@Override
public int getId() {
return realItem.getId();
}
@Override
public String getName() {
return realItem.getName();
}
@Override
public BigObject getBigObject() {
return realItem.getBigObject();
}
@Override
public void itemFill() {
if(!isFill){
realItem.itemFill();
isFill = true;
}
}
}
isFill标志位的值,若考虑多线程情况需要添加volatile关键字。保证只创建并填充一次。