什么是代理模式
代理模式是为其他对象提供一种代理以控制对这个对象的访问。
代理模式可以分为两种:
- 静态代理:代理类在编译器已经确定。
- 动态代理:代理类在JVM运行时动态生成。
静态代理相对动态代理来说,效率比较高,但是一旦修改静态代理的接口,代理类和委托类都需要做出修改,十分恼人。
原理图
注:该原理图是静态代理原理图。动态代理时,Proxy类无需实现Subject,若使用cglib进行代理,则Subject接口可以不存在,但代理类需要实现MethodInterceptor接口。
为什么要用代理模式
使用代理模式通常有以下四种情况:
-
远程代理,为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存放于不同地址空间的事实。
-
虚拟代理,根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
-
安全代理,用来控制真实对象访问时的权限。
-
智能指引,当调用真实的对象时,代理处理另外一些事。
在Spring Boot中所使用的AOP技术便是用了动态代理。
创业者的创业之路
凡事都要亲历亲为
一名创业者在创业初期凡事都亲历亲为,找了几个人组成了一个团队。
招聘接口:
public interface IRecruit {
void recruit();
}
创业者:
public class Entrepreneur implements IRecruit {
public void order() {
System.out.println("拉项目");
}
@Override
public void recruit() {
System.out.println("招聘");
}
}
公司:
public class company {
public static void main(String[] args) {
Entrepreneur entrepreneur = new Entrepreneur();
entrepreneur.order();
entrepreneur.recruit();
}
}
运行结果:
拉项目
招聘
小王做招聘——静态代理
渐渐地,团队发展成了一个小公司,事情愈发繁杂了起来。创业者一想,还是事事亲力亲为很可能会被很多无关紧要的小事占用太多时间,不行,得分出去一部分事情让别人做。
“这样吧,招聘新员工的事情让小王去做吧。”
从此,小王代理了创业者的招聘事项。
小王:
public class Wang implements IRecruit{
private Entrepreneur boss;
public Wang(Entrepreneur boss) {
this.boss = boss;
}
/**
* 代理boss的recruit,也会使用到boss的recruit,并不是不一样的重写。
* 小王的recruit是对boss的recruit的增强。
*/
@Override
public void recruit() {
System.out.println("筛选面试者");
boss.recruit();
System.out.println("发offer");
}
}
现在的公司:
public class company {
public static void main(String[] args) {
Entrepreneur entrepreneur = new Entrepreneur();
Wang wang = new Wang(entrepreneur);
entrepreneur.order();
wang.recruit();
}
}
结果:
拉项目
筛选面试者
招聘
发offer
现在的小王和创业者,便是一种静态代理的关系,小王和创业者实现了同一个招聘接口,都可以进行招聘,现在不需要创业者进行招聘了,小王代理了这件事,由于小王可以更加专注于招聘这件事,他也将招聘事宜进行了增强。
招聘秘书——动态代理
经过全体公司员工的不懈努力,现在的公司已经由一个名不见经传的小公司,发展成了一个中型企业。
创业者也将公司体系进行了改变,变得更加合理,层级更加明确,每件事都有对应的负责人。
但创业者毕竟是公司的老板,每天还是要面对很多的文件,单单自己一个人是很难办完的;公司里其他人的工作也不轻松,叫他们来给自己打下手未免有些不妥。
招聘一个秘书好了,专门在老板身边整理整理文件,做一些杂事。
这就出现了一个问题:一个人出来工作,并不是注定要给创业者做秘书,在应聘之前,甚至都不知道这创业者的详细信息,仅能通过公司公开的信息了解一二;甚至有的人来应聘,是从前一家公司跳槽来的,原来是给别人做秘书,代理的人并不是现在的创业者。
这样,我们的代码不能写成与小王类似的代码。
那么就需要用到动态代理了。
整理文件接口:
public interface IOrganize {
void organizeDocuments();
}
现在的创业者:
public class Entrepreneur implements IOrganize {
@Override
public void organizeDocuments() {
System.out.println("整理文件");
}
}
来应聘的秘书,都应当可以动态代理老板的工作:
秘书类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SecretaryFactory {
private Object o; //维护一个目标对象,由于实现不知道是什么类型,故用Object
public SecretaryFactory(Object o) {
this.o = o;
}
/**
* 使用JDK中的Proxy类生成一个代理对象,
* 用到的newProxyInstance方法的三个参数,分别是:
* 1.目标对象的类加载器
* 2.目标对象实现的接口类型
* 3.事件处理(执行目标对象方法是,会触发事情处理器的方法,把当前执行的目标对象方法作为参数传入)
*/
public Object getProxyInstance() {
Object instance = Proxy.newProxyInstance(o.getClass().getClassLoader(),
o.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("秘书开始代理");
Object res = method.invoke(o, args);
System.out.println("秘书代理完成");
return res;
}
});
return instance;
}
}
当前的公司:
public class company {
public static void main(String[] args) {
Entrepreneur entrepreneur = new Entrepreneur();
IOrganize secretary = (IOrganize) new SecretaryFactory(entrepreneur).getProxyInstance();//获取代理对象
secretary.organizeDocuments(); //通过代理对象调用目标对象的方法
}
}
运行结果:
秘书开始代理
整理文件
秘书代理完成
现在有一个人成功应聘了秘书的职位,那么这位秘书便知道了具体是给谁、做什么工作,随后便代理了原本就要交给秘书的部分的工作。
上面这种代码的是实现方式是使用了JDK的Proxy类,即通过JDK完成代理。
招聘秘书之前没有秘书要做什么的标准——用cglib动态代理
在上面的代码中,我们不让创业者实现IOrganize接口,再运行代码变化出现以下提示:
Exception in thread "main" java.lang.ClassCastException: class com.sun.proxy.$Proxy0 cannot be cast to class com.designpattern.proxy.IOrganize (com.sun.proxy.$Proxy0 and com.designpattern.proxy.IOrganize are in unnamed module of loader 'app')
at com.designpattern.proxy.company.main(company.java:11)
这时,招聘的秘书不再能完成这项工作,那招聘秘书的意义也就消失了。
有没有别的方法可以让秘书重新动态代理创业者的工作呢?
使用cglib便可以动态代理一个没有实现任何接口的类:
cglib所使用的jar包自取:(链接:https://pan.baidu.com/s/18uElgT9xXi1Fmi1SJOJITw 提取码:iuvt )
秘书类:
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 SecretaryFactory implements MethodInterceptor {
private Object o;
public SecretaryFactory(Object o) {
this.o = o;
}
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer(); //创建一个工具类
enhancer.setSuperclass(o.getClass()); //设置父类
enhancer.setCallback(this); //设置回调函数
return enhancer.create(); //创建子类对象(代理对象)
}
//重写intercept方法,调用目标对象的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("秘书使用cglib开始代理");
Object res = method.invoke(o, objects);
System.out.println("秘书使用cglib代理完毕");
return res;
}
}
当前公司:
public class company {
public static void main(String[] args) {
Entrepreneur entrepreneur = new Entrepreneur();
Entrepreneur secretary = (Entrepreneur) new SecretaryFactory(entrepreneur).getProxyInstance();//获取代理对象
secretary.organizeDocuments(); //通过代理对象调用目标对象的方法
}
}
运行结果
秘书使用cglib开始代理
整理文件
秘书使用cglib代理完毕
这样,即使创业者没有制定任何标准来做一件事,他的秘书也可以代理这项工作。