代理模式介绍
概念
代理
(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。代理对象一般称为代理方
,目标对象一般称为被代理方
,后文也会这样称呼。
实际的栗子
我们可以想象一个场景:在我们买二手房的时候,可能并不是直接去联系房屋卖家
,直接给房屋卖家
钱,而是通过一个中间商
,(我爱我家
)这样的机构购买,但是实际上我们给钱最终还是给的卖家
。这样抽象出来也就是代理模式的作用,通过代理对象(我爱我家)访问目标对象(真实卖家)
好处
那么这样做有什么好处呢?我也可以直接去访问卖家呀?这其实就是涉及到AOP的知识了(Spring内容,面向切片),实现我们基本业务逻辑和其他处理的分离,降低代码的耦合性。同时也能够在基础功能上,不改变源代码的前提下增加新的功能。
代理模式分类
一、静态代理
静态代理顾名思义,是静态的代码,也就是我们在编译的时候就已经确定了的,这样虽然能够实现我们代理模式,但是失去代码的灵活性,不能满足我们动态编程的需求。下面来看代码。
定义静态代理模式的步骤
一、创建一个代理接口,也就是被代理方所需要实现的最基本的功能。
interface Object1{
void action();
}
复制代码
二、创建一个被代理类,实现代理接口,重写接口中的方法。
//被代理类
class ObjectImpl implements Object1{
@Override
public void action() {
System.out.println("===========被代理类开始执行============");
System.out.println("==============具体操作==================");
System.out.println("==============被代理类执行完毕==================");
}
}
复制代码
三、创建一个对应的代理类,同样实现代理接口,重写接口中的方法,在类中定义一个Object类,用来获取到被代理类的对象,然后执行被代理对象的方法
+自己另外的功能方法
...
//代理类
class ProxyObject implements Object1{
Object1 obj;
public ProxyObject() {
System.out.println("代理类创建成功");
obj = new ObjectImpl();
}
@Override
public void action() {
System.out.println("代理类开始执行");
obj.action();
}
}
复制代码
其实这时候我们也发现一个问题,那就是代码很不灵活
,我们需要在编译前就想好哪写类需要用到代理模式并且给它们写好对应的代理类
,这样未免也太麻烦了! 那么这时候基于反射原理
的动态代理
就有用武之地!
二、动态代理
前面说到,动态代理基于的是java反射原理,那么具体怎么用到的呢? 其实就是我们在原来被代理类和代理类一对一
的关系把它简化成了被代理类和代理类多对一
的关系,我们只需要创建一个动态代理类,实现InvocationHandler接口,然后通过动态获取传入的被代理的对象,并动态执行被代理对象
的方法。这样听起来好像有点绕,那么我们根据代码来具体看一看:
定义动态代理模式的步骤
一、创建代理接口,也就是被代理方所需要实现的最基本的功能(这里为了体现动态代理的功能所以定义了两个不同的代理)
//动态代理的接口1
interface Subject{
void action();
}
//动态代理的接口2
interface ClothFactory{
void test();
}
复制代码
二、创建被代理类,实现代理接口,重写接口中的方法。
//被代理类1
class RealSubject implements Subject{
@Override
public void action() {
System.out.println("我是被代理类!!!!!!!");
}
}
//被代理类2
class NikeclothFactory implements ClothFactory{
@Override
public void test() {
System.out.println("Nike工厂正在加工衣服..........");
}
}
复制代码
三、创建动态代理类
class MyInvocationHandler implements InvocationHandler{
Object obj;//实现了接口的被代理类的对象的声明
//1、给被代理的对象实例化 2、返回一个被代理类的对象
public Object blind(Object obj){
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this);
}
//当通过代理类的对象发起对被重写的方法的调用时,会自动转化成对如下的invoke方法的调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method方法的返回值是returnVal
Object returnVal = method.invoke(obj,args);
return returnVal;
}
}
复制代码
因为这部分是动态代理的关键所以对代码进行解释;
在定义动态代理类的时候分几个步骤:
1、定义一个实现了接口的被代理类的对象的声明,后面用于接收被代理类的对象。
2、定义blind方法,用于给被代理的对象实例化,并通过反射,返回一个被代理的对象。通过Proxy.newProxyInstance这个静态方法,分别传入三个值:
①被代理对象的类加载器②被调用对象实现的接口③实现了InvocationHandler
其实也比较好理解,首先代理对象和被代理对象的类加载器是同一个,再加上被调用对象实现的接口,自然能够通过反射得到代理对象。
3、在实现InvocationHandler
接口的时候我们被要求实现一个invoke
方法,这也是核心,当通过代理类的对象发起对被重写的方法(接口中定义的方法)的调用时,会自动转化成对invoke 的调用。让我们看看invoke的参数列表的后面两个,method
和args
,是不是特别眼熟!这不就是通过反射获取方法的参数么(不清楚的同学可以看看我的上一章讲反射),那么其实就是通过反射,实现对被代理类的对象的方法调用,返回一个Object类型作为返回值的承担者。
四、主函数调用测试
public class DongTai {
public static void main(String[] args) {
//1、创建被代理类的对象
RealSubject real = new RealSubject();
//2、创建一个实现了InvocationHandler接口的对象
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
//3、调用blind方法,动态的返回一个根据传入的real对象所实现的接口的代理类对象
Subject subject = (Subject) myInvocationHandler.blind(real); //此时的sub就是代理类的对象
subject.action(); //转到对InvacationHandler接口的实现类的invoke()方法的调用
//举例2:
NikeclothFactory nike = new NikeclothFactory();
ClothFactory factory = (ClothFactory) myInvocationHandler.blind(nike);
factory.test();
}
}
复制代码
三、Cglib代理
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理,这部分内容我后面会再来补充。
写在最后
到这里为止,代理模式基本就讲完了,如果有小伙伴有什么问题可以私聊我,不过我相信学完反射的你能够理解其中的奥妙!!
作者:等一个offer养家糊口
链接:https://juejin.cn/post/7049748452200153118