代理模式及其应用

2022-06-07  本文已影响0人  文景大大

一、代理模式简介

代理模式旨在为服务类和客户类之间插入一个代理类,由代理类为客户类进行服务的代理访问。同时也可以对客户的行为进行增强和控制,但对客户类来说是透明的。现实中的例子有(租客、房屋中介、房子)、(结婚新人、婚庆公司、婚礼)、(业主、物业公司、小区管理)等,可以看到,客户都是通过中介代理来完成对目标资源的控制,同时中介又对资源的管理做了一定程度的增强和控制。

要想实现代理模式,必须要满足如下的要求:

默认情况下实现的代理模式都是静态代理,参见如下章节。

二、静态代理

public interface ISubject {
    void request();
}
@Slf4j
public class RealSubject implements ISubject{
    @Override
    public void request() {
        log.info("RealSubject!");
    }
}
@Slf4j
public class Proxy implements ISubject{
    private ISubject subject;

    public Proxy(ISubject subject){
        this.subject = subject;
    }

    @Override
    public void request() {
        // 代理类执行服务类职能前后可以进行增强和控制
        before();
        subject.request();
        after();
    }

    public void before(){
        log.info("Proxy before...");
    }

    public void after(){
        log.info("Proxy after...");
    }
}
public class Main {

    public static void main(String[] args) {
        Proxy proxy = new Proxy(new RealSubject());
        proxy.request();
    }

}

静态代理的特点就是一个服务类对应一个代理类,但是如果服务类比较多的时候,我们就必须创建很多代理类,显得很冗余,因此才有了动态代理。

三、动态代理

相比于静态代理,我们无需为每一个服务类创建一个代理类,可以依靠Java的反射机制,在程序运行期间,动态地为目标服务对象创建代理对象,因此简化了编程工作,提高了系统地可扩展性。

3.1 JKD动态代理

@Slf4j
public class JdkHandler implements InvocationHandler {
    /**
     * 需要代理的目标服务对象
     */
    private Object object;

    public JdkHandler(Object object){
        this.object = object;
    }

    /**
     * 获取目标服务对象的代理
     * @return
     */
    public Object getProxy(){
        Class<?> clazz = object.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    /**
     * 调用目标服务对象的某个方法,同时也可以对服务进行增强和控制
     * @param proxy 代理实例
     * @param method 目标服务对象的某个方法
     * @param args 目标服务对象的方法入参
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 增强行为
        before();
        // 调用目标服务对象的某个方法
        Object result = method.invoke(object, args);
        // 增强行为
        after();
        return result;
    }

    public void before(){
        log.info("代理增强行为 before...");
    }

    public void after(){
        log.info("代理增强行为 after...");
    }
}
// 动态代理jdk
public static void main(String[] args) {
    JdkHandler jdkHandler = new JdkHandler(new RealSubject());
    // 要求目标服务类必须有实现的接口
    ISubject subject = (ISubject)jdkHandler.getProxy();
    subject.request();
}

可以看到,我们的代理类JdkHandler必须要实现InvocationHandler接口,而其内部引用的目标服务对象则是通用的Object类型,而不是如上具体的ISubject,实现了解耦通用。当执行目标服务对象中的任意方法时,代理类JdkHandler中的invoke方法就会得到执行。

3.2 Cglib动态代理

@Slf4j
public class CglibInterceptor implements MethodInterceptor {
    /**
     * 需要代理的目标服务对象
     */
    private Object obj;

    public CglibInterceptor(Object obj){
        this.obj = obj;
    }

    /**
     * 获取目标服务对象的代理
     * @return
     */
    public Object getProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        // 设置拦截器,回调对象设置为自己
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 增强行为
        before();
        // 调用目标服务对象的某个方法
        Object result = methodProxy.invoke(obj, objects);
        // 增强行为
        after();
        return result;
    }

    public void before(){
        log.info("代理增强行为 before...");
    }

    public void after(){
        log.info("代理增强行为 after...");
    }
}
// 动态代理cglib
public static void main(String[] args) throws Exception {
    CglibInterceptor cglibInterceptor = new CglibInterceptor(new RealSubject());
    // 不要求目标服务类有实现的接口
    RealSubject subject = (RealSubject) cglibInterceptor.getProxy();
    subject.request();
}

如上示例使用的cglib在spring中自带的,因此不用单独引入cglib的maven包。总结下来,使用JDK动态代理和Cglib动态代理的区别如下:

四、应用案例

4.1 通用案例

4.2 具体案例

五、使用总结

5.1 优点

5.2 缺点

5.3 静态代理和动态代理的比较

上一篇下一篇

猜你喜欢

热点阅读