什么是代理模式
在我们现实生活中的购物场景中,我们通常是从代理商那里购买产品,而不是从厂家那里购买。

而代理模式的思想也是类似的,它分为几个角色:
- 抽象角色:一般是接口或抽象类
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 它在代理真实角色的基础上 , 一般会做一些附属的操作。
- 客户:使用代理角色进行一些操作。
静态代理
以电影上映为例:电影是电影公司委托给电影院进行播放的,而电影院在提供播放电影的服务之外,还可以提供其他服务:例如售卖零食等。现在我们用代理模式来实现这个过程
(1)抽象角色:电影接口,它描述了它的功能——播放电影
public interface Movie {
void play();
}
(2)被代理类:实现电影接口的动作片电影:
public class ActionMovie implements Movie {
public void play() {
System.out.println("正在播放动作电影《终结者》");
}
}
(3)代理类:电影院:
public class Cinema implements Movie{
ActionMovie am;
public Cinema(ActionMovie am) {
this.am = am;
}
public void play() {
playAdvertisement(true);
am.play();
playAdvertisement(false);
}
public void playAdvertisement(boolean isStart){
if ( isStart ) {
System.out.println("电影马上开始,开始售卖零食");
} else {
System.out.println("电影即将结束,开始关门");
}
}
}
(4)测试方法:
@Test
public void testProxy() {
Movie movie = new Cinema(new ActionMovie());
movie.play();
}
可以发现,代理模式可以让代理类(电影院)在不修改被代理对象(动作片电影类)的基础上,加上一些新功能。而所谓的静态代理是指:代理类(电影院类)的.class文件在运行前就已经存在。
静态代理的好处
- 公共业务由代理完成,实现了业务的分工。并且公共业务发生扩展时更加方便和集中;
- 真实角色不需要关注公共事情。
缺点
如果真实对象增加,比如此处电影院还可以代理播放其他类型的电影,那么就要在代理类Cinema里原有的基础上添加代码,这使得工作量大大增加。
动态代理
在动态代理中,代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手动编写源码。
同样以电影院上映为例,保持电影接口,动作片电影类不变,修改一下代理类:
(1)代理类不再是实现Movie接口,而是继承一个InvocationHandler类,表明它是一个动态代理类
public class Cinema implements InvocationHandler {
Object movie;
public Cinema(Object movie) {
this.movie = movie;
}
public void playAdvertisement(boolean isStart){
if ( isStart ) {
System.out.println("即将开始播放,开始售卖零食");
} else {
System.out.println("即将结束播放,开始关门");
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
playAdvertisement(true);
method.invoke(movie, args);
playAdvertisement(false);
return null;
}
}
(2)测试方法
@Test
public void testProxy() {
InvocationHandler movie = new Cinema(new ActionMovie());
//代理类
Movie movieProxy = (Movie) Proxy.newProxyInstance(ActionMovie.class.getClassLoader(),
ActionMovie.class.getInterfaces(), movie);
movieProxy.play();
}
相关类
上面的代码是通过Proxy类的静态方法newProxyInstance ()来动态创建代理类。
Proxy提供了创建动态代理类和实例的静态方法,它同时也是由这些方法创建而来的所有动态代理类的父类。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//loader对象表示类加载器;interfaces对象表示代理接口;
InvocationHandler类是什么
InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的InvocationHandler实现类(本例的电影院对象)。当代理的方法被调用时,代理便会通知和转发给内部的InvocationHandler实现类。其实现类可以通过接口方法invoke来调用被代理类(本例的动作电影类)的方法。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
//代理对象proxy;代理对象调用的方法method;调用方法的参数args
}
扩展案例
在原来案例的基础上,我们再增加需求:电影院还可以播放悬疑片:
public class SuspenseMovie implements Movie {
public void play() {
System.out.println("正在播放《惊魂记》");
}
}
测试方法:
@Test
public void testProxy() {
InvocationHandler actionMovie = new Cinema(new ActionMovie());
InvocationHandler suspenseMovie = new Cinema(new SuspenseMovie());
//代理类
Movie actionMovieProxy = (Movie) Proxy.newProxyInstance(ActionMovie.class.getClassLoader(),
ActionMovie.class.getInterfaces(), actionMovie);
Movie susMovieProxy = (Movie) Proxy.newProxyInstance(
SuspenseMovie.class.getClassLoader(),
SuspenseMovie.class.getInterfaces(), suspenseMovie);
actionMovieProxy .play();
susMovieProxy.play();
}
相比于只能代理某一角色的静态代理,动态代理可以代理某一类业务(多个类),比如本例中Cinema代理的是电影接口的实现类,它不会更改原有代码。