侧边栏壁纸
  • 累计撰写 106 篇文章
  • 累计创建 3 个标签
  • 累计收到 19 条评论
标签搜索

目 录CONTENT

文章目录

设计模式-代理模式

卑微幻想家
2021-03-19 / 0 评论 / 0 点赞 / 334 阅读 / 5,280 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-03-24,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

代理模式定义

创建一个对象的代理,用以控制这个对象的访问

UML
从类图我们可以看出,委托类(RealSubject)与代理类(Proxy)都需要实现同一个接口,他们有相同的方法(具有相同的行为),代理类引用了一个委托类的实例,代理类并不具体实现这个方法,而是直接调用委托类的方法,从而达成对委托类的代理。

静态代理

Subject类

public interface Subject {
    void request();
}

RealSubject类

public class RealSubject implements Subject{

    @Override
    public void request() {
        System.out.println("request");
    }
}

Proxy类

public class Proxy implements Subject{

    private RealSubject realSubject = new RealSubject();

    @Override
    public void request() {
        System.out.println("增强功能");
        realSubject.request();
        System.out.println("增强功能");
    }
}

Client类

public class Client {

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

通过上面静态代理的例子可以看出:

  1. 代理类和委托类实现了相同的接口,会有大量的重复代码产生,如果接口增加一个方法,不仅委托类要实现这个方法,代理类也需要实现这个方法,这就增加了代码维护的复杂度。
  2. Proxy类只为RealSubject类提供了代理,如果需要代理其他类,那么就不能胜任了,需要再添加适用于其他类的代理类。

通过以上两点,可以看出静态代理的缺点,那么我们就需要动态代理来解决了。目前Java实现动态代理有两种方案,分别是JDK提供的实现方案和CGLIB,接下来我们分别了解这两种实现方案。

JDK动态代理

在上面的示例中,一个代理只能代理一种类型,而且是在编译期就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象

在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy 类的支持

public interface InvocationHandler {

    /**
     * proxy - 调用该方法的代理实例
     * method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
     * args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
public class Proxy implements java.io.Serializable {
	/**
     * loader - 类加载器来定义代理类
     * interfaces - 代理类实现的接口列表
     * h - 得到InvocationHandler接口的子类的实例
     */
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
}

现在我们用JDK提供的动态代理方案来实现最开始的例子

DynamicProxyHandler类

public class DynamicProxyHandler implements InvocationHandler {

    private Object targetObject;

    public Object newProxyInstance(Object targetObject){
        this.targetObject = targetObject;
        /*
        该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
        第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
        第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
        第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
         */
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

    /**
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("增强功能");
        Object invoke = method.invoke(targetObject, args);
        System.out.println("增强功能");
        return invoke;
    }
}

Client类

public class Client {
    public static void main(String[] args) {
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler();
        Subject subject = (Subject) dynamicProxyHandler.newProxyInstance(new RealSubject());
        subject.request();
    }
}

由此可以看到,通过动态代理,我们可以代理很多不同类型的对象,代理类是在程序运行过程中动态生成的,即使接口变化也不影响。

CGLIB动态代理

我们发现JDK实现动态代理,需要委托类实现一个接口,生成的代理类也会实现相同的接口,从而具有了和委托类相同的“行为”,而对象的行为其实也可以在类中进行定义,我们可以通过继承委托类,实现委托类的公共方法,从而对方法进行增强。这就是CGLIB的大致思想。

要使用CGLIB首先要引入依赖

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.2.5</version>
</dependency>

RealSubjectNoImplInterface类,与RealSubject相比,没有实现任何接口

public class RealSubjectNoImplInterface {
    public void request() {
        System.out.println("request");
    }
}

DynamicProxyInterceptor类

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DynamicProxyInterceptor implements MethodInterceptor {


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("增强功能");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("增强功能");
        return invoke;
    }
}

Client类

import net.sf.cglib.proxy.Enhancer;

public class Client {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubjectNoImplInterface.class);
        enhancer.setCallback(new DynamicProxyInterceptor());
        RealSubjectNoImplInterface subjectNoImplInterface = (RealSubjectNoImplInterface)enhancer.create();
        subjectNoImplInterface.request();
    }
}

区别

名称区别
静态代理动态代理的理论基础。
JDK动态代理需要有顶层接口才能使用(Java中类不能多继承),但是在只有顶层接口的时候也可以使用。使用反射完成。使用了动态生成字节码技术。
CGLIB动态代理可以直接代理类,使用字节码技术,不能对 final类进行代理(Java语法决定,final类不能有子类)。使用了动态生成字节码技术。
0

评论区