设计模式——拦截器

本文说明:在学习《Java EE互联网轻量级框架整合开发》此书时,里面提到了几种设计模式,我在学习这几种设计模式时写了笔记,放上来让大家共同学习,如果有错误,望指出。

本章由两部分组成:

1、基本概念+关键代码讲解

2、完整例子

基本概念讲解

拦截器模式是动态代理的方便写法,将想要插入的逻辑利用我们定义的拦截器接口抛给外部实现而已。所以就是在动态代理的基础上加上拦截器实现即可。我们以JDK动态代理实现拦截器为例。

拦截器模式主要包括2个接口4个类,拦截器接口,拦截器类(继承拦截器接口),目标接口,目标类(实现目标接口),JDK动态代理核心类(涵盖拦截器),测试类。

【拦截器接口和拦截器类】:将想插入的逻辑通过三个方法before、after、around实现;

【目标接口和目标类】:被插入逻辑的目标代码;

【JDK动态代理核心类】:利用拦截器实现核心插入逻辑;

【测试类】:测试逻辑是否实现。

下面主要讲讲【拦截器接口和拦截器类】和【JDK动态代理核心类】。

拦截器接口和拦截器类

拦截器接口我们定义含有三个方法,分别是before、after和around。有读者可能会奇怪before和after已经涵盖了在目标方法的前后插入逻辑的所有可能的地方,那么这个around是来干嘛的?around顾名思义,就是环绕在目标方法的周围,那么就相当于before+after。

好像很有道理,其实是错的。

around,其实是取代了目标方法原有的代码。它的前面可以有before,后面可以有after,是目标方法的替代品,而不是插入品。

拦截器接口我们定义如下:

package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.Method;
//含有4个参数,除了我们的invoke方法有3个参数之外,还有一个目标对象的参数。
public interface Interceptor {
    public boolean before(Object proxy, Object target, Method method, Object[] args);
    public void after(Object proxy, Object target, Method method, Object[] args);
    public void around(Object proxy, Object target, Method method, Object[] args);
}

细心的读者会注意到,before方法的返回值不是void而是boolean。什么作用?为了实现around。我们可以在before实现后,根据before的返回值来决定是否实现目标方法原有代码。我们可以定义为:返回true实现目标方法原有代码,返回false实现替代代码,在这里就是调用around方法。然后不管返回值为如何,都要实现after方法。至于实不实现after(其他同),由外部实现我们的接口决定。

我们可以这么实现拦截器接口:

public class InterceptorImpl implements Interceptor {
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法前逻辑");
        return true;
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法后逻辑");
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("取代了被代理对象的方法");
    }
}

JDK动态代理核心类

与JDK动态代理实现的一样,核心逻辑没有变,依然是实现bind和invoke方法,不同的是,得利用拦截器来写入插入逻辑,我们之前的invoke的逻辑是这么实现的:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("进入逻辑方法之前");
    System.out.println("在调度真实对象之前的服务");
    Object returnObj = method.invoke(target, args);
    System.out.println("在调度真实对象之后的服务");
    return returnObj;
}

可见插入前后的逻辑我们是直接用打印来验证。这里外部接口实现了拦截器,我们替换成拦截器的三个方法实现即可。代码如下:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if(interceptorClass == null) {
        return method.invoke(target, args);
    }
    Object returnObj = null;
    Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
    if(interceptor.before(proxy, target, method, args)) {
        returnObj = method.invoke(target, args);
    }else {
        interceptor.around(proxy, target, method, args);
    }
    interceptor.after(proxy, target, method, args);
    return returnObj;
}

另外还有一点要变动的地方:bind方法。bind方法我们之前是这么实现的:

public Object bind(Object target) {
    this.target = target;
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

这里由于加入了拦截器的实现,拦截器又是外部帮我们实现的,所以我们的bind方法除了需要传入target对象,还需要传入拦截器对象。我们可以用反射得到拦截器对象这样方便利用配置文件动态配置,所以我们只需要把拦截器对象传入一个拦截器类的字符串。我们可以这么写:

public Object bind(Object target, String interceptorClass) {
    this.target = target;
    this.interceptorClass = interceptorClass;
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

这样就实现了有拦截器的【JDK动态代理核心类】。

当然,这样我们每次要调用依然得先new一个【JDK动态代理核心类】,再调用它的bind方法得到返回的对象,需要两步。我们可以简单缩减成一步(前面的JDK动态代理也可以简单缩成一步),即将bind方法写成static的,然后调用【JDK动态代理核心类】.bind(target, interceptorClass)返回一个proxy对象,那么我们可以改写成:

public JdkProxyExample(Object target, String interceptorClass) {
    this.target = target;
    this.interceptorClass = interceptorClass;
}

public static Object bind(Object target, String interceptorClass) {
    return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxyExample(target, interceptorClass));
}

可以看到我们加了一个构造函数,然后直接把外部传入的两个对象通过new JdkProxyExample(target, interceptorClass)的形式传给newProxyInstance的第三个参数,并把bind改成static方法。

这样一来,在外部可以直接通过下面一行代码得到proxy对象:

HelloWorld proxy = (HelloWorld) JdkProxyExample.bind(new HelloWorldImpl(), interceptorClass);

完整代码

//包含2个接口4个类,拦截器接口,拦截器类,目标接口,目标类,JDK动态代理核心类,测试类

//拦截器接口
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.Method;
public interface Interceptor {
    public boolean before(Object proxy, Object target, Method method, Object[] args);
    public void after(Object proxy, Object target, Method method, Object[] args);
    public void around(Object proxy, Object target, Method method, Object[] args);
}

//拦截器类
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.Method;
public class InterceptorImpl implements Interceptor {
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法前逻辑");
        return false;    //这里为false,则不调用目标原有方法,直接调用around的替代方法
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法后逻辑");
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("取代了被代理对象的方法");
    }
}

//目标接口
package com.amiao.design_pattern.dynamic_proxy.jdk;
public interface HelloWorld {
    public void sayHelloWorld();
}

//目标类
package com.amiao.design_pattern.dynamic_proxy.jdk;
public class HelloWorldImpl implements HelloWorld {
    public void sayHelloWorld() {
        System.out.println("Hello World");
    }
}

//JDK动态代理核心类
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyExample implements InvocationHandler {

    private Object target = null;
    private String interceptorClass = null;

    public JdkProxyExample(Object target, String interceptorClass) {
        this.target = target;
        this.interceptorClass = interceptorClass;
    }

    public static Object bind(Object target, String interceptorClass) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxyExample(target, interceptorClass));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(interceptorClass == null) {
            return method.invoke(target, args);
        }
        Object returnObj = null;
        Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
        if(interceptor.before(proxy, target, method, args)) {
            returnObj = method.invoke(target, args);
        }else {
            interceptor.around(proxy, target, method, args);
        }
        interceptor.after(proxy, target, method, args);
        return returnObj;
    }
}

//测试类
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import com.amiao.design_pattern.dynamic_proxy.jdk.HelloWorld;
import com.amiao.design_pattern.dynamic_proxy.jdk.HelloWorldImpl;
public class TestMain {
    public static void main(String args[]) {
        HelloWorld proxy = (HelloWorld) JdkProxyExample.bind(new HelloWorldImpl(), "com.amiao.design_pattern.dynamic_proxy.interceptor.InterceptorImpl");
        proxy.sayHelloWorld();
    }
}

/*
* 运行结果
* 反射方法前逻辑
* 取代了被代理对象的方法
* 反射方法后逻辑
* */

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值