硬核!Github霸榜8小时的SpringMVC笔记,清华教授都给了好评

50 篇文章 0 订阅

上一篇分享的是《基于springcloud一套可落地实施的安全认证框架整合》,这篇给大家分享《SpringMVC-RequestMappingHandlerMapping》。

前言

使用SpringBoot进行web开发时,控制器类由@RestController注解修饰,通常@RestController注解与@RequestMapping配合使用,被修饰的类用于处理由DispatcherServlet分发下来的web请求。那么当一个web请求到达时,DispatcherServlet是如何将请求下发给对应的控制器处理呢。该篇文章将结合SpringMVC源码,对在分发请求过程中起重要作用的类RequestMappingHandlerMapping进行学习。

SpringBoot版本:2.4.1

正文

一. DispatcherServlet分发请求

当一个web请求到来时,DispatcherServlet负责接收请求并响应结果。DispatcherServlet首先需要找到当前请求对应的Handler(处理器)来处理请求,流程如下图所示。

HandlerMapping称为处理器映射器,是一个接口,定义web请求和Handler之间的映射。DispatcherServlet中有一个成员变量叫做handlerMappings,是一个HandlerMapping的集合,当请求到来时,DispatcherServlet遍历handlerMappings中的每一个HandlerMapping以获取对应的handler。上述步骤发生在DispatcherServlet的doDispatch()方法中,部分源码如下所示。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            //根据请求获取Handler
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
            ......
        }
        catch (Exception ex) {
                ......
        }
        catch (Throwable err) {
            ......
        }
        ......
    }
    catch (Exception ex) {
        ......
    }
    catch (Throwable err) {
        ......
    }
    finally {
        ......
    }
}

Handler的获取由DispatcherServlet的getHandler()方法完成,下面再看一下getHandler()具体做了什么事情。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

前文已知handlerMappings是HandlerMapping的集合,因此getHandler()主要实现遍历每一个HandlerMapping并根据请求获取对应的Handler。仅看源码不够直观,现在通过打断点的方式实际看一下handlerMappings里的内容。

观察handlerMappings的内容可以发现,handlerMappings中加载了ApplicationContext中的所有HandlerMapping,例如BeanNameUrlHandlerMapping,将url与名称以/开头的bean建立了映射关系,再例如本文重点讨论的RequestMappingHandlerMapping,能够将@Controller注解修饰的类中的@RequestMapping注解的内容解析成RequestMappingInfo数据结构。每一种HandlerMapping都有自己相应的实现,来完成通过请求获取Handler的功能。

小节:DispatcherServlet分发请求主要是通过遍历HandlerMapping的集合并将请求传递给HandlerMapping以获取对应的Handler。

二. RequestMappingHandlerMapping初始化

首先通过类图认识一下RequestMappingHandlerMapping。

由类图可知,RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,RequestMappingHandlerMapping和AbstractHandlerMethodMapping均实现了afterPropertiesSet()方法,该方法会在bean属性完成初始化后被调用。这里先分析RequestMappingHandlerMapping实现的afterPropertiesSet()方法。

public void afterPropertiesSet() {

    this.config = new RequestMappingInfo.BuilderConfiguration();
    this.config.setTrailingSlashMatch(useTrailingSlashMatch());
    this.config.setContentNegotiationManager(getContentNegotiationManager());

    if (getPatternParser() != null) {
        this.config.setPatternParser(getPatternParser());
        Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
                "Suffix pattern matching not supported with PathPatternParser.");
    }
    else {
        this.config.setSuffixPatternMatch(useSuffixPatternMatch());
        this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
        this.config.setPathMatcher(getPathMatcher());
    }
    
    //调用AbstractHandlerMethodMapping的afterPropertiesSet()方法
    super.afterPropertiesSet();
}

在RequestMappingHandlerMapping的afterPropertiesSet()方法中调用了AbstractHandlerMethodMapping的afterPropertiesSet()方法,下面再分析AbstractHandlerMethodMapping的afterPropertiesSet()方法。

public void afterPropertiesSet() {
    initHandlerMethods();
}

protected void initHandlerMethods() {
    //获取容器中所有bean的名称
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            //根据bean名称处理bean
            processCandidateBean(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    try {
        //先获取容器,然后根据bean名称获取bean的Class对象
        beanType = obtainApplicationContext().getType(beanName);
    }
    catch (Throwable ex) {
        if (logger.isTraceEnabled()) {
            logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
        }
    }
    //判断bean是否是由@Controller注解或者@RequestMapping注解修饰的对象
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}

在AbstractHandlerMethodMapping的afterPropertiesSet()方法中,调用了initHandlerMethods()方法,该方法主要是从容器中将所有bean获取出来(这里是获取的所有bean的名称),然后又在processCandidateBean()方法中判断每个bean是否是由@Controller注解或者@RequestMapping注解修饰的对象,如果是则判断该bean为一个处理器,则需要在detectHandlerMethods()方法中查找出该处理器的处理器方法。detectHandlerMethods()方法是一个重要方法,并且阅读起来有一点绕,下面详细看一下这个方法做的事情。

protected void detectHandlerMethods(Object handler) {
    //获取handler的Class对象
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        //获取handler的真实Class对象(假若handler是cglib代理生成的子类,则获取原始类的Class对象)
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        //调用getMappingForMethod()获取method和RequestMappingInfo的map集合
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                    try {
                        //在selectMethods()方法中实际调用的是getMappingForMethod()方法
                        return getMappingForMethod(method, userType);
                    }
                    catch (Throwable ex) {
                        throw new IllegalStateException("Invalid mapping on handler class [" +
                                userType.getName() + "]: " + method, ex);
                    }
                });
        if (logger.isTraceEnabled()) {
            logger.trace(formatMappings(userType, methods));
        }
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            //将handler,method和RequestMappingInfo缓存,并建立映射关系
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

detectHandlerMethods()中首先是获取handler的真实Class对象,然后使用MethodIntrospector.selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup)方法将handler的方法解析成<Method, RequestMappingInfo>的map集合。metadataLookup是一个回调函数,metadataLookup的具体使用稍后再分析,现在再看一下MethodIntrospector.selectMethods()的具体实现。

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
    final Map<Method, T> methodMap = new LinkedHashMap<>();
    Set<Class<?>> handlerTypes = new LinkedHashSet<>();
    Class<?> specificHandlerType = null;

    //判断给定对象是否是JDK动态代理生成对象
    //如果不是,(如果是CGLIB动态代理生成对象)则获取其原始类的Class对象,并添加到Class的Set集合中
    if (!Proxy.isProxyClass(targetType)) {
        specificHandlerType = ClassUtils.getUserClass(targetType);
        handlerTypes.add(specificHandlerType);
    }
    //获取给定对象和给定对象父类实现的所有接口的Class对象,并添加到Class的Set集合中
    handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

    for (Class<?> currentHandlerType : handlerTypes) {
        final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

        ReflectionUtils.doWithMethods(currentHandlerType, method -> {
            //获取真实方法(如果方法是接口的方法则根据Class对象找到真实实现的方法)
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
            //执行回调函数metadataLookup
            T result = metadataLookup.inspect(specificMethod);
            if (result != null) {
                //根据真实方法获取其桥接方法,但如果真实方法不是桥接方法则返回其本身
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                    //将真实方法与其回调函数执行结果存放到map中
                    methodMap.put(specificMethod, result);
                }
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);
    }

    return methodMap;
}

在MethodIntrospector.selectMethods()中有一个Class对象的Set集合,里面存放了给定对象的Class对象以及给定对象实现的接口的Class对象(如果给定对象有父类,则还包括父类实现的接口的Class对象),然后遍历Set集合,并使用ReflectionUtils.doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf)处理Set集合中的每一个Class对象。mc是一个回调函数,mc的具体使用稍后分析,最后再来看一下ReflectionUtils.doWithMethods()的具体使用。

public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
    //获取给定对象的所有声明方法
    Method[] methods = getDeclaredMethods(clazz, false);
    for (Method method : methods) {
        //对method根据传入的MethodFilter进行过滤,满足指定的条件的method才执行回调方法
        if (mf != null && !mf.matches(method)) {
            continue;
        }
        try {
            //对满足条件的method执行回调方法
            mc.doWith(method);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
        }
    }
    //递归对给定对象的父对象执行相同操作
    if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
        doWithMethods(clazz.getSuperclass(), mc, mf);
    }
    else if (clazz.isInterface()) {
        for (Class<?> superIfc : clazz.getInterfaces()) {
            doWithMethods(superIfc, mc, mf);
        }
    }
}

ReflectionUtils.doWithMethods()中做的事情很简单,先将给定的Class对象的所有声明方法获取出来,然后针对每一个声明方法用给定的MethodFilter进行过滤,再将过滤后的声明方法传入回到函数mc并执行,(现在往前推)回调函数mc中实际就是将声明方法传入回调函数metadataLookup并执行,然后将声明方法和metadataLookup执行得到的结果存入map集合,回调函数metadataLookup中实际就是将声明方法传入getMappingForMethod()方法,在getMappingForMethod()中会将声明方法和handler上的@RequestMapping注解信息解析成RequestMappingInfo并返回。

前文可知MethodIntrospector.selectMethods()中调用ReflectionUtils.doWithMethods()时传入的MethodFilter为ReflectionUtils.USER_DECLARED_METHODS,ReflectionUtils.USER_DECLARED_METHODS表示如果方法即不是桥接方法也不是合成方法时则匹配成功,此时调用matches()返回true。

说明一下getMappingForMethod()方法,该方法是AbstractHandlerMethodMapping声明的抽象方法,RequestMappingHandlerMapping对其的实现如下。

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    //将method上的@RequestMapping注解内容解析为RequestMappingInfo
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        //将类上的@RequestMapping注解内容解析为RequestMappingInfo
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            //将method和类上的@RequestMethod注解解析成的RequestMappingInfo组合
            info = typeInfo.combine(info);
        }
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
        }
    }
    return info;
}

RequestMappingHandlerMapping在getMappingForMethod()中先后分别获取方法和类的@RequestMapping注解解析成的RequestMappingInfo并进行组合。@RequestMapping注解信息的解析发生在createRequestMappingInfo()方法中,其实现如下。

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    //查找@RequestMapping注解
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = (element instanceof Class ?
            getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    //能够查找到@RequestMapping注解,解析@RequestMapping的信息
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

protected RequestMappingInfo createRequestMappingInfo(
        RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

    RequestMappingInfo.Builder builder = RequestMappingInfo
            .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
            .methods(requestMapping.method())
            .params(requestMapping.params())
            .headers(requestMapping.headers())
            .consumes(requestMapping.consumes())
            .produces(requestMapping.produces())
            .mappingName(requestMapping.name());
    if (customCondition != null) {
        builder.customCondition(customCondition);
    }
    return builder.options(this.config).build();
}

最后回到detectHandlerMethods()方法,该方法中执行完MethodIntrospector.selectMethods()后会得到method和RequestMappingInfo的map集合,然后遍历map集合并调用registerHandlerMethod()方法将handler,method和RequestMappingInfo缓存,并建立映射关系。registerHandlerMethod()方法中会调用mappingRegistry的register()方法,mappingRegistry是AbstractHandlerMethodMapping的一个内部类对象,register()方法主要是将handler,method和RequestMappingInfo写入mappingRegistry的pathLookUp,nameLookUp,corsLookUp和registry数据结构中。相关源码如下所示。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
    this.mappingRegistry.register(mapping, handler, method);
}

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        //获取HandlerMethod的实例,HandlerMethod对handler method进行了一层封装,其持有handler的对象,并且可以方便获取方法入参和出参
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        //在registry中根据RequestMappingInfo获取已经缓存的cachedHandlerMethod,如果cachedHandlerMethod不为空且不等于handlerMethod,则报错
        validateMethodMapping(handlerMethod, mapping);

        //根据RequestMappingInfo获取path的Set集合,并建立path和RequestMappingInfo的映射关系:Map<String, List<RequestMappingInfo>> pathLookUp
        Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
        for (String path : directPaths) {
            this.pathLookup.add(path, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            //拼接name
            //规则:handler的Class对象名称取大写字母 + # + 方法名
            name = getNamingStrategy().getName(handlerMethod, mapping);
            //建立name与handlerMethod的映射关系:Map<String, List<HandlerMethod>> nameLookup
            addMappingName(name, handlerMethod);
        }

        //获取跨域配置对象
        CorsConfiguration config = initCorsConfiguration(handler, method, mapping);
        if (config != null) {
            config.validateAllowCredentials();
            //建立handlerMethod和跨域配置对象的映射关系:Map<HandlerMethod, CorsConfiguration> corsLookup
            this.corsLookup.put(handlerMethod, config);
        }

        //建立RequestMappingInfo与MappingRegistration的映射关系:Map<RequestMappingInfo, MappingRegistration<RequestMappingInfo>>
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

MappingRegistration将传入的RequestMappingInfo,获取的handlerMethod,获得的directPaths和拼接的name做了一层封装。

static class MappingRegistration<T> {

    private final T mapping;

    private final HandlerMethod handlerMethod;

    private final Set<String> directPaths;

    @Nullable
    private final String mappingName;

    public MappingRegistration(T mapping, HandlerMethod handlerMethod,
            @Nullable Set<String> directPaths, @Nullable String mappingName) {

        Assert.notNull(mapping, "Mapping must not be null");
        Assert.notNull(handlerMethod, "HandlerMethod must not be null");
        this.mapping = mapping;
        this.handlerMethod = handlerMethod;
        this.directPaths = (directPaths != null ? directPaths : Collections.emptySet());
        this.mappingName = mappingName;
    }

    public T getMapping() {
        return this.mapping;
    }

    public HandlerMethod getHandlerMethod() {
        return this.handlerMethod;
    }

    public Set<String> getDirectPaths() {
        return this.directPaths;
    }

    @Nullable
    public String getMappingName() {
        return this.mappingName;
    }
}

小节:RequestMappingHandlerMapping初始化时会先获取容器中所有被@Controller注解或@RequestMapping注解修饰的类的对象(handler对象),然后遍历这些对象和其父对象的所有方法,将这些方法的@RequestMapping注解信息(如果有)解析成RequestMappingInfo,最后将handler对象,handler方法和RequestMappingInfo加入缓存并建立映射关系。

三. RequestMappingHandlerMapping获取handler

回顾上文,一小节中提到,web请求来到DispatcherServlet之后,会先遍历HandlerMapping的集合,然后将请求传入HandlerMapping并获取handler。再贴出源码如下所示。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

实际上,RequestMappingHandlerMapping获取handler是发生在其父类AbstractHandlerMapping的getHandler()方法中,源码如下。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //根据request获取handler,实际获取到的handler是一个HandlerMethod对象
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    //根据request和handler创建HandlerExecutionChain对象,该对象还会包含和request匹配的拦截器
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    //如果handler有跨域配置,则更新HandlerExecutionChain对象使得其可以进行跨域处理
    if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
        CorsConfiguration config = getCorsConfiguration(handler, request);
        if (getCorsConfigurationSource() != null) {
            CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
            config = (globalConfig != null ? globalConfig.combine(config) : config);
        }
        if (config != null) {
            config.validateAllowCredentials();
        }
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}

getHandler()关键的操作就是在getHandlerInternal()方法中根据request获取到了handler对象(实际是一个HandlerMethod对象),RequestMappingHandlerMapping的getHandlerInternal()会调用父类AbstractHandlerMethodMapping的getHandlerInternal()方法,现在看一下其做了什么事情。

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //根据request获取请求路径
    String lookupPath = initLookupPath(request);
    this.mappingRegistry.acquireReadLock();
    try {
        //根据请求路径和request获取最匹配的HandlerMethod对象
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    //通过缓存pathLookup获取请求路径映射的RequestMappingInfo集合
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
        //集合中的每一个RequestMappingInfo均会和request进行匹配,匹配上的话就创建一个Match对象并加入Match对象集合
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    //Match集合不为空则从Match集合中找到最匹配的Match对象,并返回该Match对象的HandlerMethod对象
    if (!matches.isEmpty()) {
        Match bestMatch = matches.get(0);
        if (matches.size() > 1) {
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            matches.sort(comparator);
            bestMatch = matches.get(0);
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    }
}

小节:RequestMappingHandlerMapping获取handler实际就是根据request在映射缓存中寻找最匹配的HandlerMethod对象并封装成HandlerExecutionChain。

总结

RequestMappingHandlerMapping主要用于@Controller注解和@RequestMapping注解结合使用的场景,能够将我们编写的控制器信息缓存并在请求到来时根据请求信息找到最合适的控制器来处理请求。最后,学习SpringMVC源码,通过打断点观察内部数据结构的方式往往能够更直观地帮助我们理解,值得尝试。

  • 以上就是《SpringMVC-RequestMappingHandlerMapping》的分享。
  • 也欢迎大家交流探讨,该文章若有不正确的地方,希望大家多多包涵。
  • 创作不易,你们的支持就是我最大的动力,如果对大家有帮忙给个赞哦~~~

 

大部分Java应用都是Web应用,展现层是Web应用不可忽略的重要环节。Spring为展现层提供了一个优秀的Web框架——Spring MVC。和众多其它Web框架一样,它基于MVC设计理念,此外,由于它采用了松散耦合可插拔组件结构,具有比其它MVC框架更多的扩展性和灵活性。 Spring MVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet的作用是截获请求并组织一系列组件共同完成请求的处理工作。 JavaServer Faces (JSF) 是一种用于构建 Web 应用程序的新标准 Java 框架。它提供了一种以组件为中心来开发 Java Web 用户界面的方法,从而简化了开发。JavaServer Faces 还引起了广大 Java/Web 开发人员的兴趣。“企业开发人员”和 Web 设计人员将发现 JSF 开发可以简单到只需将用户界面 (UI) 组件拖放到页面上,而“系统开发人员”将发现丰富而强健的 JSF API 为他们提供了无与伦比的功能和编程灵活性。JSF 还通过将良好构建的模型-视图-控制器 (MVC) 设计模式集成到它的体系结构中,确保了应用程序具有更高的可维护性。最后,由于 JSF 是通过 Java Community Process (JCP) 开发的一种 Java 标准,因此开发工具供应商完全能够为 JavaServer Faces 提供易于使用的、高效的可视化开发环境。 ① 整个过程开始于客户端发送一个HTTP请求; ② DispatcherServlet接收这个请求后,并将请求的处理工作委托给具体的处理器(Handler),后者负责处理请求执行相应的业务逻辑。在这之前,DispatcherServlet必须能够凭借请求信息(URL或请求参数等)按照某种机制找到请求对应的处理器,DispatcherServlet是通过垂询HandlerMapping完成这一工作的; ③ 当DispatcherServlet从HandlerMapping中得到当前请求对应的处理器后,它就将请求分派给这个处理器。处理器根据请求的信息执行相应的业务逻辑,一个设计良好的处理器应该通过调用Service层的业务对象完成业务处理,而非自己越俎代庖。 Spring提供了丰富的处理器类型,在真正处理业务逻辑前,有些处理器会事先执行两项预处理工作: 1)将HttpServletRequest请求参数绑定到一个POJO对象中; 2)对绑定了请求参数的POJO对象进行数据合法性校验; ④ 处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和渲染视图时需要用到的模型数据对象; ⑤ 由于ModelAndView中包含的是视图逻辑名,DispatcherServlet必须知道这个逻辑名对应的真实视图对象,这项视图解析的工作通过调用ViewResolver来完成; ⑥ 当得到真实的视图对象后,DispatcherServlet将请求分派给这个View对象,由其完成Model数据的渲染工作; ⑦ 最终客户端得到返回的响应,这可能是一个普通的HTML页面,也可能是一个Excel电子表格、甚至是一个PDF文档等不一而足的视图形式,Spring的视图类型是异常丰富和灵活的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值