【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://developer.aliyun.com/article/1634958
出自【进步*于辰的博客】
1、什么是SpringMVC?
参考笔记二,P5.8。
1.1 概述
总结:前端控制器负责接收请求,处理器映射器负责寻找处理器,处理器适配器负责执行处理器(返回ModelAndView),视图解析器负责对ModelAndView的渲染(包括处理访问路径,如:请求转发路径)。
SpringMVC在底层会对请求路径进行处理(如:去除.do
后缀),如请求路径是query.do
,处理器映射器会寻找路径是query.do
与query
的处理器。
如果大家想深入学习SpringMVC,我暂未系统整理相关内容,大家可查阅博文《Spring MVC详解(学习总结)》(转发)。
1.2 示例
1、配置前端控制器。(web.xml)
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name><!--配置文件-->
<param-value>classpath:config/spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/*.do</url-pattern><!--拦截请求配置-->
</servlet-mapping>
2;配置处理器映射器、处理器适配器。
<mvc:annotation-driven /><!--精简配置-->
3、配置视图解析器。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"> </property><!--前缀-->
<property name="suffix" value=".jsp"></property><!--后缀-->
</bean>
会根据控制层方法返回的字符串查找相应的视图(如:jsp文件),prefix/suffix
用于在前后进行拼缀,生成文件路径。
6、restful风格
参考笔记二,P10.3。
注:下文中“restful控制器”指使用了restful风格配置的控制器。
6.1 介绍
常规请求方式:
https://www.vatapi.com/book/query.do?page=12&pageSize=20
restful风格请求方式:
https://www.vatapi.com/book/query/12/20
显而易见,restful风格的请求方式简洁、安全性高。(PS:谁知道哪个值对应哪个参数,代表什么意思)
要把常规请求方式转换成restful风格,需要三步:
- 在web.xml中把前端控制器的
url-pattern
的值由/*.do
改为/
。(一般在修改成restful风格前,url-pattern
的值是/*.do
,表示拦截所有动态请求)
注:“拦截”指处理器映射器寻找控制器的过程。- 在 @RequestMapper 中,在路径后加
{a}
,比如上述的query.do
,@RequestMapper 内的值是“query
”,现在改成“query/{a}
”; - 在 @RequestMapper 注解的方法形参前加
@PathVariable
,用于映射参数值。
- 在 @RequestMapper 中,在路径后加
举个栗子:
那请求就是:
${
pageContext.request.contextPath}/book/queryBySyllabusId/12
6.2 使用细节
1:关于第一步
拦截的路径在修改前是/*.do
,表示只拦截以.do
结尾的请求。(一般在请求后加.do
,表示访问动态资源。比如login.do、query.do
)
将路径修改为/
后,表示拦截所有请求,也包括类似login.do
这样的动态资源请求。那问题来了,现在拦截路径是/
,而请求是login.do
,为什么处理器映射器还能找到@RequestMapper("login")
这个控制器?
从SpringMVC的源码以及一些资料中得知:
SpringMVC在底层会去除
login.do
中的.do
(后缀)。(在【概述】中有说到)
2:关于请求数据封装机制
restful风格完全抛弃了SpringMVC的请求数据封装机制(映射封装),如上述:https://www.vatapicom/book/query/12/20
中的12、20
,并没有参数与其绑定,映射关系完全取决于控制器形参前@PathVariable
注解内的值。
3:补充第一点
SpringMVC底层会排除掉请求后的后缀。因此,对于访问非restful控制器,在请求时加不加后缀.do
都可行(不过,需要注意拦截器)。
因此,restful风格的配置不会影响其他非restful控制器,对于其他非restful控制器的访问不用修改。但是,需要注意的是,==访问restful控制器时不能加后缀==,如:
https://www.vatapi.com/book/query/12/20 √
https://www.vatapi.com/book/query/12/20.do ×
4:关于静态资源的访问
关于静态资源配置,推荐一篇博文《SpringMVC访问静态资源 mvc:resources》(转发)。
由于restful风格的配置会拦截所有资源,也包括静态资源(如:image、css、js)。
https://www.vatapi.com/gd-book/28282718.jpg // 找不到图片
https://www.vatapi.com/css/menu.css // 找不到css,页面没有样式
https://www.vatapi.com/js/index.js // 找不到js,页面没有功能
这样的请求是访问一张图片,但处理器映射器会把他当作一个对控制器的访问去寻找控制器,而不会去寻找图片,自然无法找到图片。因此,需要对静态资源做配置。:
<mvc:resources location="/static/" mapping="/static/**" /> // 举例
5:关于拦截器
在配置拦截器时,若拦截的是restful控制器,{}
内不需要一致。
示例:
<mvc:mapping path="/commonQuestion/queryByQuestionId/{questionId}"/>
{questionId}
内的questionId
可任意名称,不过,必须带上{}
。
扩展一点:
上文说道:
SpringMVC在底层会去除
login.do
中的.do
(后缀)。
而拦截器不会,如:
<mvc:mapping path="/commonQuestion/query.do"/>
拦截的是请求/commonQuestion/query.do
,而/commonQuestion/query
则不会被拦截。(简言之,路径必须一致)
7、全局异常解析器
参考笔记二,P11.4。
为了便于捕获和处理异常,可将异常抛给框架,交由异常解析器对象统一处理。示例:
1、创建全局异常解析器。
public class ExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse resp,
Object obj, Exception ex) {
ModelAndView mv = new ModelAndView();
// 异常处理业务:如:添加异常日志、请求转发至异常提示页面
...
return mv;
}
}
2、注入IOC容器。
<bean class="com.neusoft.exception.ExceptionResolver"/>
当出现异常时,会自动调用resolveException()
,参数ex
是异常对象。
也可以自定义异常类,用于抛出异常。
注意:无论异常是由我们处理(try...catch...
),还是抛出异常(throw new Exception()
),全局异常解析器都会捕获(调用resolveException()
)。
8、自定义类型转换器
参考笔记二,P11.5。
SpringMVC具有很强大的数据封装机制,但并不是“万能”的。
如:使用JSP文件的<form>
表单进行提交,除<input type="file">
之外(输出类型是二进制),其他<input>
都只有字符串和数字这两种。包括<input type="date">
(输出日期字符串)。
而数据库相应字段是时间类型(一般这种情况实体属性也是时间类型),如此SpringMVC将无法对“时间”数据进行封装。因此,需要我们手动配置。
示例:
1、创建类型转换类。
public class DateConvert implements Converter<String, Date> {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date convert(String dateStr) {
Date date = new Date();
try {
if (dateStr == null) {
throw new Exception("时间为空,无法转换");
}
date = sdf.parse(dateStr);
} catch (Exception e) {
e.printStackTrace();
}
return date;
}
}
2、配置类型转换器。
<bean id="conversionFactory" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.neusoft.convert.DateConvert"></bean>
</set>
</property>
</bean>
当<form>
表单提交时,<input type="date">
提交的“时间”字符串会自动传入dateStr
,转换成 Date 进行数据封装。
最后
本文中的例子是为了方便大家理解和阐述知识点而简单举出的,旨在阐明知识点,并不一定有实用性,仅是抛砖引玉。
本文持续更新中。。。