你的一键三连是我巨大的动力!
设计模式系列
一、什么是代理模式?
其实代理模式不仅仅只存在编程中,你细想人生处处不代理呢?
-
一家公司老板身边常常都会有美女秘书(不要想太多),作为老板当然不可能事无巨细,于是就有了秘书代理分担事务(安排行程,会议等等)
-
一个明星同样由经纪人代理(约参演电影、电视剧、接广告等等)
-
小到个人,你只管上班,
单身的你要相亲,有婚介所(有七大姑八大姨),想找房有房产中介等等
上面的例子中,可以发现, 代理模式的本质就是将非核心事务转交给第三方处理。
这样的好处有什么?
公司有美女秘书,老板会更有精力干活,什么鬼?
当然不是啦!
老板只需要关注怎么让公司更好发展,获得更多钞能力,至于行程是坐车还是订航班,要告知跟哪个部门开会等非核心事务都给美女秘书安排。秘书是代理,实际决定公司怎么运作的还是老板!
去找明星合作也是如此,先通过经纪人,再由经纪人告知明星,最后才真正由明星处理。代理是经纪人,明星才是真正去赚钱的。
回到个人本身,你只需关注相亲对象是否是你喜欢的,房子又是否决定要买。至于和谁相亲,有什么样的房子,这些非核心关注的事务统统都转交给婚介和房产中介安排。实际体验相亲的还是我,最终买房的也还是我,婚介还是房产中介就是代理。
似乎扯得有些远…
回到编程本身就是
为其他对象提供一种代理以控制对这个对象的访问,简单来说就是 通过代理对象去访问我们的目标对象。
其本质还是一样一样的,目标对象 就是我们的老板、明星;代理对象 就是秘书、经纪人。
目标对象只做核心的事情,非核心事情还是一样外包给代理对象。所以想要你想要拜访老板还是接触明星,当然是需要经过秘书、经纪人的。
老板、明星忙着挣钱呢,哪有空!有事找秘书、经纪人。
所以代理模式定义也是这样的,只能通过代理对象去访问我们的目标对象。
知道是什么还不行,还得知道怎么实现,走!
二、实现静态代理和动态代理
代码在具体的实现上,跟我们上面所有呈现的例子是一样一样的!
静态代理还是动态代理都只是实现的形式,它们的意图(也就是目的)也是一样一样的!
-
高内聚低耦合:核心代码专注业务本身,把非业务代码通通交给代理实现
-
增加扩展性:扩展其他功能时不影响核心业务代码
下面得例子以明星类,作为核心业务代码(专注能红能赚钞能力)经纪人作为代理类负责不修改核心业务扩展功能(把明星安排得明明白白)
2.1 静态代理
定义一个明星的接口,开始他的表演
/**
* 定义一个明星的接口
*/
public interface IStarDao {
/**
* 明星的表演
*/
void action();
}
实现明星的行为
/**
* 定义明星用十八般武艺
*/
public class StarDao implements IStarDao {
@Override
public void action() {
System.out.println("一颗剽悍的种子拍广告");
}
}
静态代理的关键代码
定义经纪人代理类,由经纪人处理代理各种事务,明星专心干活,挣钱。
/**
* 经纪人代理
*/
public class AgentProxy implements IStarDao {
/**
* 由明星行动
*/
private IStarDao action;
public AgentProxy(IStarDao target) {
this.action = target;
}
@Override
public void action() {
System.out.println("==========广告商联系经纪人接广告==========");
//表演行动开始::代理人是经纪人,但实际赚钱的是明星
perform.action();
System.out.println("==========经纪人数钱==========");
}
}
开始跑起来
public class Client {
public static void main(String[] args) {
//要红要挣钱的明星
StarDao starDao = new StarDao();
//帮明星代理的经纪人
AgentProxy agentProxy = new AgentProxy(starDao);
//帮明星代理的经纪人,让明星开始他的表演
agentProxy.action();
}
}
运行结果
没想到一颗剽悍的种子在代码的世界出道了…
2.3 静态代理的优劣
静态代理的优势很明显:
拓展新功能时能不修改我们核心代码。 (这也是代理模式的初衷)
但是劣势同样明显:
代理对象和目标对象类(核心业务代码)需要实现一样的接口。 会导致代理类会非常非常多。
一旦经纪人需要代理其他明星就需要再定义接口,现实就不知道经纪人能否代理多个明星,但是你懂所表达的意思就行哈。
因此出现了动态代理。
2.4 JDK动态代理
动态代理分别有JDK代理和cglib代理。
先说JDK代理,JDK代理实现是利用lang包下的reflect
java.lang.reflect
同样定义一个明星的接口,开始他的表演
public interface IStarDao {
void action();
}
实现明星的行为
public class StarDao implements IStarDao {
@Override
public void action() {
System.out.println("一颗剽悍的种子拍广告");
}
}
JDK动态代理的关键代码
代理类无需再和目标对象类实现同样的接口,更加的灵活。(可以代理多个明星也不麻烦了,嘿嘿)
代理实例的三个参数:
-
ClassLoader loader : 指定当前目标对象使用的类加载器
-
Class<?>[] interfaces : 目标对象实现的接口类型
-
InvocationHandler : 事情处理,执行目标对象的方法时,会触发事情处理方法,把当前执行的目标对象方法作为参数传入
public class AgentProxyFactory {
private Object target;
public AgentProxyFactory(Object target){
this.target = target;
}
/**
* 给目标对象 生成一个代理对象
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//反射机制调用目标对象的方法
System.out.println("==========广告商联系经纪人接广告==========");
Object returnValue = method.invoke(target,args);
System.out.println("==========经纪人数钱==========");
return returnValue;
}
});
}
}
可以发现代理类不需要继承接口,但需要在真正实例化时将目标对象给代理对象既可。
public class Client {
public static void main(String[] args) {
//创建目标对象
StarDao iStarDao = new StarDao();
//给目标对象,创建代理对象,可以转成Dao
IStarDao proxyInstance = (IStarDao) new AgentProxyFactory(iStarDao).getProxyInstance();
//通过代理对象,调用目标对象
proxyInstance.action();
}
}
2.5 JDK动态代理的优劣
优势还是一样在扩展功能时不修改核心代码,相比静态代理,JDK动态代理类不需要和目标对象类(核心业务代码)一样都需要实现同样的接口,让代理类更加灵活。
劣势是目标对象依然需要实现接口。
2.5 cglib动态代理
cglib代理也称 子类代理,它是从内存中构建出一个子类来扩展目标对象的功能。
同时CGLIB是一个强大且高性能的代码生成包,可以在运行期扩展Java类与实现Java接口。
cglib跟前面两种很不一样。需要先导入cglib的jar包。
cglib包的下载地址,已经放在微信公众号“一颗剽悍的种子”,回复cglib就可以下载cglib的jar包。
同样定义目标类,但是不同的不需要再定义接口,直接实现。
public class StarDao {
public void action() {
System.out.println("一颗剽悍的种子拍广告");
}
}
代理类AgentProxyFactory 实现 MethodInterceptor 接口用来调用目标类,实现动态代理
public class AgentProxyFactory implements MethodInterceptor {
/**
* 维护一个目标对象
*/
private Object targer;
public AgentProxyFactory(Object targer) {
this.targer = targer;
}
/**
* 返回一个代理对象,是target 对象的代理对象
*/
public Object getProxyInstance(){
//1.创建一个工具类
Enhancer enhancer = new Enhancer();
//2.设置父类
enhancer.setSuperclass(targer.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类对象,即代理对象
return enhancer.create();
}
/**
* 调用目标对象
* @param o
* @param method
* @param args
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("==========广告商联系经纪人接广告==========");
Object returnValue = method.invoke(targer, args);
System.out.println("==========经纪人数钱==========");
return returnValue;
}
}
cglib动态代理跑起来跟JDK动态代理跑一样。
public class Client {
public static void main(String[] args) {
//创建目标对象
StarDao target = new StarDao();
//获取到代理对象,并且将目标对象传给代理对象
StarDao proxyInstance = (StarDao) new AgentProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxyInstance.sing();
}
}
cglib动态代理,优势相比于静态代理和动态代理,可以说不用像后两者一样去实现同一个接口。让代理模式真正灵动起来。劣势,可能就是需要多导入jar包了,更加方便灵活的同时也是有代价的。
三、代理模式的应用场景
用Spring的同学一定对其两大特性不陌生,一个是IOC,另一个是AOP。面向切面编程AOP的底层其实就是动态代理来实现的。
最后
我是一颗剽悍的种子 把我会的,认真的分享 是我写博客一直不变的信条
用有限的生命去学无限的知识注定是失败,所以我会用有限的时间,尽可能输出最有价值的知识谢谢能遇见可爱又一键三连的你。