代理模式是什么:
当两个类 类a与类b发生交互时,不让类b直接与类a接触,而是通过代理类c与类a交互,类c内部调用类a的处理方法,但在调用前后可以根据需求做不同的处理,使用代理类类c实现了对类a的消息过滤,转发,后续处理等操作,保护并隐藏了类a,为设计不同的策略留下控件,使程序设计更加灵活。
什么情况下需要实现代理:在调用核心方法前进行参数检查;在目标方法前后添加事务;添加缓存
举例来说,定义公用接口vehicle;
public
interface
Vehicle {
void
run();
}
标识车辆的抽象,都有一个run方法;实现这个接口的有各种各样的车 比如:
public
class
Bus
implements
Vehicle{
@Override
public
void
run() {
//
TODO
Auto-generated method stub
System.
out
.println(
"bus is running"
);
}
}
或者:
public
class
Cars
implements
Vehicle{
@Override
public
void
run() {
//
TODO
Auto-generated method stub
System.
out
.println(
"car is running"
);
}
}
....
这些实现vehicle接口的类称为委托类;
现在有一个需求,就是给这些所有的车run之前执行人上车动作,run执行之后执行车上的人下车动作;在保证面向对象原则下解决方案大致有:
1。 直接修改实现类或者给每个委托类创建子类重写run方法 : 可以想象,如果委托类有100多个,将是一个相当繁杂的工作,而且继承实现的多层父子关系使代码臃肿,不利于后期维护。
2. 使用代理类ProxyVehicle:
public
class
ProxyVehicle
implements
Vehicle {
Vehicle
v
;
public
ProxyVehicle(Vehicle v) {
//
TODO
Auto-generated constructor stub
this
.
v
= v;//在实际使用中,只需根据需要将不同的委托类传入代理类构造方法中,就完成了该委托类上下车功能的升级;
}
@Override
public
void
run() {
System.
out
.println(
"快上车!"
);//上车动作
v
.run();
System.
out
.println(
"下车啦!"
);//下车动作
}
}
在实际使用中,只需根据需要将不同的委托类传入代理类构造方法中,就完成了该委托类上下车功能的升级;
public
class
Client {
public
static
void
main(String[] args) {
Vehicle vehicle =
new
Bus();
Vehicle proxyVehicle=
new
ProxyVehicle(vehicle);
proxyVehicle.run();
}
}
长远来看,如果以后不仅添加上下车功能,如果还要添加日志打印功能,权限检查功能,异常处理功能,难道要每个需求都分别给每个实现类重新创建一个代理类或者挨个修改?显然使用代理模式更加方便。 由此可见,使用代理模式相对于方法一有着不小的优势。
上面方法二就是静态代理,为了保持行为的一致性,静态代理类ProxyVehicle也要实现Vehicle接口,这就使静态代理有了很大的局限性,即 一个代理类只能为一个接口服务,在这个例子中这个被服务的接口是Vehicle,其他的类,比如有AircraftCarrier,Battleships等委托类实现了Ships接口,在Ships接口中有Sali()方法,那么如果要给Sail()前后添加上人,下人功能,就要重新定义一个代理类,来实现Ships接口.
为了弥补静态代理这个缺点,出现了动态代理
这里以jdk动态代理为例 还是那个Vehicle的例子;
实现动态创建代理类,只需一行代码:
DynamicProxyInvocator ih=
new
DynamicProxyInvocator(vehicle);
Vehicle proxyVehicle = (Vehicle) Proxy.newProxyInstance(
Bus.
class
.getClassLoader() //第一个参数,指定类加载器,一般用委托类的加载器
,
new
Class<?>[] { Vehicle.
class
} //第二个参数,指定创建的代理类将为这个类实现的哪 ~ //些接口服务
,
ih //第三个参数,指定要对这些接口做什么样的处理和服~ //务
);
唯一在之前静态代理没有出现的是第三个参数,ih是实现InvocationHandler接口的实例,自定义
DynamicProxyInvocator
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
public
class
DynamicProxyInvocator
implements
InvocationHandler {
Object
v
;//与静态代理不同,这里被代理类型不再指定为Vehicle
public
DynamicProxyInvocator(Object obj) {
v
= obj;
}
@Override
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
System.
out
.println(
"上车!"
);
method.invoke(
v
, args); //反射调用相应函数
System.
out
.println(
"下车!"
);
return
null
;
}
}
可以看到,在使用动态代理时没有写死代理类是对哪个类做的代理,InvocationHandler中也未指定该代理类是专门为某个接口服务的;
继续上面Vehicle与Ships的例子,要想简单地为所有的Ship也添加上人 下人功能,只需做如下修改
Ships acc=
new
AirCraftCarrier();
DynamicProxyInvocator ih =
new
DynamicProxyInvocator(acc);
//上下人功能
Ships proxyShip = (Ships) Proxy.newProxyInstance(Ships.
class
.getClassLoader(),
new
Class[] { Ships.
class
}, ih);
proxyShip.sail();
即可为所有实现Ships接口的船添加上下人的功能。
值得一提的是,newProxyInstance的第二个参数是一个数组,可以传入想扩展的所有被代理类实现的接口,只有在这里指定了该接口,InvocationHandler才会拦截该方法,并添加InvocationHandler中做的修饰。另外,在InvocationHandler的
public
Object invoke(Object proxy, Method method, Object[] args)
方法中,可以用method.getName()得到当前拦截到的方法名,进行分别处理。