Spring设计模式-代理模式
代理模式:
代理对象处于访问者和被访问者之间,可以隔离这两者之间的直接交互,访问者与代理对象打交道就好像在跟被访者者打交道一样,因为代理者通常几乎会拥有全部被代理者的职能,代理对象能够处理的访问请求就不必要劳烦被访问者来处理了,从这个角度看,代理对象可以减少被访问者的负担,另外,即使代理对象最终要将请求转发给真正的被访问者,它也可以在转发访问请求之前或者之后加入特定的逻辑.比如安全限制,或者做一些中间操作(类似于我爱我家一样从中间收取手续费😄)。
1.静态代理
其实说白了就是为其它的对象提供一种中间代理,以控制对被访问者的访问,让我们先看下类图,结合类图,然后我们用代码走一遍.
代理模式相关类关系图.png
1.首先我们需要定义一个被访问的对象接口,该接口是对被访问者或者被访问资源的抽取,也就是我们图中的Isubject,:
public interface ISubject {
String request();
}
2.定义一个被访问者或者被访问资源的具体实现类,也就是图中的SubjectImpl。如果你租过房子,找过中介,你应该就能明白,这个对象其实就是房主本人。
@Slf4j
public class SubjectImpl implements ISubject {
@Override
public String request() {
log.info("target subject request method...");
return "ok";
}
}
3.定义一个被访问者或者被访问资源之间的代理实现类,在租房的过程中它其实就充当我爱我家的作用,你其实也可以理解为它充当的就是目标对象的影子,但是这个影子拥有比目标对象更多的功能,它内部需要注入一个被访问者的具体实例,其实这步操作就相当于在我爱我家那边需要挂上房主本人的信息;
@Data
@Slf4j
public class SubjectProxy implements ISubject {
/**
* 需持有一个被访问者的具体实例:就类似于房东在我爱我家挂上了自己的房子一样,这个就是该房东的相关信息;
*/
private ISubject subject;
public SubjectProxy(ISubject iSubject) {
this.subject = iSubject;
}
@Override
public String request() {
log.info("SubjectProxy request");
return " proxy " + subject.request();
}
}
就这样,一个简单的静态代理模式就ok了,接下来,我们测试下,
public class Test {
public static void main(String[] args) {
ISubject iSubject = new SubjectImpl();
SubjectProxy subjectProxy = new SubjectProxy(iSubject);
System.out.println(subjectProxy.request());
}
}
输出结果为:proxy ok
然而,在实际开发中,我们的需求在变,某天我们在此基础上引入更复杂的业务,这时,依旧采用原思路走,看代码:
4.定义一个代理SubjectProxy 的对象,同样的我们可以这么理解,我爱我家又拖了一家卖房中介卖房主的房子,而这个卖房中介肯定要收更多的钱,那么收钱就是新增的业务了;
@Data
@Slf4j
public class ServiceSubjectProxy implements ISubject {
private ISubject iSubject;
public ServiceSubjectProxy(ISubject iSubject) {
this.iSubject = iSubject;
}
@Override
public String request() {
log.info("ServiceSubjectProxy request");
return "serviceSubject " + this.iSubject.request();
}
}
此时,测试demo怎么写嘞,还是一样的嘞,请看代码!
public class Test {
public static void main(String[] args) {
ISubject iSubject = new SubjectImpl();
SubjectProxy subjectProxy = new SubjectProxy(iSubject);
ServiceSubjectProxy serviceSubjectProxy = new ServiceSubjectProxy(subjectProxy);
System.out.println(serviceSubjectProxy.request());
}
}
输出为:serviceSubject proxy ok
到此刻,有木有感觉很简单,简单就对了,我的目的就是为了通俗易懂,然后将对此的理解应用在实际中!
2.动态代理
以上是对Isubject的request方法做的代理,但如果有那么一刻,我们的代码中有很多接口中都有这个request方法,此时我们想统一处理,我么该怎么做?我们不可能新写一套跟上面差不多的逻辑,这会玩死人的。这时我们就要用动态代理了。
动态代理:为指定的接口在系统运行期间动态的生成代理对象(通过jdk编译器运行期间动态织入),动态代理主要由jdk的一个InvocationHandler接口和一个Proxy类,有兴趣的可以看看这两个类的实现.接下来,就让我们针对Isubject提供代理对象;
@Slf4j
public class ISubjectInvocationHandler implements InvocationHandler {
private Object obj;
public ISubjectInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(obj, args);
log.info("invoke :{}", object);
return object;
}
}
测试案例:
public class Test {
public static void main(String[] args) {
ISubject iSubject = (ISubject) Proxy.newProxyInstance(Test.class.getClassLoader(),
new Class[]{ISubject.class},
new ISubjectInvocationHandler(new SubjectImpl()));
iSubject.request();
}
}
运行结果:ok
即使再出现更多的对象,只要他们织入的横切的逻辑是一样的,那么她们都可被注入ISubjectInvocationHandler,通过Proxy为其生成相应的动态类,当Proxy动态生成的代理对象被调用时,对于的InvocationHandler就会去拦截相应方法的调用,InvocationHandler是Spring aop中实现横切逻辑的载体。
动态代理虽好,但其依赖固定的接口定义,Spring还提供了一种Cglib的动态字节码生成技术,为没有实现接口的目标对象生成动态代理的实例;
cglib是怎么回事嘞?听我道来:一般来说,如果一个类没有接口定义,我们可以通过继承来覆盖父类的方法,从而达到一种将横切的逻辑在子类中织入,然后让系统使用子类即可,这样就达到了一种和代理模式一样的效果了。但是,请注意,此时此刻我们用的是动态代理,而不是静态代理那样为每个子类创建一个扩展子类,因此我们需要借助像cglib这样的动态字节码生成库,在代码运行期间,动态的为目标对象生成相应的扩展子类;cglib可以对实现了某种接口的类(这个使用InvocationHandler更好),或者没有实现接口的类进行扩展,关于这块内容的实现,请大家移步动态代理的本质进行阅读学习!