Proxy:为其他对象提供一个代理以控制对这个对象的访问
静态代理
想象一下我们在演《无间道》。阿仁是一个卧底警察(Undercover),他是真正能够搜集情报(getInfo)的人,他的上司黄Sir(Sir)负责直接与阿仁交接情报,他们都是一线警察(FrontlinePolice)。我们的警局老大梁警官(BOSS)想要知道罪犯的情报,但是他不能联系阿仁,只能通过黄Sir拿到情报。同时梁警官想要拿到的情报可以直接作为证据来起诉犯罪集团,整理情报这个任务就交给了黄Sir。
现在我们整理一下这部戏里的角色:一切的关键就是情报,而这个任务只有一线警察能够完成。所以我们把FrontlinePolice抽取出来作为一个接口。FrontlinePolice就是代理模式中的抽象角色(可以是接口,也可以是抽象类)
public interface FrontlinePolice {
void getInfo();//获取情报
}
阿仁,一个Undercover,同时他也能够完成FrontlinePolice的任务。Undercover就是代理模式中的真实角色/被代理角色
public class Undercover implements FrontlinePolice {
private String name;
public Undercover(String name) {
this.name = name;
}
@Override
public void getInfo() {
System.out.println(this.name + "搜集到了情报");
}
}
黄Sir,对应阿仁的代理角色,梁警官要情报只能命令黄Sir搜集情报。同时黄Sir也可以做一些整理情报之类的其他工作。
当然这里也可以定义set方法来传入属性。
public class Sir implements FrontlinePolice {
private Undercover undercover;
private String name;
public Sir(Undercover undercover, String name) {
this.undercover = undercover;
this.name = name;
}
@Override
public void getInfo() {
undercover.getInfo();
System.out.println(name + "整理好了情报");//整理情报
}
}
梁警官,使用黄Sir这个代理角色的人,对应代理模式中的客户角色。
public class BOSS {
public static void main(String[] args) {
Undercover undercover = new Undercover("阿仁");
Sir sir = new Sir(undercover,"黄Sir");
sir.getInfo();
}
}
输出结果
可以看到搜集情报的是阿仁,而黄Sir在这基础上整理了情报(当然也可以做一些其他的事情)
以上就是静态代理,跟装饰者模式有些类似,但是装饰者模式在于增强对象本身的一些功能,而代理模式在于代理对象调用真实对象的方法,同时在代理对象中增加一些功能。
动态代理
上面我们实现的静态代理虽说可以实现一些功能的增加以及访问的控制,但是一个致命的问题出现了:代码量翻倍。每一个真实对象都需要一个代理对象来代理他,那我们的项目中有不计其数的对象需要代理呢?总不能有1W个需要代理的对象就创建1W个代理类吧。所以动态代理就来了。
Java实现动态代理共有三种方式:
- 基于JDK(反射机制)实现,生成代理类比较快,但是调用方法比较慢
- 基于cglib实现,生成代理类比较慢,但是调用方法比较快
- 基于javassist(不太会,就不写了)
SpringAOP就是使用JDK和cglib实现的,当被代理的类实现了接口的时候就用JDK,否则用cglib。
基于JDK实现
这里我们只需要改变“黄Sir”这个代理类,把它换成使用更广泛的代理类InvocationHandlerImpl。
public class InvocationHandlerImpl implements InvocationHandler {
private Object object; //用来接受真实对象的属性
public InvocationHandlerImpl(Object object) {
this.object = object;
}
/**
*向外提供一个获取代理实例的方法
*newProxyInstance的三个参数:1被代理类的classloader,2被代理类实现的接口,3:h(InvocationHandler),调用代理类的方法时会转到调用传入的InvocationHandler的invoke()方法
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用之前");
Object result = method.invoke(object,args);
System.out.println("方法调用之后");
return result;
}
}
然后再我们使用的时候需要调用getProxy方法拿到代理对象,然后使用接口类型来接收
public class BOSS {
public static void main(String[] args) {
Undercover undercover = new Undercover("阿仁");
FrontlinePolice proxy = new InvocationHandlerImpl(undercover).getProxy();
proxy.getInfo();
}
}
输出结果
基于cglib实现
上面的JDK反射方式已经实现了我们想要的一个代理类代理所有真实对象,但是有一个条件限制:那就是被代理的对象需要实现接口,因为反射生成的代理对象是继承了java.lang.reflect.Proxy类的,碍于java的单继承原则,代理对象想要具有和被代理对象相同的方法就只能通过实现接口的方式。所以当我们的被代理对象并没有实现任何接口,而我们又希望使用代理模式的时候,cglib的实现方式就派上用场了。
cglib作为第三方库,使用时需导入cglib的包。这里我用的maven项目,直接添加依赖即可。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
当然我们不再需要FrontlinePolice接口,Undercover也不实现任何接口。这里我们的Undercover里定义了两个方法(后面用来筛选),如果类中定义了其他构造函数的话,则需要显式的定义一个无参的构造函数(否则会报错java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
)。因为cglib创建代理类的时候会调用父类的无参构造函数。
public class Undercover {
private String name;
public void setName(String name){
this.name = name;
}
public void getInfo() {
System.out.println(name + "搜集到了情报");
}
public void getName() {
System.out.println("我的名字是"+name);
}
}
然后我们需要两个实现了MethodInterceptor接口的类MethodInterceptorImpl1(进行增强的拦截器)和MethodInterceptorImpl2(什么都不做的拦截器)。
public class MethodInterceptorImpl1 implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o,objects);
System.out.println("整理了情报");
return result;
}
}
public class MethodInterceptorImpl2 implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o,objects);
return result;
}
}
intercept方法的注释和各项参数如下
{
/**
* All generated proxied methods call this method instead of the original method.
* 所有生成的代理方法都会调用这个方法而不是调用原始方法
* The original method may either be invoked by normal reflection using the Method object, or by using the MethodProxy (faster).
* 原始方法可以使用Method类的反射调用,也可以通过更快的MethodProxy调用
* @param obj "this", the enhanced object
* 被增强的对象
* @param method intercepted Method
* 被拦截的方法
* @param args argument array; primitive types are wrapped
* 参数数组,如果是原始类型则被包装
* @param proxy used to invoke super (non-intercepted method); may be called as many times as needed
* 用来调用父类方法(非拦截方法),可以多次调用
*/
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
MethodProxy proxy) throws Throwable;
}
我们需要定义一个CallbackFilter,用来设置方法拦截规则。这里只对setName()方法进行增强。
public class CallbackFilterImpl implements CallbackFilter {
public int accept(Method method) {
if ("setName".equals(method.getName())){
return 1;
}else{
return 0;
}
//返回的int类型用来标识callback[]中的下标(后面会用到)
}
}
使用:定义Enhancer,设置enhancer的回调数组,回调筛选规则,父类的class等。
public class BOSS {
public static void main(String[] args) {
Undercover undercover = new Undercover();
Enhancer enhancer = new Enhancer();
CallbackFilterImpl filter = new CallbackFilterImpl();
//设置回调数组,这里我只定义了两个
enhancer.setCallbacks(new Callback[]{new MethodInterceptorImpl1(),new MethodInterceptorImpl2()});
//设置回调筛选filter
enhancer.setCallbackFilter(filter);
//设置父类的类型,也就是代理类应该继承哪个类
enhancer.setSuperclass(undercover.getClass());
//通过create()方法生成代理类
Undercover proxy = (Undercover) enhancer.create();
proxy.setName("阿仁");
proxy.getInfo();
}
}
结果如下:
可以看到我们只对getInfo()方法进行了增强,而没有对setName()方法增强。