设计模式系列(三):代理模式,看这篇就够了~

什么是代理模式

代理模式是23种设计模式的一种,属于结构型模式(具体看设计模式的三大类型)。
代理模式的作用是,通过产生代理类,来对原有的类的方法做功能上的增强。我们不再直接调用原有的类的方法,而是通过代理类,同时,代理类还能在调用前后,做些其他的事情。

代理模式的分类

	1. 静态代理
	2. 动态代理	
		1.	JDK动态代理
		2.	CGLIB动态代理

静态代理

一个简单的例子:以曹操挟天子以令诸侯为例。
注:为了便于区分和理解,这里类名和方法都用中文来定义。

现在有一个皇上的接口,有处理朝政的功能。

public interface 皇上{

    void 处理朝政();
}

汉献帝 刘协 也就是曹操所挟持的天子,实现了皇上的接口。

public class 汉献帝 implements 皇上{
    @Override
    public void 处理朝政() {
        System.out.println("盖皇帝玉玺....");
    }
}

还有曹操,因为他也有处理朝政的功能,所以也实现了皇上接口。

public class 曹操 implements 皇上{

    private 汉献帝 liuxie;
    // 因为曹操可以支配汉献帝,所以直接在构造方法里,初始化汉献帝对象,方便曹操调用。
    public 曹操 (){
        liuxie = new 汉献帝();
    }
    @Override
    public void 处理朝政() {
        System.out.println("查看奏折内容");
        liuxie.处理朝政();
        System.out.println("检查奏折内容");
    }
}

现在我们来测试一下:

如果直接使用汉献帝调用:

       皇上 king = new 汉献帝();
       king.处理朝政();
    }

在这里插入图片描述
如果我们通过曹操类来调用呢?

	皇上 king = new 曹操();
   	king.处理朝政();

在这里插入图片描述
可以看到,我们通过曹操类,对汉献帝的处理朝政的方法进行了增强。

现在把曹操类的名字改一下:曹操 => 汉献帝代理

public class 汉献帝代理 implements 皇上{

    private 汉献帝 liuxie;
    public 汉献帝代理 (){
        liuxie = new 汉献帝();
    }
    @Override
    public void 处理朝政() {
        System.out.println("查看奏折内容");
        liuxie.处理朝政();
        System.out.println("检查奏折内容");
    }
}

测试:

	皇上 king = new 汉献帝代理();
   	king.处理朝政();

在这里插入图片描述
这就是静态代理。汉献帝代理 也就是代理类。他对汉献帝类的方法进行了扩展和增强。

动态代理

动态代理的特点就是代理类不再手动编写,而是在运行时通过反射动态生成。动态代理又分为两种实现方式。JDK动态代理 和CGLIB动态代理。

JDK动态代理

还以刚才的例子为例,动态代理,就不需要在手动编写曹操这个代理类和方法了。使用JDK动态代理,只需要编写代理类,实现InvocationHandler接口,实现invoke()方法。

public class 汉献帝代理 implements InvocationHandler {

    // 目标类,也就是被代理对象
    private Object target;

    public 汉献帝代理 (Object object) {
        this.target = object;
    }
	 /**
     * 该方法负责集中处理动态代理类上的所有方法调用。
     * 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
     *
     * @param proxy  代理类实例
     * @param method 被调用的目标方法
     * @param args   调用目标方法时 传入的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("---进入代理类---");
        System.out.println("当前代理的类是: " + target.getClass().getName());
        System.out.println("目标方法是: " + method.getName() + "()");
		
		// 执行目标方法
        Object result = method.invoke(target, args);

        System.out.println("---目标方法已执行---");
        return result;
    }
     // 获取代理类的 实例的方法
     public Object getProxyInstance(){

       return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

这里说一下InvocationHandler接口中的invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

该方法负责集中处理动态代理类上的所有方法调用,当我们通过代理类来调用目标方法时,会进入到该方法。了解一下方法的参数:

Object proxy:生成的代理类的实例
Method method:目标方法
Object[] args:调用目标方法时,传入的参数

在invoke()方法内部,可以通过method.invoke(target, args) 来执行真正的目标方法。

现在我们知道了,通过代理类调用目标方法时,会进入到上面的invoke()方法,那代理类又是怎么创建的呢?
这里就需要用到Proxy这个类。

Proxy 类 位于 java.lang.reflect 包下

在这个类中有一个newProxyInstance()方法,该方法用来创建代理对象的实例。

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

Proxy.newProxyInstance()方法的参数:

ClassLoader loade:指定当前目标对象使用的类加载器
Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把前执行目标对象的方法作为参数传入,也就是上面所编写的汉献帝代理类。

我们通过 Proxy.newProxyInstance(loader, interfaces, handler); 来产生代理对象的实例,使用目标对象所属的接口泛型接收。然后使用代理对象调用目标方法。就会触发上面的invoke()方法。

测试:

 汉献帝代理 proxy = new 汉献帝代理(new 汉献帝());
 King king= (King) proxy.getProxyInstance();
 king.处理朝政();

在这里插入图片描述
通过Proxy.newProxyInstance()方法可以看到,使用JDK动态代理,创建代理对象时,要传入目标对象实现的接口的类型。所以JDK动态代理针对的是实现了接口的类

CGLIB动态代理

如果一个类没有实现任何接口,又需要产生代理的话,就需要使用到CGLIB的动态代理。
实现方式:实现MethodInterceptor 接口,实现intercept()方法。
原理:通过“继承”的方式 可以继承父类所有的公开方法,然后重写这些方法,在重写时对方法进行功能上的增强。

public class 汉献帝代理 implements MethodInterceptor {

    private Object target;//需要代理的目标对象

    //重写拦截方法
    @Override
    public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
  		//方法执行,参数:target 目标对象 arr参数数组
       System.out.println("-----进入代理方法-----");
       Object invoke = method.invoke(target, arr);
       System.out.println("-----目标方法执行结束-----");
       return invoke;
    }

    //定义获取代理对象方法
    public Object getCglibProxy(Object objectTarget){
        //为目标对象target赋值
        this.target = objectTarget;
        Enhancer enhancer = new Enhancer();
        //设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(objectTarget.getClass());
        enhancer.setCallback(this);// 设置回调
        Object result = enhancer.create();//创建并返回代理对象
        return result;
    }
}

测试:

public static void main(String[] args) {
    汉献帝代理 cglib = new 汉献帝代理();//实例化CglibProxy对象
    汉献帝 king =  (汉献帝) cglib.getCglibProxy(new 汉献帝());//获取代理对象
    king.处理朝政();
}

在这里插入图片描述

JDK动态代理和CGLIB动态代理的区别

JDK动态代理

  1. 实现InvocationHandler接口,重写invoke() 方法。
  2. 针对实现了接口的类产生代理。如果目标类没有实现接口,那么不能使用JDK动态代理。

CGLIB动态代理

  1. 实现 MethodInterceptor 接口,重写 intercept() 方法。
  2. 针对没有实现接口的类产生代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值