设计模式——代理模式

代理模式介绍

其实在我们生活中各行各业也都会设计到代理,比如租房子,我们可以找中介公司,让中介公司帮我们去找合适的房源,这就是代理模式,再比如,有些同学找男朋友女朋友也会通过中间人去介绍,这也是一种代理模式。在我们编程中,由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这样代理对象就能给目标对象提供保护。

代理模式特点

优点:
① 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
② 代理对象可以扩展目标对象的功能
③ 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
缺点:
① 增加系统复杂度
② 降低处理请求的速度

代理模式的结构

主角:
① 抽象主题类:就是需要提供一个接口或者抽象类给被代理对象去实现或者继承,里面提供被代理对象需要实现的一系列方法。

package proxy;

/**
 * 抽象主题类
 */
public interface Subject {
    void request();
}

② 真实主题类:需要被代理的对象,实现了抽象主题类中的具体业务方法。

package proxy;

/**
 * 真实主题
 */
public class SubjectImpl implements Subject{
    @Override
    public void request() {
        System.out.println("真实主题处理请求");
    }
}

③代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

package proxy;

/**
 * 代理类
 */
public class ProxySubject implements Subject{
    private Subject subject = new SubjectImpl();
    @Override
    public void request() {
        this.preRequest();
        subject.request();
        this.postRequest();
    }

    private void preRequest()
    {
        System.out.println("访问真实主题之前的功能扩展。");
    }
    private void postRequest()
    {
        System.out.println("访问真实主题之后的功能扩展。");
    }
}

测试:

package proxy;

public class start {
    public static void main(String[] args) {
        ProxySubject proxySubject = new ProxySubject();
        proxySubject.request();
    }
}

测试结果:

访问真实主题之前的预处理。
真实主题处理请求
访问真实主题之后的后续处理。

代理模式结构图:
结构图

动态代理

上面的代理模式比较死板,假如我们要增加被代理对象,那么也要增加响应的代理对象,有没有一种方式能否自动设置被代理对象以及需要被代理的方法呢?有,这种方式就是动态代理,动态代理典型的例子那就是SpringAOP。
在JDK中,Proxy类提供了一个静态方法———newProxyInstance,内容如下:

public static Object newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces,
   InvocationHandler invocationHandler) throws IllegalArgumentException

给予我们来生成一个代理对象(proxy),它有3个参数:
classLoader——类加载器;
interfaces——绑定的接口,也就是把代理对象绑定到哪些接口下,可以是多个;
invocationHandler ——绑定代理对象逻辑实现。
我们再来看看invocationHandle接口,他定义了一个invoke方法,这个方法就是实现代理对象的逻辑。

/**
* 处理代理对象方法逻辑
* @param proxy 代理对象
* @param method 当前方法
* @param args 运行参数
* @return 方法调用结果
*/
public Object invoke(Object proxy, Method method, Object[] args);

然后通过目标对象(target)、方法(method)和参数(args)就能够反射方法运行了。
很多项目中我们都有用到拦截器,我们就用动态代理实现一个拦截器的功能。

首先我们来定义一个需要被代理的对象以及它的接口。

package com.springboot.chapter4.service;
public interface HelloService {
   public void sayHello(String name);
}
package com.springboot.chapter4.service.impl;
import com.springboot.chapter4.service.HelloService;
public class HelloServiceImpl implements HelloService {
   @Override
   public void sayHello(String name) {
     if (name == null || name.trim() == "") {
       throw new RuntimeException ("parameter is null!!");
     }
     System.out.println("hello " + name);
   }
}

我们准备在sayHello前后通过拦截器扩展一些功能,扩展功能也是代理模式的特点之一,定义一个拦截器接口:

package com.springboot.chapter4.intercept;

import com.springboot.chapter4.invoke.Invocation;

import java.lang.reflect.InvocationTargetException;

public interface Interceptor {
    /**
     * 事前方法
     */
    boolean before();

    /**
     * 事后方法
     */
    void after();

    /**
     * 反射方法
     * @param invocation {@link Invocation}
     * @return
     */
    Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;

    /**
     * 是否返回方法,事件没有发生异常
     */
    void afterReturning();

    /**
     * 事件发生异常后执行
     */
    void afterThrowing();

    /**
     * 是否使用了around方法替代原方法
     */
    boolean useAround();
}

然后定义一个具体的拦截器,实现拦截器接口

package com.springboot.chapter4.intercept;

import com.springboot.chapter4.invoke.Invocation;

import java.lang.reflect.InvocationTargetException;

public class MyInterceptor implements Interceptor {

    /**
     * 事前方法
     */
    @Override
    public boolean before() {
        System.out.println("before ......");
        return true;
    }

    /**
     * 事后方法
     */
    @Override
    public void after() {
        System.out.println("after ......");
    }

    /**
     * 反射方法
     *
     * @param invocation {@link Invocation}
     * @return
     */
    @Override
    public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        System.out.println("around before ......");
        Object proceed = invocation.proceed();
        System.out.println("around after ......");
        return proceed;
    }

    /**
     * 是否返回方法,事件没有发生异常
     */
    @Override
    public void afterReturning() {
        System.out.println("afterReturning ......");
    }

    /**
     * 事件发生异常后执行
     */
    @Override
    public void afterThrowing() {
        System.out.println("afterThrowing ......");
    }

    /**
     * 是否使用了around方法替代原方法
     */
    @Override
    public boolean useAround() {
        return true;
    }
}

invocation源码:

package com.springboot.chapter4.invoke;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Invocation {
   private Object[] params;
   private Method method;
   private Object target;
   public Invocation(Object target, Method method, Object[] params) {
     this.target = target;
     this.method = method;
     this.params = params;
   }
   // 反射方法
   public Object proceed() throws
     InvocationTargetException, IllegalAccessException {
     return method.invoke(target, params);
   }
   /**** setter and getter ****/
}

注意invovation中的proceed方法,他会以反射的方式去调用原有的方法。
我们提供一个ProxyBean,它需要能够获取代理对象,以及处理代理对象方法逻辑。获取代理对象我们前面说到在JDK中,Proxy类提供了一个静态方法———newProxyInstance,处理代理对象方法逻辑我们可以实现invocationHandle接口。

package com.springboot.chapter4.invoke;

import com.springboot.chapter4.intercept.Interceptor;
import com.springboot.chapter4.intercept.MyInterceptor;
import com.springboot.chapter4.service.HelloService;
import com.springboot.chapter4.service.impl.HelloServiceImpl;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyBean implements  InvocationHandler{
    private Object target = null;
    private Interceptor interceptor = null;

    /**
     * 处理代理对象方法逻辑
     *
     * @param proxy  代理对象
     * @param method 当前方法
     * @param args   运行参数
     * @return 方法调用结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        //异常标识
        boolean exceptionFlag = false;
        Invocation invocation = new Invocation(target, method, args);
        Object retObject = null;
        try {
            if(this.interceptor.before()){
                retObject = this.interceptor.around(invocation);
            }else {
                retObject = method.invoke(target, args);
            }
        }catch (Exception e){
            exceptionFlag = true;
        }
        this.interceptor.after();
        if(exceptionFlag){
            this.interceptor.afterThrowing();
        }else{
            this.interceptor.afterReturning();
            return retObject;
        }
        return null;
    }

    /**
     * 绑定代理对象
     * @param target 被代理的对象
     * @param interceptor 拦截器
     * @return 代理对象
     */
    public static Object getProxyBean(Object target, Interceptor interceptor){
        ProxyBean proxyBean = new ProxyBean();
        //保存被代理对象
        proxyBean.target = target;
        //保存拦截器
        proxyBean.interceptor = interceptor;
        //生成代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxyBean);
    }

    public static  void testProxy(){
        HelloService helloService = new HelloServiceImpl();
        HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
        proxy.sayHello("ZhangSan");

        System.out.println("########################name is null!!");
        proxy.sayHello(null);
    }
}

测试类:

package com.springboot.chapter4.invoke;

import org.junit.jupiter.api.Test;

class ProxyBeanTest {

    @Test
    void testProxy() {
        ProxyBean.testProxy();
    }
}

运行结果:

before ......
around before ......
hello ZhangSan
around after ......
after ......
afterReturning ......
########################name is null!!
before ......
around before ......
after ......
afterThrowing ......

到这里我们成功用动态代理扩展了sayHello的功能。编程是门实践学科,通过自己动手会有更加深入的理解哦!
想了解更多设计模式的可以关注我,写的不好的地方请多多指教,共同进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

·码上修·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值