为什么要使用代理模式:
比较官方的回答:在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用, 其特征是代理类与委托类有同样的接口。代理模式是常用的java设计模式。
看完这个解释依然云里雾里,我觉得代理除了上述功能,最易理解的是,在原有代码的基础上增加功能时,不会修改原有代码,非常符合程序设计的开闭原则。
代理模式一般分为静态代理和动态代理,顾名思义,静态代理是比较死板,不通用的模式,实现的代码是自己实现。
动态代理实现方式有很多种,主要有java提供的InvocationHandler和spring提供的cglib,前者的真实对象必须继承接口,后者不用继承任何接口和类,也可以实现动态代理。spring AOP就是采用cglib技术实现的动态代理。
静态代理:
举例:电影院放映电影,按照以前的方式,电影院从电影开始到电影结束是一套完整的流程。电影院可以播放各种不同的电影,属于一个规范,因此可以把电影院定义为抽象类或者接口:
public interface Movie {
// 播放电影
public void play();
}
电影远播开始播放一部具体的电影:
public class RealMovie implements Movie{
@Override
public void play() {
System.out.println("开始播放:《阿甘正转》");
}
}
主程序调用:
public class Test {
public static void main(String[] args) {
RealMovie realMovie = new RealMovie();
realMovie.playMovie();
}
后来电影院想增加收入,在播放电影的前后两个环节插入零食贩卖的功能,但是又不希望修改既有代码,于是决定使用静态代理来实现:
public class ProxyMovie implements Movie {
private RealMovie realMovie = new RealMovie();
@Override
public void playMovie() {
System.out.println("买点零食看电影吃吧(电影开播前广告)");
realMovie.playMovie();
System.out.println("买点零食回家吃吧(电影结束后广告)");
}
}
静态代理的特点:代理类和被代理类都需要继承父接口,实现play方法,这样主程序就无法直接访问被代理类,即使被代理类的play方法中增加了N多复杂的代码,也不影响主程序去调用。
动态代理:java提供的Proxy类
电影1:
public class RealMovie implements Movie{
@Override
public void play() {
System.out.println("开始播放:《阿甘正转》");
}
}
电影2:
public class RealMovie2 implements Movie{
@Override
public void play() {
System.out.println("开始播放:《复仇者联盟》");
}
}
代理类:
public class ProxyMovie implements InvocationHandler {
private Object object;
public ProxyMovie(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("买点零食看电影吃吧(电影开播前广告)");
// 真实对象的方法都交由InvocationHandler.invoke方法执行
method.invoke(object, args);
System.out.println("买点零食回家吃吧(电影结束后广告)");
return null;
}
}
可以看出代理类不需要继承被代理类的父类,继承的是java提供的InvocationHandler接口,该接口只有一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
所有被代理的类的方法都会通过该方法调用。且所有扩展功能都在此方法中去实现。
主程序调用代理类:
public class Test {
public static void main(String[] args) {
// 动态代理(代理类交由Proxy动态去实现接口(加载时实现))
RealMovie realMovie = new RealMovie();
InvocationHandler proxyMovie = new ProxyMovie(realMovie);
Movie movie = (Movie) Proxy.newProxyInstance(
realMovie.getClass().getClassLoader(),
realMovie.getClass().getInterfaces(),
proxyMovie);
movie.play();
RealMovie2 realMovie2 = new RealMovie2();
InvocationHandler proxyMovie2 = new ProxyMovie(realMovie2);
Movie movie2 = (Movie)Proxy.newProxyInstance(
realMovie.getClass().getClassLoader(),
realMovie.getClass().getInterfaces(),
proxyMovie2);
movie2.play();
}
}
test主程序执行结果:

有点类似于面向切面编程的感觉。Proxy是java提供的生成动态代理实例的类,此类在程序运行时,会继承被代理类的父类,从方法newProxyInstance传入的参数可以看出,无论是传realMovie.getClass().getInterfaces(),还是realMovie2.getClass().getInterfaces(),
都可以实现代理的功能。应为这两个实例的父类是同一个。另外realMovie.getClass().getClassLoader()获取的都是AppClassLoader ,所以只要是同一个类加载器,传什么实例并不是重点。
java提供的动态代理有一个缺点,就是若类既没有继承父类,也没有实现接口,是无法被代理的,cglib提供的动态代理却可以实现代理
cglib动态代理:
public class RealMovie {
public void playMovie(){
System.out.println("开始播放:《阿甘正传》");
}
}
此类没有继承或者实现任何类。
代理类:
public class MyProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("买点零食看电影吃吧(电影开播前广告)");
methodProxy.invokeSuper(o, objects);
System.out.println("买点零食回家吃吧(电影结束后广告)");
return null;
}
}
test主程序调用”
public class Test {
public static void main(String[] args) {
// cglib动态代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealMovie.class);
enhancer.setCallback(new MyProxy());
RealMovie realMovie = (RealMovie)enhancer.create();
realMovie.playMovie();
}
}
执行结果:

代理成功。
在spring framework 中,AOP就是使用cglib实现的动态代理。
4074

被折叠的 条评论
为什么被折叠?



