设计模式专家

Spring设计模式-代理模式

2018-03-11  本文已影响123人  Terminalist
代理模式:

代理对象处于访问者和被访问者之间,可以隔离这两者之间的直接交互,访问者与代理对象打交道就好像在跟被访者者打交道一样,因为代理者通常几乎会拥有全部被代理者的职能,代理对象能够处理的访问请求就不必要劳烦被访问者来处理了,从这个角度看,代理对象可以减少被访问者的负担,另外,即使代理对象最终要将请求转发给真正的被访问者,它也可以在转发访问请求之前或者之后加入特定的逻辑.比如安全限制,或者做一些中间操作(类似于我爱我家一样从中间收取手续费😄)。

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更好),或者没有实现接口的类进行扩展,关于这块内容的实现,请大家移步动态代理的本质进行阅读学习!

上一篇下一篇

猜你喜欢

热点阅读