美文网首页撸一个mvc框架
动手撸一个 mvc 框架2

动手撸一个 mvc 框架2

作者: 想54256 | 来源:发表于2020-04-29 13:33 被阅读0次

    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;
        }
    }
    

    相关文章

      网友评论

        本文标题:动手撸一个 mvc 框架2

        本文链接:https://www.haomeiwen.com/subject/kkvgwhtx.html