理解
用演员和经纪人来形容代理模式最恰当不过,一个演员每天要拍电影,拍广告,拍电视剧有很多事情要做,但是每天有很多人来找他谈合作,以及各种行程需要合理安排,如果这些全都要演员自己来弄肯定忙不过来,容易出错,所以就有了经纪人(代理人),这些代理人帮演员打理行程,应对合作商,演员只需要专心于演戏就好了。演员和经纪人之间其实就是代理模式。
作用
在我们写程序的时候也经常遇到,我们的业务代码负责业务流程的控制,但是除了业务流程有时候还有一些其他的事情,比如事务的管理,对象的创建之类的,这时就需要有一个代理类能帮业务代码处理这些工作,让业务代码专心于业务流程的处理,所以就需要代理模式了。
实现
代理模式分为静态代理和动态代理之分,静态代理指的是代理类通过硬编码来创建,比较简单,而动态代理指代理类在jvm运行过程中自动创建出来,需要使用java的代理包。
静态代理
定义接口:
public interface Work {
public void handWork(int giveMoney);
}
构建被代理者:
public class Worker implements Work {
@Override
public void handWork(int giveMoney) {
System.out.println("hard working");
}
}
构建代理人:
public class WorkerProxy implements Work {
private Worker worker;
public WorkerProxy() {
worker = new Worker();
}
@Override
public void handWork(int giveMoney) {
if (isAccept(giveMoney)) {
worker.handWork(giveMoney);
acceptMoney(giveMoney);
}
}
private void acceptMoney(int giveMoney) {
System.out.println("accept money:" + giveMoney);
}
private boolean isAccept(int giveMoney) {
if (giveMoney < 200) {
System.out.println("money is not enough");
return false;
}
return true;
}
}
测试:
public static void main(String[] args) {
Work work = new WorkerProxy();
work.handWork(100);
work.handWork(500);
}
输出结果:
money is not enough
hard working
accept money:500
被代理者只要专注于努力工作就好了,代理者来判断是否要做这件工作,以及做完工作之后收款。
动态代理
动态代理更为灵活,在spring的AOP,各类rpc框架中都有使用,动态代理不在需要硬编码构建方法代理类,所以前面静态代理中只需要保留接口类,和被代理类即可:
定义接口:
public interface Work {
public void handWork(int giveMoney);
}
构建被代理者:
public class Worker implements Work {
@Override
public void handWork(int giveMoney) {
System.out.println("hard working");
}
}
之后利用java的动态代理方法:java.lang.reflect.Proxy#newProxyInstance构架代理类:
public static void main(String[] args) {
Work work = new Worker();
Work proxy1 =(Work) Proxy.newProxyInstance(work.getClass().getClassLoader(),work.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(isAccept((int)args[0])){
method.invoke(work,args);
acceptMoney((int)args[0]);
}
return null;
}
private boolean isAccept(int giveMoney) {
if (giveMoney < 200) {
System.out.println("money is not enough");
return false;
}
return true;
}
private void acceptMoney(int giveMoney) {
System.out.println("accept money:" + giveMoney);
}
});
proxy1.handWork(1);
proxy1.handWork(300);
}
执行结果:
money is not enough
hard working
accept money:300
和静态代理完全一样,其中核心代码是这一样:
Work proxy1 =(Work) Proxy.newProxyInstance(work.getClass().getClassLoader(),work.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
newProxyInstance传入的三个参数分别为被代理者的类加载器,被代理者实现的接口,以及即将创建出来的代理者的工作接口InvocationHandler,而InvocationHandler接口中的invoke方法三个参数proxy为被创造出来的代理对象,method为代理了的那个方法,args为外部调用该方法时传入的参数。
如果只看上面的这个案例,我们可能感觉动态代理其实也没有什么优越性,无非就是把提前写好的静态类放到了需要执行时来动态创建,该写的代码一行也没少。但是正是这一特性成就了Spring的AOP,因为Spring框架是事先无法知道各种业务场景下需要什么样的代理类的的,所以无法通过提前编写静态类放到spring包中供大家使用,只有大家开发业务的时候,根据具体情况才知道到底需要什么样的代理类。
另一种情况就是当各种代理其实做的工作都一样时,动态代理就有了优越性,比如我们的事务处理,比如我们的rpc请求,其实各种代理做的事情都是一样的,但是如果在静态代理模式下,就算再简单你也必须对每一个接口写一个静态代理类,而动态代理就不需要了。
上面说到rpc中的静态代理,其实rpc的客户端根本就没有被代理对象,他们只有一个接口,在这种情况下,其实上面的代码就变成了如下:
public static void main(String[] args) {
Work proxy =(Work) Proxy.newProxyInstance(Work.class.getClassLoader(), new Class[]{Work.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("try to demand remote work to work hard……");
return null;
}
});
proxy.handWork(11);
}
可以看到代理类如果不需要最终去执行被代理者的方法的话,完全不需要有被代理者,自己就是一个被动态创造出来的工作类,这一特性成就了java的rpc框架。