spring揭秘24-springmvc02-5个重要组件

news2024/12/23 20:58:48

文章目录

  • 【README】
  • 【1】HanderMapping-处理器映射容器
    • 【1.1】HanderMapping实现类
      • 【1.1.1】SimpleUrlHandlerMapping
  • 【2】Controller(二级控制器)
    • 【2.1】AbstractController抽象控制器(控制器基类)
  • 【3】ModelAndView(模型与视图)
    • 【3.1】ModelAndView中的视图信息
    • 【3.2】ModelAndView中的模型数据
  • 【4】ViewResolver视图解析器
    • 【4.1】可用的ViewResolver实现类
      • 【4.1.1】单一视图类型的视图解析器(以UrlBasedViewResolver为基类)
      • 【4.1.2】多视图类型的视图解析器
  • 【5】View视图
    • 【5.1】View实现原理
    • 【5.2】View实现类(基类AbstractView)
    • 【5.3】AbstractView的子类AbstractUrlBasedView
      • 【5.3.1】使用jsp技术的view
      • 【5.3.2】使用通用模版技术的view
      • 【5.3.3】面向二进制文档格式的View
      • 【5.3.4】重定向视图RedirectView
      • 【5.3.5】使用XSLT技术的View
    • 【5.4】自定义View实现

【README】

本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;

1)springmvc有5个主要组件:

  • HandlerMapping: 封装请求标识与二级控制器映射关系;
  • Controller:二级控制器;
  • ModelAndView:封装模型数据与视图;
  • ViewResovler:视图解析器;
  • View:视图;

2)DispatcherServlet使用上述5个组件处理web请求的流程(把它们串起来)

  • DispatcherServlet一级控制器根据请求标识(如URL)从HandlerMapping查找对应二级控制器Controller,并把请求转发给该Controller;
  • Controller处理完成后返回ModelAndView(模型数据+视图)给DispatcherServlet一级控制器;
  • DispatcherServlet一级控制器根据ModelAndView对象,通过视图解析器ViewResolver获取视图实例View;
  • DispatcherServlet一级控制器调用view.render()方法做实际的视图渲染;


【1】HanderMapping-处理器映射容器

1)HandlerMapping: 用于封装请求标识(如URL)与二级控制器Controller(处理器)间的映射关系; 它实际上是一个接口;

【HandlerMapping】

public interface HandlerMapping {
    String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
    /** @deprecated */
    @Deprecated
    String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    default boolean usesPathPatterns() {
        return false;
    }

    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

【1.1】HanderMapping实现类

1)常用HanderMapping实现类列表:

  • BeanNameUrlHandlerMapping :bean名称与url处理器映射;即一级控制器DispatcherServlet查找BeanName与请求url路径相同的二级控制器, 并把请求转发给该二级控制器处理;
  • SimpleUrlHandlerMapping: 简单URL处理器映射; 相比BeanNameUrlHandlerMapping ,SimpleUrlHandlerMapping能够提供更多灵活匹配模式;


【1.1.1】SimpleUrlHandlerMapping

1)为什么有了BeanNameUrlHandlerMapping ,还需要 SimpleUrlHandlerMapping ?

2)SimpleUrlHandlerMapping作用

  • 可以配置web请求到具体二级控制器的映射;
  • 可以把一组或多组拥有相似特征的web请求映射给二级控制器;
<!-- SimpleUrlHandlerMapping: 可以配置web请求到具体二级控制器的映射, 可以把一组或多组拥有相似特征的web请求映射给二级控制器-->
    <bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <!-- 设置SimpleUrlHandlerMapping优先级为1,优先匹配,若有多个HandlerMapping时 -->
        <property name="order" value="1" />
        <property name="mappings">
            <value>
                /userController.do=userController
                /bankCard*.do=bankCardController
                /bankCard/*.do=bankCardController
            </value>
        </property>
    </bean>

3)一级控制器DispatcherServlet可以有多个HandlerMapping, 请求匹配时,哪个HandlerMapping优先匹配,哪个最后匹配呢?

  • HandlerMapping的优先级规定遵循spring框架内一贯的Ordered接口所规定的语义;
  • HandlerMapping接口的实现类都实现了Ordered接口,在配置HandlerMapping时,只需要指定其 order属性即可;


【2】Controller(二级控制器)

1)Controller:是springmvc框架支持的用于处理具体web请求的处理器类型之一;

【Controller】二级控制器

@FunctionalInterface
public interface Controller {
    @Nullable
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

2)当然,我们可以自行实现Controller接口,包括请求参数的抽取,请求编码的设定,session管理,国际化信息处理,异常处理等;

  • 但为了简化开发成本,这些Controller细节,springmvc已经有现成实现,我们复用即可;

3)总结起来:springmvc提供了一套封装Controller细节的实现类,如下。

在这里插入图片描述

4)Controller可以分为两类: 基于servletapi的没有封装细节的控制器, 封装处理细节的规范操作控制器;

  • 基于servletapi的自行处理细节的控制器,包括 AbstractController,MultiActionController :
    • 控制器处理细节逻辑包括:从HttpServletRequest获取参数, 然后参数验证,调用业务层逻辑,最终返回一个 ModelAndView ;特别的,还可以自行通过 HttpServletResponse 输出最终的视图;
  • 封装处理细节的规范操作控制器,BaseCommandController及子类; 封装的细节如下:
    • 自动抽取请求参数并绑定到 Command对象;
    • 提供统一的数据验证方式; BaseCommandController及子类可以接受 Validator进行数据验证,我们可以提供具体Validator实现;
    • 规范化表单请求的处理流程, 并且对简单的多页面表单请求处理提供支持;

显然: 使用规范操作控制器,可以复用springmvc封装好的处理细节或者自行实现部分细节,开发成本低;

【注意】本文使用的spring版本是6.1.10,本文发现6.1.10中有AbstractController,但没有MultiActionController, BaseCommandController及其子类,所以Controller二级控制器章节仅做了解

在这里插入图片描述



【2.1】AbstractController抽象控制器(控制器基类)

1)AbstractController是所有控制器基类,该类通过模版方法模式定义了控制器处理细节的主要步骤:

  • 管理当前Controller所支持的请求方法类型(GET/POST)
  • 管理页面的缓存设置, 即是否允许浏览器缓存当前页面;
  • 管理执行流程在会话上的同步;

我们需要做的是: 重写AbstractController#handleRequestInternal()模版方法,实现具体业务逻辑处理;

【AbstractController】

public abstract class AbstractController extends WebContentGenerator implements Controller {
    private boolean synchronizeOnSession;

    public AbstractController() {
        this(true);
    }

    public AbstractController(boolean restrictDefaultSupportedMethods) {
        super(restrictDefaultSupportedMethods);
        this.synchronizeOnSession = false;
    }

    public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
        this.synchronizeOnSession = synchronizeOnSession;
    }

    public final boolean isSynchronizeOnSession() {
        return this.synchronizeOnSession;
    }

    @Nullable
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (HttpMethod.OPTIONS.matches(request.getMethod())) {
            response.setHeader("Allow", this.getAllowHeader());
            return null;
        } else {
            this.checkRequest(request);
            this.prepareResponse(response);
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized(mutex) {
                        return this.handleRequestInternal(request, response);
                    }
                }
            }

            return this.handleRequestInternal(request, response); // 处理请求内部逻辑
        }
    }

    @Nullable
    protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

【UserController】

public class UserController extends AbstractController {

    private UserAppService userAppService;

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("handleRequestInternal 被访问了");
        ModelAndView modelAndView = new ModelAndView("userListPage");
        modelAndView.addObject("userList", userAppService.listUser());
        return modelAndView;
    }

    public void setUserAppService(UserAppService userAppService) {
        this.userAppService = userAppService;
    }
}


【3】ModelAndView(模型与视图)

1)ModelAndView: 包含2部分信息,包括视图内容(逻辑视图名称或具体视图实例),模型数据;

2)ModelAndView实际上是一个数据对象:通过该对象,可以使得web请求处理逻辑与视图渲染解耦开;



【3.1】ModelAndView中的视图信息

1)ModelAndView可以以逻辑视图名的形式或者View视图实例的形式来保存视图信息;

【DispatcherServlet#render()】

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
    response.setLocale(locale);
    String viewName = mv.getViewName();
    View view;
    if (viewName != null) { // 若ModelAndView中逻辑视图名存在,则通过逻辑视图名获取视图实例 
        view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
        if (view == null) {
            String var10002 = mv.getViewName();
            throw new ServletException("Could not resolve view with name '" + var10002 + "' in servlet with name '" + this.getServletName() + "'");
        }
    } else { // 若逻辑视图名【不】存在,则直接通过 ModelAndView 获取视图实例 
        view = mv.getView(); 
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");
        }
    }   

  // ...
  view.render(mv.getModelInternal(), request, response); // 获取视图实例后渲染 
  //...
}

2)由上述代码可知:

  • 若ModelAndView中逻辑视图名存在,则通过逻辑视图名获取视图实例
  • 若逻辑视图名【不】存在,则直接通过 ModelAndView 获取视图实例


【3.2】ModelAndView中的模型数据

1)ModelAndView通过 ModelMap来保存模型数据, 通过构造方法传入或实例方法添加的模型数据添加到这个ModelMap中;

2)ModelAndView封装模型数据的代码示例;

public class UserController extends AbstractController {

    private UserAppService userAppService;

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("handleRequestInternal 被访问了");
        ModelAndView modelAndView = new ModelAndView("userListPage");
        modelAndView.addObject("userList", userAppService.listUser());
        return modelAndView;
    }

    public void setUserAppService(UserAppService userAppService) {
        this.userAppService = userAppService;
    }
}

【ModelAndView】

public ModelAndView addObject(String attributeName, @Nullable Object attributeValue) {
        this.getModelMap().addAttribute(attributeName, attributeValue);
        return this;
    }
// 
public ModelMap getModelMap() {
        if (this.model == null) {
            this.model = new ModelMap();
        }

        return this.model;
    }
// ModelMap 定义
public class ModelMap extends LinkedHashMap<String, Object> {
    // ...
}


【4】ViewResolver视图解析器

1)视图解析器:通过ModelAndView中的逻辑视图名,为一级控制器DispatcherServlet返回一个视图实例;

【DispatcherServlet#resolveViewName()】

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
        if (this.viewResolvers != null) {
            Iterator var5 = this.viewResolvers.iterator();

            while(var5.hasNext()) {
                ViewResolver viewResolver = (ViewResolver)var5.next();
                View view = viewResolver.resolveViewName(viewName, locale); // 视图解析器通过视图逻辑名获取视图实例 
                if (view != null) {
                    return view;
                }
            }
        }

        return null;
    }

【BeanNameViewResolver】视图解析器具体实现

public View resolveViewName(String viewName, Locale locale) throws BeansException {
    ApplicationContext context = this.obtainApplicationContext();
    if (!context.containsBean(viewName)) {
        return null;
    } else if (!context.isTypeMatch(viewName, View.class)) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Found bean named '" + viewName + "' but it does not implement View");
        }

        return null;
    } else {
        return (View)context.getBean(viewName, View.class); // 实际上是从spring容器中根据视图名(或beanName)获取View实例对象 
    }
}

2)视图解析器接口 ViewResolver, 其常用实现类是BeanNameViewResolver,或者AbstractCachingViewResolver子类;

针对每次请求都重新实例化View可能影响性能,所以AbstractCachingViewResolver是对View做了缓存功能的视图解析器; 默认启用缓存功能;可以通过setCache(boolean)重新设置缓存开关;

【AbstractCachingViewResolver】

public void setCache(boolean cache) {
    this.cacheLimit = cache ? 1024 : 0;
}


【4.1】可用的ViewResolver实现类

1)springmvc提供的ViewResolver实现类分为2类:

  • 支持单一视图类型的视图解析器;
  • 支持多种视图类型的视图解析器;


【4.1.1】单一视图类型的视图解析器(以UrlBasedViewResolver为基类)

1)单一视图解析器:顾名思义,仅支持一种视图类型的视图解析; 基类是UrlBasedViewResolver;

2)UrlBasedViewResolver子类如下:

  • InternalResourceViewResolver :对应 InternalResourceView视图类型的解析,即处理jsp模型的视图;(默认视图解析器)
  • FreeMarkerViewResolver :对应 FreeMarker视图模版的解析;
  • XsltViewResolver:根据逻辑视图名查找并返回XsltView类型的View实例;

3)单一视图类型的视图解析器使用:

  • 使用prefix属性指定模版所在路径;
  • 使用suffix属性指定模版文件的后缀名;
  • 对应的视图解析器就可以根据 [prefix] + viewName(逻辑视图名) + [suffix] 拼接成的URL找到对应模版文件,并构造对应view实例返回;


【4.1.2】多视图类型的视图解析器

1)多视图类型的视图解析器:可以对多种视图类型的视图进行解析;

2)多视图类型的视图解析器有3个:ResourceBundleViewResolver, XmlViewResolver, BeanNameViewResolver ;

3)在dispatcher-servlet.xml可以注册多个视图解析器;(dispatcher-servlet.xml是一级控制器DispatcherServlet加载依赖组件时读取的xml配置文件,在web.xml中配置) ;

【dispatcher-servlet.xml】

<!-- 注册视图解析器bean到springweb容器(一级控制器DispatcherServlet的web容器WebApplicationContext) -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

<!-- 注册BeanNameViewResolver视图解析器到springweb容器(一级控制器DispatcherServlet的web容器WebApplicationContext) -->
<bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <property name="order" value="1" />
</bean>

4)与可以配置多个HandlerMapping类似,dispatcher-servlet.xml也可以配置多个视图解析器;

  • 通过指定order属性,设置视图解析器优先级;
  • 若没有配置视图解析器或DispatcherServlet没有在当前的WebApplicationContext中找到任何ViewResolver定义,则默认使用 InternalResourceViewResolver 作为视图解析器; (建议把InternalResourceViewResolver 优先级设置最低,作为兜底)


【5】View视图

1)View视图:是springmvc中把视图渲染逻辑从DispatcherServlet解耦出来的关键组件; 通过实现View接口, 我们可以支持多种视图渲染技术;

2)视图渲染: 通过view.render()方法实现;

  • DispatcherServlet一级控制器根据请求标识(如URL)从HandlerMapping查找对应二级控制器Controller,并把请求转发给该Controller;
  • Controller处理完成后返回ModelAndView给DispatcherServlet一级控制器;
  • DispatcherServlet一级控制器根据ModelAndView对象,通过视图解析器ViewResolver获取视图实例View;
  • DispatcherServlet一级控制器调用view.render()方法做实际的视图渲染;

【DispatcherServlet】

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
    response.setLocale(locale);
    String viewName = mv.getViewName();  
    View view;
    if (viewName != null) { // 逻辑视图名不为空 
        view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request); // 解析该逻辑视图名得到视图实例 
        if (view == null) {
            String var10002 = mv.getViewName();
            throw new ServletException("Could not resolve view with name '" + var10002 + "' in servlet with name '" + this.getServletName() + "'");
        }
    } else {
        view = mv.getView(); // 否则直接从 ModelAndView 中获取视图实例 
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");
        }
    }

    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Rendering view [" + view + "] ");
    }

    try {
        if (mv.getStatus() != null) {
            request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus());
            response.setStatus(mv.getStatus().value());
        }

        view.render(mv.getModelInternal(), request, response); // 视图渲染 
    } catch (Exception var8) {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Error rendering view [" + view + "]", var8);
        }

        throw var8;
    }
}

【AbstractView#render()】 视图渲染方法

public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (this.logger.isDebugEnabled()) {
        Log var10000 = this.logger;
        String var10001 = this.formatViewName();
        var10000.debug("View " + var10001 + ", model " + (model != null ? model : Collections.emptyMap()) + (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
    }

    Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response); // 合并模型数据 
    this.prepareResponse(request, response);
    this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response); // 渲染合并内后的模型数据 
} 


【5.1】View实现原理

1)View视图的工作: 使用相应的技术API将模版和最终提供的模型数据合并在一起, 最终输出结果页面给客户端;

【JstlView】jstl标签视图,继承InternalResourceView(jsp视图)

public class JstlView extends InternalResourceView {
    @Nullable
    private MessageSource messageSource;

    public JstlView() {
    }

    public JstlView(String url) {
        super(url);
    }

    public JstlView(String url, MessageSource messageSource) {
        this(url);
        this.messageSource = messageSource;
    }

    protected void initServletContext(ServletContext servletContext) {
        if (this.messageSource != null) {
            this.messageSource = JstlUtils.getJstlAwareMessageSource(servletContext, this.messageSource);
        }

        super.initServletContext(servletContext);
    }

    protected void exposeHelpers(HttpServletRequest request) throws Exception {
        if (this.messageSource != null) {
            JstlUtils.exposeLocalizationContext(request, this.messageSource);
        } else {
            JstlUtils.exposeLocalizationContext(new RequestContext(request, this.getServletContext()));
        }

    }
}


【5.2】View实现类(基类AbstractView)

1)View基类:AbstractView; 所有视图类都继承AbstractView ;

【AbstractView】

public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {
    public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1";
    private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;
    @Nullable
    private String contentType = "text/html;charset=ISO-8859-1";
    @Nullable
    private String requestContextAttribute;
    private final Map<String, Object> staticAttributes = new LinkedHashMap(); // 静态属性    
    ... 
    
	// 视图渲染 
    public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isDebugEnabled()) {
            Log var10000 = this.logger;
            String var10001 = this.formatViewName();
            var10000.debug("View " + var10001 + ", model " + (model != null ? model : Collections.emptyMap()) + (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
        }
        
        Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response); // 1 合并模型数据 
        this.prepareResponse(request, response); // 2 准备响应报文 
        this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response); // 3 渲染模型数据
    }

    // 合并模型数据 
    protected Map<String, Object> createMergedOutputModel(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> pathVars = this.exposePathVariables ? (Map)request.getAttribute(View.PATH_VARIABLES) : null;
        int size = this.staticAttributes.size();
        size += model != null ? model.size() : 0;
        size += pathVars != null ? pathVars.size() : 0;
        Map<String, Object> mergedModel = CollectionUtils.newLinkedHashMap(size); // 合并后的模型数据容器map
        mergedModel.putAll(this.staticAttributes); // 合并静态属性 
        if (pathVars != null) {
            mergedModel.putAll(pathVars); // 合并路径变量
        }

        if (model != null) {
            mergedModel.putAll(model); // 合并ModelAndView模型数据 
        }

        if (this.requestContextAttribute != null) {
            // 合并请求上下文数据  
            mergedModel.put(this.requestContextAttribute, this.createRequestContext(request, response, mergedModel));
        }

        return mergedModel;
    }
     protected RequestContext createRequestContext(HttpServletRequest request, HttpServletResponse response, Map<String, Object> model) {
        return new RequestContext(request, response, this.getServletContext(), model);
    }

    // 准备响应报文 
    protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
        if (this.generatesDownloadContent()) {
            response.setHeader("Pragma", "private");
            response.setHeader("Cache-Control", "private, must-revalidate");
        }

    }
}

2)AbstractView定义的视图渲染流程:

  • 合并模型数据:合并静态属性,合并路径变量,合并ModelAndView模型数据 , 合并请求上下文数据 ;
  • 准备响应报文:若是下载文件,则设置响应头;
  • 渲染模型数据:调用renderMergedOutputModel()方法渲染,但该方法是抽象方法,需要子类实现;


【5.3】AbstractView的子类AbstractUrlBasedView

1)AbstractUrlBasedView:继承自AbstractView,仅定义了一个url属性;

  • 所有需要指定url的视图都继承AbstractUrlBasedView;
  • 不需要指定url的视图继承 AbstractView(如二进制文件视图);

【AbstractUrlBasedView】

public abstract class AbstractUrlBasedView extends AbstractView implements InitializingBean {
    @Nullable
    private String url;

    protected AbstractUrlBasedView() {
    }

    protected AbstractUrlBasedView(String url) {
        this.url = url;
    }

    public void setUrl(@Nullable String url) {
        this.url = url;
    }

    @Nullable
    public String getUrl() {
        return this.url;
    }

    public void afterPropertiesSet() throws Exception {
        if (this.isUrlRequired() && this.getUrl() == null) {
            throw new IllegalArgumentException("Property 'url' is required");
        }
    }

    protected boolean isUrlRequired() {
        return true;
    }

    public boolean checkResource(Locale locale) throws Exception {
        return true;
    }

    public String toString() {
        String var10000 = super.toString();
        return var10000 + "; URL [" + this.getUrl() + "]";
    }
}


【5.3.1】使用jsp技术的view

1)使用jsp技术的view:

  • InternalResourceView ; (支持jsp技术的主要视图类)
  • JstlView;(支持jstl标签的视图类)


【5.3.2】使用通用模版技术的view

1)通用模版技术,包括 FreeMarker, Velocity, Groovy;

  • FreeMarkerView;
  • VelocityVIew;
  • GroovyMarkupView

2)FreeMarkerView 与 VelocityVIew 都继承自 AbstractTemplateView, 如下;

在这里插入图片描述



【5.3.3】面向二进制文档格式的View

1)二进制文档,包括pdf,excel, 图片等;

  • AbstractPdfView :pdf视图
  • AbstractPdfStamperView :pdf视图
  • AbstractXlsView :excel视图;
  • AbstractXlsxView :excel实体;
  • AbstractXlsxStreamingView :excel视图;
public abstract class AbstractPdfView extends AbstractView {
	public AbstractPdfView() {
        this.setContentType("application/pdf");
    }
    ...
}

public abstract class AbstractPdfStamperView extends AbstractUrlBasedView {
    public AbstractPdfStamperView() {
        this.setContentType("application/pdf");
    }
    ... 
}

public abstract class AbstractXlsView extends AbstractView {
    public AbstractXlsView() {
        this.setContentType("application/vnd.ms-excel");
    }
    ...
}

public abstract class AbstractXlsxView extends AbstractXlsView {
    public AbstractXlsxView() {
        this.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    }

    protected Workbook createWorkbook(Map<String, Object> model, HttpServletRequest request) {
        return new XSSFWorkbook();
    }
}

public abstract class AbstractXlsxStreamingView extends AbstractXlsxView {
    public AbstractXlsxStreamingView() {
    }

    protected SXSSFWorkbook createWorkbook(Map<String, Object> model, HttpServletRequest request) {
        return new SXSSFWorkbook();
    }

    protected void renderWorkbook(Workbook workbook, HttpServletResponse response) throws IOException {
        super.renderWorkbook(workbook, response);
        ((SXSSFWorkbook)workbook).dispose();
    }
}



【5.3.4】重定向视图RedirectView

1)springmvc实现重定向的两种方式

  • 方式1:新建 RedirectView,并封装到ModelAndView返回;
    • 若RedirectView的 http10Compatible属性为true: RedirectView将直接通过HttpServletResponse的sendRedirect() 方法进行重定向;
    • 否则: 通过设置http状态为303以及http header(“Location”) 达到相同目的;
  • 方式2:逻辑视图名称前使用 redirect 或 forward前缀;

【UserController】重定向

public class UserController extends AbstractController {

    private UserAppService userAppService;

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("userList", userAppService.listUser());
        // 方式1: 通过RedirectView实现重定向
//        modelAndView.setView(new RedirectView("bankCardList.do"));
        // 方式2: 逻辑视图名称前新增redirect 前缀实现重定向
        modelAndView = new ModelAndView("redirect:bankCardList.do");
        return modelAndView;
    }

    public void setUserAppService(UserAppService userAppService) {
        this.userAppService = userAppService;
    }
}

重定向效果:

在这里插入图片描述



【5.3.5】使用XSLT技术的View

1)XSLT技术: 在计算机科学中,可扩展样式表转换语言(英语:Extensible Stylesheet Language Transformations,缩写XSLT)是一种样式转换标记语言,可以将XML数据转换为另外的XML或其它格式。

  • XsltView


【5.4】自定义View实现

1)业务场景:通过springmvc的pdf视图展示pdf文件

2)自定义view,需要继承 AbstractUrlBasedView,或者 AbstractView;

【PdfUrlViewController】

public class PdfUrlViewController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setView(TomPdfView.build(getClass().getResource("/").getPath() + "/socialNetworkPropagation.pdf"));
        return modelAndView;
    }
}

【TomPdfView】自定义View类,继承 AbstractUrlBasedView

public class TomPdfView extends AbstractUrlBasedView {

    public TomPdfView(String url) {
        super(url);
        setContentType("application/pdf");
    }

    public static TomPdfView build(String url) {
        return new TomPdfView(url);
    }

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType(getContentType());
        InputStream inputStream =new FileInputStream(getUrl());
        OutputStream outputStream = response.getOutputStream();
        BusiIOUtils.copy(inputStream, outputStream);
        outputStream.flush();
        inputStream.close();
        outputStream.close();
    }
}

【dispatcher-servlet.xml】

<bean id="pdfUrlViewController" class="com.tom.springmvc.controller.file.PdfUrlViewController" />

【访问效果】

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2172937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【x**3专享#2】编译 uboot 和 kernel 的文件结构

虚拟机交叉编译环境配置及内核编译步骤说明书 请先查看前篇文章内容 1. 获取虚拟机 IP 地址 打开虚拟机终端&#xff0c;执行以下命令查看虚拟机 IP 地址&#xff1a;ifconfig或者ip addr记录 eth0 或 ens33 等网络接口的 inet 后面的 IP 地址 2. 使用 Xftp 连接虚拟机 打开…

【C++拓展(四)】秋招建议与心得

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; C拓展 1. 前言2. 今年秋招形势到底如何?3. 学历…

基于微信小程序的旅游助手的设计与实现(源码+定制+文档讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

【hot100-java】【零钱兑换】

R9-dp篇 class Solution {public int coinChange(int[] coins, int amount) {int ncoins.length;int [][] fnew int[n1][amount1];//除2防止下面1溢出Arrays.fill(f[0],Integer.MAX_VALUE/2);f[0][0]0;for (int i0;i<n;i){for (int c0;c<amount;c){if(c<coins[i]) f[i…

webpack打包Vue项目【完整版】

文章目录 前言Vue 相关包&#xff1a;项目搭建1. 初始化项目2. 安装 Vue 和 Webpack3. 创建目录结构4. 创建文件项目5. 配置 Webpack6. 配置 Babel7. package.json8. 打包和运行 前言 基于 上一篇 webpack 的配置详解 &#xff0c;我们已经知道了 webpack 在项目中的常用的配置…

IP地址与智能家居能够碰撞出什么样的火花呢?

感应灯、远程遥控空调&#xff0c;自动感应窗帘——智能家居已经在正逐步走入我们的生活&#xff0c;为我们带来前所未有的便捷与舒适体验。而在这一进程中&#xff0c;IP地址又能够与智能家居碰撞出什么样的火花呢&#xff1f; 一、IP地址&#xff1a;智能家居的连接基石 智…

SOLIDWORKS 2025新版本揭秘 | 设计、协作和数据管理篇

除非另有说明&#xff0c;否则所有增强功能均适用于 3DEXPERIENCE SOLIDWORKS 和 SOLIDWORKS Cloud Services。 1&#xff5c;更髙效的协作和数据管理 直接从 SOLIDWORKS 访问社区&#xff0c;轻松与业界同行建立联系并开展协作。 利用实时通知时刻关注蕞新动态&#xff0c;…

基于Hive和Hadoop的图书分析系统

本项目是一个基于大数据技术的图书分析系统&#xff0c;旨在为用户提供全面的图书信息和深入的图书销售及阅读行为分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以…

腾讯云SDK产品功能

本文主要介绍音视频终端 SDK&#xff08;腾讯云视立方&#xff09;的核心功能。 直播推流 音视频终端 SDK&#xff08;腾讯云视立方&#xff09;为终端直播场景提供强大的 RTMP、RTC 推流能力&#xff0c;配合云直播&#xff08;CSS&#xff09;全球布局的2000节点&#xff0…

山丹县综能智慧新能源:“智能二维码”,推动班组管理信息化

近日&#xff0c;为了提升管理效率&#xff0c;国电投建业光伏电站将二维码引入设备巡视和班组建设中。 首先&#xff0c;使用传统纸质巡视作业卡&#xff0c;巡视工作强度大&#xff0c;容易出现错误&#xff1b;此外&#xff0c;“三会一活动”和培训记录等班组建设过程材料…

DCDC电源PCB设计

环路 高频电流环路面积越小越好&#xff1a; 电感所在的支路不属于高频电流环路 对于BUCK电路&#xff0c;电容Ci的越靠近芯片Vi引脚越好&#xff0c;D1越靠近SW引脚越好&#xff0c;同时另一端越靠近芯片GND引脚越好&#xff1a; 示例&#xff1a; 然后是布局输出&#…

一次性使用唾液采集器:唾液样本常温收集保存及运输的较佳选择工具!

一次性使用唾液采集器确实是进行唾液样本常温收集、保存及运输的较佳选择工具之一&#xff0c;尤其适用于需要非侵入性、便捷且成本效益高的样本采集场景&#xff0c;如基因检测、药物监测、疾病筛查等领域。以下是其作为较佳选择的几个主要原因&#xff1a; 非侵入性采集&…

Linux安装tomcat及配置环境变量超详细教程

微服务Linux解析部署使用全流程 linux系统的常用命令 Linux安装vim超详细教程 Linux安装JDK及配置环境变量超详细教程 1、上传压缩包 统一创建目录&#xff1a;/usr/local/tomcat&#xff0c;将压缩包上传到这个目录下。拖动文件到这个目录下即可。 2、执行解压命令 先进…

ESP01S连接新版OneNET物联网平台

ESP01S连接OneNET ESP01S模块连接新板OneNET物联网平台OneNET创建产品创建设备创建物模型 ESP01S1、烧录MQTT固件2、发送AT指令连接到OneNET1、AT2、ATCWMODE13、ATCWDHCP1,14、ATCWJAP"WIFI名称","WIFI密码"5、ATMQTTUSERCFG0,1,"设备名称",&qu…

18.Linux-配置DNF仓库

DNF仓库产生背景 在现实的场景中&#xff0c;我们经常要安装一些软件包&#xff0c;但由于现场不提供网络。 需要使用光盘或文件下载的方式去安装。 对于linux有两种离线安装方式&#xff1a;二进制文件安装和源码安装 其中二进制文件是比较简单的安装方式&#xff0c;不同的l…

【IAPP源码】仿奇异社区UI

【IAPP源码】仿奇异社区UI 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89801612 更多资源下载&#xff1a;关注我。

【Linux网络】详解TCP协议(2)

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; Linux网络 &#x1f389;其它专栏&#xff1a; C初阶 | C进阶 | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 TCP协议的三次握手和四次挥手 的相关内容。 如果看到最后您觉得…

AT89C51单片机和STC单片机烧录不同引脚问题

首先确定一下&#xff0c;两种烧录接口引脚不同 STC烧录器主要使用串口引脚 实际上stm32中也可以使用这种UART通信方式烧录程序&#xff0c;只是需要确定连接引脚进入bootloader模式 AT89C51来源Atmel公司&#xff0c;其中AVR单片机也是这个公司 ISP和SPI不是一个概念&…

C++不同的头文件中各种函数的操作使用(长期更新,找到新的就补充进来)

一、万能头文件 #include <bits/stdc.h> 万能头文件中包含的内容 // C #ifndef _GLIBCXX_NO_ASSERT #include <cassert> #endif #include <cctype> #include <cerrno> #include <cfloat> #include <ciso646> #include <climits> #in…

智慧城市交通管理中的云端多车调度与控制

城市交通管理中的云端多车调度与控制 智慧城市是 21世纪的城市基本发展方向&#xff0c;为了实现智慧城市建设的目标&#xff0c;人们需要用现代化的手段去管理和控制城市中的各种资源和设施。智能交通控制与管理是智慧城市中不可缺少的一部分&#xff0c;因为现代城市交通系统…