多年以前在学习设计模式时,一直以为代理就是这个事情我不做了,交给别人做。现在重学设计模式,才发现自己还是太天真,而且经历过这么多事情也明白,就算再好的代理,还是需要自己做一些事情的。
代理模式:在不修改一个类的前提下,实现一个该类的代理类来,代理类需要实现所有行为,并且可以根据需要在行为中增加其他逻辑和细节。
举个例子,王晶需要找周星驰拍一部新电影,即希望调用Zhou.act();
。但是王晶不能直接联系周星驰,他需要联系星爷的经纪人,或者我们称之为代理人,去商讨拍电影的细节工作。这个时候,这位代理人其实需要做更多细节性的工作,比如说
ZhouProxyer.bargainContract();
ZhouProxyer.schedule();
ZhouProxyer.act(); // 实际调用Zhou.act();
ZhouProxyer.complete();
ZhouProxyer.summary();
我们可以看到,作为星爷的代理人,其实他在星爷真正拍电影前后做了很多事情,这些事不需要星爷操心,只需保证自己的act()正常即可。
从这个例子中,我们可以进行抽象,将主要的主体提炼出来:
- Client:客户(王晶),类的使用者;
- Subject:定义行为的抽象类或接口;
- RealSubject:真实的底层实现类,实现了Subject定义的行为(周星驰);
- Proxy:代理类,也实现了Subject定义的行为,且拥有RealSubject的一个实例(经纪人);
现在,我们用三种代理模式来实现上述的这个关系。
静态代理
静态,说白了就是写死的代码,按照你写的代码实现代理机制。上面说的几个主要主体,都需要自己定义和生成。
现在我们先来将Subject接口定义出来。
package com.designpattern.proxy;
public interface IActor {
public void act();
}
而后是星爷的类,它实现了IActor接口,其中为了方便,随便用了一个勤汉单例:
package com.designpattern.proxy;
public class Zhou implements IActor {
private static Zhou instance = new Zhou();
public static Zhou getInstance(){
return instance;
}
@Override
public void act() {
System.out.println("Here comes Zhou's newest movie: Crazy biscuit!");
}
}
然后我们再定义一个Zhou的代理人,同样也实现了IActor:
package com.designpattern.proxy;
public class ZhouProxy implements IActor {
private IActor zhou;
public ZhouProxy(IActor zhou){
this.zhou = zhou;
}
@Override
public void act() {
bargainContract();
schedule();
zhou.act();
complete();
summary();
}
private void complete() {
System.out.println("Movie complete.");
}
private void summary() {
System.out.println("Movie's summary.");
}
private void schedule() {
System.out.println("Make a schedule for the new movie.");
}
private void bargainContract() {
System.out.println("Bargain the price of movie contract");
}
}
最后我们写一个王晶出来,王晶的main方法就是拍电影:
package com.designpattern.proxy;
public class Wang {
public static void main(String[] args){
IActor zhouProxy = new ZhouProxy(Zhou.getInstance());
zhouProxy.act();
}
}
好的,我们运行一下,看到输出的结果:
Bargain the price of movie contract
Make a schedule for the new movie.
Here comes Zhou's newest movie: Crazy biscuit!
Movie complete.
Movie's summary.
从代码中我们能看到,王晶其实只找到了代理人,但是经过调用代理人的act方法,我们仍能完成让周星驰拍电影的需求。这样下来,其实静态代理模式已经实现了。
动态代理
动态,就是在运行时能够根据实际情况进行判断并生成代理类的模式。它不需要具体定义某个类,只需要编写一些代理类需要做的事情即可。我们直接运用JDK中InvocationHandler来定义代理人,利用Proxy类来获得动态代理的实例。
我们先来看接口InvocationHandler,其实就定义了一个方法:
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
那么我们现在写一个使用动态代理的代理类ActorProxy,它实现了InvocationHandler:
package com.designpattern.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ActorProxy implements InvocationHandler {
private Object actor ;
public ActorProxy(Object actor){
super();
this.actor = actor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
bargainContract();
schedule();
method.invoke(actor, args);
complete();
summary();
return null;//如果method有返回,也可以返回其值
}
private void complete() {..}
private void summary() {..}
private void schedule() {..}
private void bargainContract() {..}
}
我们再重写Wang的main函数,调用这个类进行动态代理操作:
public class Wang {
public static void main(String[] args){
IActor actor = (IActor) Proxy.newProxyInstance(Zhou.getInstance().getClass().getClassLoader(), Zhou.getInstance().getClass().getInterfaces(), new ActorProxy(Zhou.getInstance()));
actor.act();
}
}
而结果和静态代理的一致。其中Proxy.newProxyInstance()方法,不用往里看,我们先看一下它的参数:
- ClassLoader loader:表示需要代理的类的加载器;
- Class>[] interfaces:表示需要代理的类实现的接口;
- InvocationHandler h:即代理类;
根据参数其实我们能够想到,代理类其实就是在相同的加载器loader中,加载一个实现了相同interfaces接口的代理类,并且该代理类的行为是在第三个参数h中定义的。
Cglib
不知道大家有没有想到一个问题,如果我想实现一个没有实现任何接口的类的代理类,或者说一个类虽然实现了某些接口,但是我希望针对类中非重载的方法进行代理,应该如何实现呢?其实这也是在Spring框架中需要解决的一个问题,而答案自然也在Spring框架中找。
使用Cglib库,需要注意以下几点:
- 需要引入cglib的jar文件,Spring的核心包(spring-core-x.x.x.jar)中已经包括了Cglib功能,当然你也可以只下Cglib的包;
- 代理的类不能为final,否则报错;
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;
这里想说一句,网上各种教程,全部都是在说OSCHINA的Gradle源,但是人家早就在2015年06月29日停止服务了。所以我在网上找到了阿里爸爸的Gradle库地址
repositories {
def REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public/'
all { ArtifactRepository repo ->
if(repo instanceof MavenArtifactRepository){
def url = repo.url.toString()
if (url.startsWith('https://repo1.maven.org/maven2') || url.startsWith('https://jcenter.bintray.com/')) {
project.logger.lifecycle "Repository ${repo.url} replaced by $REPOSITORY_URL."
remove repo
}
}
}
maven {
url REPOSITORY_URL
}
}
然后如果没有配置过Spring的话,可以直接下Cglib的包:
compile group: 'cglib', name: 'cglib', version: '3.2.2'
OK,准备工作已经完毕了,现在我们来定义一个没有实现接口的Zhou:
package com.designpattern.proxy;
public class OnlyZhou {
public void onlyAct(){
System.out.println("Only Zhou Action.");
}
public final void finalAct() {
System.out.println("This is a final act!");
}
}
而后,我们定义一个使用Cglib库的代理:
package com.designpattern.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ActorCglibProxy implements MethodInterceptor {
private ActorCglibProxy(){}
public static OnlyZhou getProxyInstance(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OnlyZhou.class);
enhancer.setCallback(new ActorCglibProxy());
return (OnlyZhou) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
bargainContract();
schedule();
proxy.invokeSuper(obj, args); //注意,此处不同!!!
complete();
summary();
return null;
}
private void complete() {..}
private void summary() {..}
private void schedule() {..}
private void bargainContract() {..}
}
然后我们来调用这个新的代理,先调用了一个正常的方法,而后调用了一个final的方法:
public class Wang {
public static void main(String[] args){
OnlyZhou onlyZhou = ActorCglibProxy.getProxyInstance();
onlyZhou.onlyAct();
onlyZhou.finalAct();
}
}
可以看到结果为:
Bargain the price of movie contract
Make a schedule for the new movie.
Only Zhou Action.
Movie complete.
Movie's summary.
This is a final act!
其中前五行是被截获(intercept)的方法输出的,而最后一行是出自一个final方法,上面说了final方法不会被截获,所以只输出了一行信息。