程序员社区

动手撸一个 mvc 框架4


title: 动手撸一个 mvc 框架4
date: 2020/04/26 10:54


本节内容

我们回到 tag2 看看拦截器是怎么被装配上的

Spring 4.0

我们接下来看 tag2 ,点进去

动手撸一个 mvc 框架4插图
动手撸一个 mvc 框架4插图1

上图我们提出了 2 个问题,接下来我们来研究一下:

众所周知我们要自定义拦截器的时候需要实现 HandlerInterceptor 接口,假设我们开启了 @EnableMvc 并且通过配置类的方式对拦截器进行注册,如下图:

动手撸一个 mvc 框架4插图2

上图引出了一个叫 InterceptorRegistry 的东西,我们看下它是什么:

动手撸一个 mvc 框架4插图3

那么谁会调用 getInterceptors 方法呢:

动手撸一个 mvc 框架4插图4

那么问题来了,我们继承 WebMvcConfigurerAdapter 的那个类是在什么时候调用的呢?

动手撸一个 mvc 框架4插图5
动手撸一个 mvc 框架4插图6
动手撸一个 mvc 框架4插图7

我们在上面的代码中没有发现读取 xml 配置的拦截器,那是不是用了 @EnableMvc 注解,就不能使用 xml 配置的拦截器了?

当然不是,因为在 RequestMappingHandlerMapping 中拦截器采用的是 adaptedInterceptors 字段,而不是上面设置的 interceptors 字段。

动手撸一个 mvc 框架4插图8

我们开始吧

1、AbstractHandlerMapping 新增 getHandlerInternal 方法

public abstract class AbstractHandlerMapping implements HandlerMapping {

    private List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

    ...

     private HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {

        HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain(handler);

        for (HandlerInterceptor interceptor : adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                String lookupPath = request.getServletPath();
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath)) {
                    handlerExecutionChain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
                // 当一个拦截器没有条件的时候会走这里
                handlerExecutionChain.addInterceptor(interceptor);
            }
        }

        return handlerExecutionChain;
    }

    /**
     * 查找给定请求的处理程序,如果未找到特定请求,则返回 null。
     */
    protected abstract Object getHandlerInternal(HttpServletRequest request);

    public void setInterceptors(List<HandlerInterceptor> interceptors) {
        adaptedInterceptors.addAll(interceptors);
    }
}

2、拦截器接口

public interface HandlerInterceptor {

    /**
     * Controller执行前调用此方法
     */
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

//    /**
//     * Controller执行后但未返回视图前调用此方法
//     */
//    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView);

    /**
     * Controller执行后且视图返回后调用此方法
     */
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);

}

3、MappedInterceptor

/**
 * Spring 中这里实现了 HandlerInterceptor 是因为 adaptedInterceptors 中有些不是 MappedInterceptor 的实现,而是 HandlerInterceptor
 * <p>
 * 这里可以理解为它采用了装饰者模式,动态的给 HandlerInterceptor 增加了 matches 方法
 * <p>
 * MappedInterceptor 与 InterceptorRegistration 之间是类似于 String 和 StringBuilder 之间的关系
 * <p>
 * MappedInterceptor 也是不可变对象
 *
 * @author yujx
 * @date 2020/04/26 11:10
 */
public final class MappedInterceptor implements HandlerInterceptor {

    private final HandlerInterceptor interceptor;

    private final List<String> includePatterns;

    private final List<String> excludePatterns;

    public MappedInterceptor(HandlerInterceptor interceptor, List<String> includePatterns, List<String> excludePatterns) {
        this.interceptor = interceptor;
        this.includePatterns = includePatterns;
        this.excludePatterns = excludePatterns;
    }

    // 全部 return true 了,不想写逻辑
    public boolean matches(String lookupPath) {
        return true;
    }


    public HandlerInterceptor getInterceptor() {
        return interceptor;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return interceptor.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        interceptor.afterCompletion(request, response, handler, ex);
    }
}

4、InterceptorRegistry

/**
 * 拦截器注册中心
 *
 * @author yujx
 * @date 2020/04/26 13:11
 */
public class InterceptorRegistry {

    private final List<InterceptorRegistration> registrations = new ArrayList<>();

    /**
     * 添加拦截器
     */
    public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
        InterceptorRegistration registration = new InterceptorRegistration(interceptor);
        registrations.add(registration);
        return registration;
    }

    /**
     * 返回所有注册的拦截器。
     */
    protected List<HandlerInterceptor> getInterceptors() {
        List<HandlerInterceptor> interceptors = new ArrayList<>();
        for (InterceptorRegistration registration : registrations) {
            interceptors.add(registration.getInterceptor());
        }
        return interceptors ;
    }
}

5、MappedInterceptor 的“伙伴”

public class InterceptorRegistration {

    private final HandlerInterceptor interceptor;

    private final List<String> includePatterns = new ArrayList<>();

    private final List<String> excludePatterns = new ArrayList<>();

    public InterceptorRegistration(HandlerInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    public InterceptorRegistration addPathPatterns(String... patterns) {
        this.includePatterns.addAll(Arrays.asList(patterns));
        return this;
    }

    public InterceptorRegistration excludePathPatterns(String... patterns) {
        this.excludePatterns.addAll(Arrays.asList(patterns));
        return this;
    }

    public HandlerInterceptor getInterceptor() {
        // 如果 includePatterns 和 excludePatterns 都是空的,则直接返回这个拦截器
        if (CollectionUtil.isEmpty(includePatterns) && CollectionUtil.isEmpty(excludePatterns)) {
            return interceptor;
        }

        // Spring 中在这里将其转换成了数组,目的是为了让其不可变
        return new MappedInterceptor(interceptor, includePatterns, excludePatterns);
    }
}

6、可以让用户添加拦截器的

public interface WebMvcConfigurer {

    default void addInterceptors(InterceptorRegistry registry) {
    }
    
}

7、配置类及其相关类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}


/**
 * 为什么要使用 WebMvcConfigurerComposite 作为 WebMvcConfigurer 集合的容器,并且采用组合模式?
 * <p>
 * 可能是为了更面向对象一点吧
 *
 * @author yujx
 * @date 2020/04/26 13:25
 */
public class DelegatingWebMvcConfiguration implements ApplicationContextAware, InitializingBean {

    private ApplicationContext applicationContext;

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
        handlerMapping.setInterceptors(getInterceptors());

        return handlerMapping;
    }

    private List<HandlerInterceptor> getInterceptors() {
        InterceptorRegistry registry = new InterceptorRegistry();
        configurers.addInterceptors(registry);

        return registry.getInterceptors();
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        this.applicationContext = ctx;
    }

    // 当这个对象初始化完成后调用
    @Override
    public void afterPropertiesSet() {
        Collection<WebMvcConfigurer> configurers = applicationContext.getBeansOfType(WebMvcConfigurer.class).values();
        this.configurers.addWebMvcConfigurers(configurers);
    }
}


class WebMvcConfigurerComposite implements WebMvcConfigurer {

    private final List<WebMvcConfigurer> delegates = new ArrayList<>();

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        for (WebMvcConfigurer mvcConfigurer : delegates) {
            mvcConfigurer.addInterceptors(registry);
        }
    }

    public void addWebMvcConfigurers(Collection<WebMvcConfigurer> configurers) {
        delegates.addAll(configurers);
    }
}
赞(0) 打赏
未经允许不得转载:IDEA激活码 » 动手撸一个 mvc 框架4

一个分享Java & Python知识的社区