spring揭秘24-springmvc02-5个重要组件
文章目录
- 【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揭秘》,作者王福强,非常棒的一本书,墙裂推荐;
代码详情参见: springmvcDiscoverFirstDemo【github】
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 */@DeprecatedString 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;}@NullableHandlerExecutionChain 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 {@NullableModelAndView 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;}@Nullablepublic 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); // 处理请求内部逻辑}}@Nullableprotected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
【UserController】
public class UserController extends AbstractController {private UserAppService userAppService;@Overrideprotected 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;@Overrideprotected 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 {@Nullableprivate 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;@Nullableprivate String contentType = "text/html;charset=ISO-8859-1";@Nullableprivate 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); // 合并后的模型数据容器mapmergedModel.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 {@Nullableprivate String url;protected AbstractUrlBasedView() {}protected AbstractUrlBasedView(String url) {this.url = url;}public void setUrl(@Nullable String url) {this.url = url;}@Nullablepublic 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;@Overrideprotected 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 {@Overrideprotected 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);}@Overrideprotected 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" />
【访问效果】
相关文章:

spring揭秘24-springmvc02-5个重要组件
文章目录 【README】【1】HanderMapping-处理器映射容器【1.1】HanderMapping实现类【1.1.1】SimpleUrlHandlerMapping 【2】Controller(二级控制器)【2.1】AbstractController抽象控制器(控制器基类) 【3】ModelAndView(模型与视…...
关键字:register
1.铺垫 1.1 计算集中具有存储能力的硬件:cpu中的寄存器、cache,内存,硬盘等 1.2离cpu越近的存储硬件,效率越高,单价成本越贵;离cpu越远的存储硬件,效率越低,单价成本越便宜&#x…...

力扣 简单 110.平衡二叉树
文章目录 题目介绍解法 题目介绍 解法 平衡二叉树:任意节点的左子树和右子树的高度之差的绝对值不超过 1 //利用递归方法自顶向下判断以每个节点为根节点的左右子树的最大深度是否大于1 class Solution {public boolean isBalanced(TreeNode root) {if(root null){return tr…...
基于深度学习的代码优化
基于深度学习的代码优化是一种使用深度学习技术来提升编程代码性能、减少运行时间或资源消耗的方式。通过模型学习大量代码的特征和结构,深度学习可以帮助自动化地识别和应用优化策略。以下是一些关键应用领域: 编译器优化:深度学习模型可以用…...
汽车电气系统中KL30、KL15、KL50、KLR、KL31、KL87、KL75的作用
目录 1、KL30 (Battery Positive Terminal) 2、KL15 (Ignition Switch, Positive) 3、KL50 (Starter Motor Terminal) 4、KLR (Ignition-Off Draw) 5、KL31 (Ground) 6、KL87 (Relay Output) 7、KL75 (Accessory) 在汽车电气系统中,KL系列的术语起源于德国&a…...

随笔(四)——代码优化
文章目录 前言1.原本代码2.新增逻辑3.优化逻辑 前言 原逻辑:后端data数据中返回数组,数组中有两个对象,一个是属性指标,一个是应用指标,根据这两个指标展示不同的多选框 1.原本代码 getIndicatorRange(indexReportLi…...

安装管理K8S的开源项目KubeClipper介绍
安装管理K8S的开源项目KubeClipper介绍 1. 概述 KubeClipper是九州云开源的一个图形化界面 Kubernetes 多集群管理工具,旨在提供易使用、易运维、极轻量、生产级的 Kubernetes 多集群全生命周期管理服务。让运维工程师从繁复的配置和晦涩的命令行中解放出来&#…...

北交大研究突破:塑料光纤赋能低成本无摄像头AR/VR眼动追踪技术
北交大研究:探索无摄像头低成本AR/VR眼动追踪新路径 在AR/VR技术领域,眼动追踪作为一项关键技术,对于提升用户体验、优化渲染效率具有重要意义。然而,传统的眼动追踪方案多依赖于高成本的摄像头,这不仅增加了设备的制造…...
算法题总结(七)——哈希表
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法 242、有效地字母异位词 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t…...

PS批量执行动作,ps批量修改图片大小,并修改文件的类型
PS批量执行动作,ps批量修改图片大小,并修改文件的类型 修改格式,文件类型为:jpg,psd,tiff,并修改大小 打开文件(也可以不打开,) 点击文件>脚本>文件…...

CentOS 替换 yum源 经验分享
视频教程在bilibili:CentOS 替换 yum源 经验分享_哔哩哔哩_bilibili问题原因 解决方法 1. 进入镜像目录 [rootlocalhost ~]# cd /etc/yum.repos.d/ 2.备份文件 [rootlocalhost yum.repos.d]# rename repo bak * 3.寻找阿里镜像源复制 https://developer.aliyun.com/mirror/ …...
Elasticsearch基础_2.数据类型
文章目录 一、基本的数据类型1.1、keyword1.2、text1.3、数值类型1.4、布尔类型1.5、时间类型 二、复杂的数据类型三、字段映射 一、基本的数据类型 1.1、keyword keyword类型是不进行切分的字符串类型。这里的“不进行切分”指的是:在索引时,对keyword…...

docker快速安装ELK
一、创建elk目录 创建/elk/elasticsearch/data/目录 mkdir -p /usr/local/share/elk/elasticsearch/data/ 创建/elk/logstash/pipeline/目录 mkdir -p /usr/local/share/elk/logstash/pipeline/ 创建/elk/kibana/conf/目录 mkdir -p /usr/local/share/elk/kibana/conf/ 二、创建…...

GS-SLAM论文阅读笔记-CaRtGS
前言 这篇文章看起来有点像Photo-slam的续作,行文格式和图片类型很接近,而且貌似是出自同一所学校的,所以推测可能是Photo-slam的优化与改进方法,接下来具体看看改进了哪些地方。 文章目录 前言1.背景介绍GS-SLAM方法总结 2.关键…...

15分钟学 Python 第36天 :Python 爬虫入门(二)
Python 爬虫入门:环境准备 在进行Python爬虫的学习和实践之前,首先需要准备好合适的开发环境。本节将详细介绍Python环境的安装、必要库的配置、以及常用工具的使用,为后续的爬虫编写奠定坚实的基础。 1. 环境准备概述 1.1 为什么环境准备…...

Spring:强制登陆与拦截器
1.只使用session验证 (1)第一步:用户登陆时存储session ApiOperation("用户登陆") PostMapping("/login") public AppResult login(HttpServletRequest request,RequestParam("username") ApiParam("用…...

MySQL-数据库约束
1.约束类型 类型说明NOT NULL非空约束 指定非空约束的列不能存储NULL值 DEFAULT默认约束当没有给列赋值时使用的默认值UNIQUE唯一约束指定唯一约束的列每行数据必须有唯一的值PRIMARY KEY主键约束NOT NULL和UNIQUE的结合,可以指定一个列霍多个列,有助于…...

线性表三——队列queue
#include<bits/stdc.h> using namespace std; int n,m; queue<int> q;int main(){cin>>n>>m;for(int i1;i<n;i) q.push(i);int k0;while(!q.empty()){k;if(k<m)//从队头出来,再次回到队尾{int idq.front();//记录出去的编号 q.pop();…...

算法笔记(十)——队列+宽搜
文章目录 N 叉数的层序遍历二叉树的锯齿形层序遍历二叉树最大宽度在每个树行中找最大值 BFS是图上最基础、最重要的搜索算法之一; 每次都尝试访问同一层的节点如果同一层都访问完了,再访问下一层 BFS基本框架 void bfs(起始点) {将起始点放入队列中;标记…...
webpack配置全面讲解【完整篇】
文章目录 前言webpack 核心包:配置文件导出三种方式:在线配置 webpack配置文件解析:入口(Entry):输出(Output):加载器(Loaders):插件&…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...