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):插件&…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...