撸一个mvc框架

动手撸一个 mvc 框架2

2020-04-29  本文已影响0人  想54256

title: 动手撸一个 mvc 框架2
date: 2020/04/22 17:06


本节内容 & 思考题

本节我们将要带大家先过一遍 initHandlerMappings(context) 方法,从中引出我们今天要做的 HandlerMapping

SpringMVC 请求流程?


组合模式

建造者模式

Spring MVC 4.0

1、初始化处理器映射器

DispatcherServlet#initHandlerMappings

2、请求来了怎么处理

image image image

我们先看 tag1

image image image

看到这里你是不是有点疑惑,这个 MappingRegistry 是什么东西。

image image

那 mappingLookup 中的数据到底是什么时候注册的呢?

当创建 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 对象的时候 InitializingBean#afterPropertiesSet

image image

注:RequestMappingHandlerMapping 是怎么放到 bdMap 中的呢? 自己去看 @EnableMvc 注解,我相信你的聪明才智

tag2 与拦截器相关,我们下次在讲

我们实现一下 RequestMappingHandlerMapping 初始化阶段的代码

也就是 InitializingBean#afterPropertiesSet 方法引出的一系列代码。

1、先准备 2 个注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Controller {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any
     */
    String value() default "";

}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {

    /**
     * 此注释表示的主要映射。
     */
    String[] value() default {};

    /**
     * 要映射到的HTTP请求方法,从而缩小了主要映射:
     * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
     */
    RequestMethod[] method() default {};
}

2、新增 HandlerMapping 相关抽象

/**
 * 由定义请求和处理程序对象之间的映射关系的对象实现的接口。
 *
 * @author yujx
 * @date 2020/04/23 11:29
 */
public interface HandlerMapping {

    /**
     * 返回此请求的处理程序和所有拦截器。可以根据请求URL,会话状态或实现类选择的任何因素进行选择。
     */
    HandlerExecutionChain getHandler(HttpServletRequest request);
}


public abstract class AbstractHandlerMapping implements HandlerMapping {
    /**
     * 返回此请求的处理程序和所有拦截器。可以根据请求URL,会话状态或实现类选择的任何因素进行选择。
     */
    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) {

        // 根据请求获取处理程序
        Object handler = this.getHandlerInternal(request);
        if (handler == null) {
            return null;
        }

        // TODO: 2020/4/24 这个地方和拦截器有关,以后再说 
        // return this.getHandlerExecutionChain(handler, request);
        return null;
    }

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

3、新增 RequestMappingHandlerMapping

本节我们只介绍 RequestMappingHandlerMapping 中的 MappingRegistry 内部类数据进行注册部分。

public class RequestMappingHandlerMapping extends AbstractHandlerMapping implements InitializingBean, ApplicationContextAware {

    private ApplicationContext applicationContext;

    // 映射的注册中心
    private final MappingRegistry mappingRegistry = new MappingRegistry();

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

    class MappingRegistry {

        private final Map<RequestMappingInfo, HandlerMethod> mappingLookup = new LinkedHashMap<>();

        private final Multimap<String, RequestMappingInfo> urlLookup = ArrayListMultimap.create();

        public void register(String beanName, Class<?> beanType, Method method, RequestMappingInfo requestMappingInfo) {

            // 创建方法对象
            HandlerMethod handlerMethod = new HandlerMethod(beanName, beanType, applicationContext, method);

            // 添加到 mappingLookup 中
            mappingLookup.put(requestMappingInfo, handlerMethod);

            // 将路径匹配放到 urlLookup 中
            for (String pattern : requestMappingInfo.getPatternsCondition().getPatterns()) {
                urlLookup.put(pattern, requestMappingInfo);
            }
        }

        /**
         * 一个 url 对应多个 RequestMappingInfo 的情况一般出现在请求方式不同的情况下
         */
        public List<RequestMappingInfo> getMappingsByUrl(String urlPath) {
            return new ArrayList<>(this.urlLookup.get(urlPath));
        }

        public Map<RequestMappingInfo, HandlerMethod> getMappings() {
            return mappingLookup;
        }
    }

    /**
     * 设置所有提供的bean属性后,由BeanFactory调用。
     */
    @Override
    public void afterPropertiesSet() {

        Map<String, Object> beansOfType = applicationContext.getBeansOfType(Object.class);
        beansOfType.forEach((bdName, bean) -> {

            Class<?> beanType = bean.getClass();

            // 判断这个类上是否包含 @Controller 注解或者 @RequestMapping 注解
            if (this.isHandler(beanType)) {
                // 如果包含,则解析它方法上的 @RequestMapping 注解,进行注册
                this.detectHandlerMethods(bdName, beanType);
            }
        });
    }

    private boolean isHandler(Class<?> clazz) {
        Controller controller = AnnotationUtil.getAnnotation(clazz, Controller.class);
        if (ObjectUtil.isNotNull(controller)) {
            return true;
        }

        RequestMapping requestMapping = AnnotationUtil.getAnnotation(clazz, RequestMapping.class);
        if (ObjectUtil.isNotNull(requestMapping)) {
            return true;
        }

        return false;
    }

    /**
     * 找到标注 @RequestMapping 的方法,注册到注册中心
     */
    private void detectHandlerMethods(String beanName, Class<?> beanType) {

        // 使用 Map 装的目的是为了去重
        Map<Method, RequestMappingInfo> methodMap = new LinkedHashMap<Method, RequestMappingInfo>();
        Queue<Class<?>> handlerTypes = new LinkedList<>();

        handlerTypes.add(beanType);

        while (!handlerTypes.isEmpty()) {
            Class<?> handlerType = handlerTypes.remove();

            // 将他的父类或者接口
            handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
            Class<?> superclass = handlerType.getSuperclass();
            if (ObjectUtil.isNotNull(superclass)) {
                handlerTypes.add(superclass);
            }

            for (Method method : handlerType.getMethods()) {
                // 判断当前 method 是否有 @RequestMapping 注解,如果有返回 RequestMappingInfo,没有返回 null
                RequestMappingInfo info = this.createRequestMappingInfo(method);
                if (ObjectUtil.isNotNull(info)) {
                    RequestMappingInfo classRequestMappingInfo = this.createRequestMappingInfo(handlerType);
                    if (ObjectUtil.isNotNull(classRequestMappingInfo)) {
                        // 使两者结合
                        info = info.combine(classRequestMappingInfo);
                    }
                }

                if (ObjectUtil.isNotNull(info)) {
                    // 放进 Map 中
                    methodMap.put(method, info);
                }
            }
        }

        methodMap.forEach(((method, info) -> {
            // 注册到映射注册中心
            mappingRegistry.register(beanName, beanType, method, info);
        }));
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = AnnotationUtil.getAnnotation(element, RequestMapping.class);
        if (ObjectUtil.isNull(requestMapping)) {
            return null;
        }

        return RequestMappingInfo
                .paths(requestMapping.value())
                .methods(requestMapping.method())
                .build();
    }

    /**
     * 查找给定请求的处理程序,如果未找到特定请求,则返回 null。
     */
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) {
        throw new RuntimeException("没有为当前请求找到处理器!");
    }
}

4、相关的对象

/**
 * 请求条件对象
 */
public interface RequestCondition<T> {

    /**
     * 将两个 RequestCondition 对象结合
     */
    T combine(T other);

    /**
     * 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
     * <p>
     * 不匹配返回 null
     */
    T getMatchingCondition(HttpServletRequest request);

}

和 3 个实现:

public final class PatternsRequestCondition implements RequestCondition<PatternsRequestCondition> {

    private final Set<String> patterns = new HashSet<>();

    public PatternsRequestCondition(String... paths) {
        this(Arrays.asList(paths));
    }

    public PatternsRequestCondition(Collection<String> patterns) {
        this.patterns.addAll(patterns);
    }

    public Set<String> getPatterns() {
        return this.patterns;
    }

    /**
     * 将两个 RequestCondition 对象结合
     */
    @Override
    public PatternsRequestCondition combine(PatternsRequestCondition other) {
        Set<String> result = new HashSet<>();
        result.addAll(this.patterns);
        result.addAll(other.patterns);

        return new PatternsRequestCondition(result);
    }

    /**
     * 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
     * <p>
     * 不匹配返回 null
     */
    @Override
    public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
        String lookupPath = request.getServletPath();

        Set<String> matches = new HashSet<>();
        for (String pattern : this.patterns) {
            String match = this.getMatchingPattern(pattern, lookupPath);
            if (match != null) {
                matches.add(match);
            }
        }

        return matches.isEmpty() ? null : new PatternsRequestCondition(matches);
    }

    private String getMatchingPattern(String pattern, String lookupPath) {
        if (ObjectUtil.equal(pattern, lookupPath)) {
            return pattern;
        }

        // 解析 {} 的情况
        // TODO: 2020/4/24 我实在不想写啊

        return null;
    }
}

public class RequestMethodsRequestCondition implements RequestCondition<RequestMethodsRequestCondition> {

    private final Set<RequestMethod> methods = new HashSet<>();

    public RequestMethodsRequestCondition(RequestMethod... methods) {
        this(Arrays.asList(methods));
    }

    private RequestMethodsRequestCondition(Collection<RequestMethod> requestMethods) {
        this.methods.addAll(requestMethods);
    }

    /**
     * 将两个 RequestCondition 对象结合
     */
    @Override
    public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) {
        Set<RequestMethod> result = new HashSet<>();
        result.addAll(this.methods);
        result.addAll(other.methods);

        return new RequestMethodsRequestCondition(result);
    }

    /**
     * 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
     * <p>
     * 不匹配返回 null
     */
    @Override
    public RequestMethodsRequestCondition getMatchingCondition(HttpServletRequest request) {
        if (methods.isEmpty()) {
            return null;
        }

        RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod());
        if (methods.contains(requestMethod)) {
            return new RequestMethodsRequestCondition(requestMethod);
        }

        return null;
    }
}

public class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

    // 请求路径条件 /rest/xxx
    private PatternsRequestCondition patternsCondition;

    // 请求方法条件 GET、POST
    private RequestMethodsRequestCondition requestMethodsRequestCondition;

    private RequestMappingInfo(PatternsRequestCondition patternsCondition, RequestMethodsRequestCondition requestMethodsRequestCondition) {
        this.patternsCondition = patternsCondition;
        this.requestMethodsRequestCondition = requestMethodsRequestCondition;
    }

    /**
     * 将两个 RequestCondition 对象结合
     */
    @Override
    public RequestMappingInfo combine(RequestMappingInfo other) {
        PatternsRequestCondition patternsRequestCondition = this.patternsCondition.combine(other.patternsCondition);
        RequestMethodsRequestCondition requestMethodsRequestCondition = this.requestMethodsRequestCondition.combine(other.requestMethodsRequestCondition);

        return new RequestMappingInfo(patternsRequestCondition, requestMethodsRequestCondition);
    }

    /**
     * 检查此条件是否与给定请求匹配,并返回潜在的新请求条件,其中包含适合当前请求的内容。
     * <p>
     * 不匹配返回 null
     */
    @Override
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        PatternsRequestCondition pattern = this.patternsCondition.getMatchingCondition(request);
        if (ObjectUtil.isNull(pattern)) {
            return null;
        }

        RequestMethodsRequestCondition requestMethods = this.requestMethodsRequestCondition.getMatchingCondition(request);
        if (ObjectUtil.isNull(requestMethods)) {
            return null;
        }

        return new RequestMappingInfo(pattern, requestMethods);
    }

    public static Builder paths(String... paths) {
        return new Builder(paths);
    }

    public PatternsRequestCondition getPatternsCondition() {
        return patternsCondition;
    }

    public RequestMethodsRequestCondition getRequestMethodsRequestCondition() {
        return requestMethodsRequestCondition;
    }

    public static class Builder {

        private String[] paths;

        private RequestMethod[] methods;

        public Builder(String... paths) {
            this.paths = paths;
        }

        public Builder paths(String... paths) {
            this.paths = paths;
            return this;
        }

        public Builder methods(RequestMethod... methods) {
            this.methods = methods;
            return this;
        }

        public RequestMappingInfo build() {

            PatternsRequestCondition patternsCondition = paths != null ? new PatternsRequestCondition(this.paths) : new PatternsRequestCondition();
            RequestMethodsRequestCondition requestMethodsRequestCondition = methods != null ? new RequestMethodsRequestCondition(methods) : new RequestMethodsRequestCondition();

            return new RequestMappingInfo(patternsCondition, requestMethodsRequestCondition);
        }
    }
}

5、相关对象2

public class HandlerMethod {

    private final String beanName;

    private final BeanFactory beanFactory;

    private final Class<?> beanType;

    private final Method method;

    private final MethodParameter[] parameters;

    public HandlerMethod(String beanName, Class<?> beanType, BeanFactory beanFactory, Method method) {
        this.beanName = beanName;
        this.beanType = beanType;
        this.beanFactory = beanFactory;
        this.method = method;
        this.parameters = this.initMethodParameters();
    }

    private MethodParameter[] initMethodParameters() {
        int count = this.method.getParameterTypes().length;
        MethodParameter[] result = new MethodParameter[count];
        for (int i = 0; i < count; i++) {
            result[i] = new MethodParameter(method, i);
        }
        return result;
    }
}


public class MethodParameter {

    // 方法
    private final Method method;

    // 参数对应的索引位置
    private final int parameterIndex;

    public MethodParameter(Method method, int parameterIndex) {
        this.method = method;
        this.parameterIndex = parameterIndex;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读