Spring MVC补充
Spring MVC框架中的细节:
1、静态资源的访问权限
由于Spring MVC的前端控制器DispatcherServlet中配置的路径是/,表示缺省的servlet, 意味着当前端访问servlet的时候,当找不到,就会匹配该前端控制器的 DispatcherServelt。
由于当使用spring mvc框架的时候,项目中就不需要编写Servlet了(JSP本质上也是 Servlet),因此当前端的请求来了会与jsp页面进行匹配,然后就会进入前端控制中。
思考:
1)如果前端访问的资源是静态资源呢?
经过测试,如果使用spring mvc框架配置了前端控制器,jsp页面仍旧可以直接访 问,但是静态资源(html\css\javascript)无法访问。这是由于访问静态资源的请求 在匹配不到jsp之后会进入前端控制器与方法进行映射,没有找到则会报错。
2)针对1)的问题该如何解决?
可以进行如下配置:
<mvc:resource mapping=”/js/**” location=”/js/”>
该配置表示当请求url中包含”/js/**”时,就去/js/路径去匹配相应的资源,而不进 入前端控制器
还可以配置<mvc:default-servlet-handler/>,该配置表示如果前端控制器没有匹配到 相应的资源,则采用原始的tomcat容器进入资源匹配。
2、请求数据的乱码问题
当前端使用post请求发送数据,如果数据包含中文时,后端接收数据会出现乱码问题。
(get方式的请求,乱码问题tomcat内部已经解决)
1)如果采用request对象获取数据:
request.setCharacterEncoding(“utf-8”);
2)如果采用spring mvc框架进行前端控制,不需要直接操作request对象了,可以配置 一个过滤器,所有的请求来了都进入该过滤器并将编码设置成UTF-8即可。
备注:url-pattern是/*表示所有的请求都进入该filer,优先级高,与1中的/相比,该路 径表示缺省,优先级低,是找不到再匹配该路径
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3、请求参数的自动封装
当前端请求进入前端控制器之后,前端控制器从springmvc容器中获取对象并调用方法,这就涉及到请求中的参数与方法参数之间的一个映射关系。
请求中的参数是都是string类型的键值对,方法中的参数按类型可以分为是基本数据类型、对象、数组、集合。
1)当方法中的参数是基本数据类型
请求中的参数自动按相同的参数名称进行映射(请求中key值与方法形参相同时, value值作为实参传入方法)。
2)当方法中的参数是普通的java对象
请求中的参数自动按java对象相同的属性名称进行映射,将请求参数封装进一个 java对象,并将该对象作为实参传入方法。
3)当方法中的参数类型是数组
请求中的参数如果中存在相同的key对应不同的value值,将会自动将对应的key 组成一个数组,传给方法中与key值相同的形参。
4)当方法中的参数是集合
从上面1)和2)可知,springmvc参数的映射要么是请求的key值与形参相同,与某个对象的属性值相同,然后框架通过反射调用set方法进行封装的。
因此如果想要将一个前端请求的参数封装进行一个集合中,可以编写一个类,这个类定义一个集合的成员变量,然后方法的形参是与该成员变量相同,则框架底层会尝试将前端的数据封进该对象的属性(也就是集合)。问题是前端页面的参数只能是键值对,如何与集合中的元素对应起来?
因此,在前端页面的key值不仅要指定要属性名(key值与对象的成员变量相同),还要指定封进入集合中的那个位置(采用索引),还需要指定需要封进集合中该位置上对象的那个属性。格式如下:
<input type=”text” name=”userList[0].name” placeholder = “请填入姓名”>
备注:重点在userList[0].name的书写,如方法中的参数为UserList userList,UserList类中有个private List<User> userList成员变量,此时需要将前端请求的参数封装进UserList对象,所以前端页面key值首先写userList,表示要封进UserList对象的userList属性中,由于该属性是一个集合,然后写[0]表示封进索引为0的元素中,然后写name表示封进索引为0的元素的name属性中(User对象有name属性)。
备注:如果前端页面通过adjax请求直接发送一个json格式的数据到Controller层的方法,可以在该方法中的集合形参前面加上@RequestBody,表示将该adjax请求的数据直接封进集合中。
4、SpringMVC RESTfu风格的请求
在spring mvc中可以使用占位符来接收url路径上的值,将该值当作参数传递给占位符, 例如:
访问的url为:http://localhost:8080/test/demo/zhangsan
controller层:
@Controller
@RequestMapping(“/test”)
public class ControllerDemo{
@RequestMapping(“/demo/{name}”)
public void test(@PathVariable(value=”name”,required =”true”) String username){
System.out.println(name);
}
}
上述代码表示,@RequestMapping(“/demo/{name}”)中使用{name}作为占用符,用于接 收匹配到的url路径上的值,访问的是/test/demo/zhangsan,匹配到的路径是 “/test/demo/{name}”,其中占位符name会接受zhangsan这个参数,然后在方法的参数 列表中,@PathVariable(value=”name”) String username表示将占位符name获得的值给 username,其中required是true表示,路径必有与占位符匹配上。
5、自定义转换器
(定义一个类实现Converter接口,指定泛型<T,S>表示将T类型的转成S类型,复写 conver方法,然后在spring-mvc.xml中配置ConversionServiceFactoryBean的Bean对象, 将编写的转换器放入converters属性中,然后再在注解驱动中表示)
前端请求到达服务器端都是字符串类型的,但是已知当前端请求匹配到方法之后,会进 行数据的自动封装,而且封装的不一样是字符串,例如url中包含参数age=23,方法中 含有参数int age,此时字符串型的”23”能被封进整型的age中,这是由于spring mvc中 内置了默认的类型转化器,能自动实现类型转换。但是内置的默认类型转换器有时候并 不能满足需求,也可以自定义类型转换器,可以设置在遇到指定类型的数据时,让spring mvc按照指定的方法进行转换。
譬如:当url中包含date=2020-02-02时,内置的默认转换器无法将”2020-02-02”转成Date 对象,可以进行自定义。
spring mvc中提供了Converter接口,编写一个转换器实现该接口,该接口有泛型<T,S>,T 表示需要被转换的类型,在url中的类型都是String,因此这里的T应该是String,而S 表示需要转成的类型,此处需要将String转成Date,这个S表示Date。
public class DateConverter implements Converter<String,Date>{
public Date conver(String str){
SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”);
Date date = format.parse(str);//这里会抛异常,try...catch...包裹或直接抛
return date;
}
}
然后在spring-mvc.xml进行配置:
<bean id = “converter” class=”ConversionServiceFactoryBean类全包名”>
<property name = “converters”>
<list>
<bean class = “自定义转换器的全类名”>
</list>
</property>
</bean>
<mvc:annotation-driven conversion-service =”converter”>
6、文件上传
备注:将前端页面的文件封装成一个MulitiPartFile对象并自动与方法中的形参进行匹 配,底层依赖commons-fileupload和commons-io,因此需要导包。
文件上传三要素:
1、表单项type=”file”;
2、表单的提交方式为post;
3、表单的enctype属性是多部分表单形式,即enctype=”multipart/form-data”
<form action=${pageContext.request.contextPath}/test method = “post”
enctype=”multipart/form-data”>
名称:<input type =”text” name=”name”><br>
文件:<input type=”file” name=”file”><br>
<input type=”submit” value=”提交”>
</form>
Spring MVC有CommonsMultipartResolver组件,可以将前端页面的文件封装成 MultipartFile对象。
CommonsMultipartResolver组件的配置:
<bean id = “multiPartResover” class =”CommonsMultipartResolver全类名”>
<property name=”defaultEncoding” value=”UTF-8”>
<property name=”maxUploadSize” value=”50000”>
</bean>
7、Spring MVC拦截器
(采用aop思想,当请求来了之后,在控制器方法执行之前、执行后返回视图前、返回 视图后,进行统一的逻辑操作)
spring mvc中的拦截器类似与filter,是针对前端发来的请求进行拦截并且只会拦截控制 器中的方法(html\css\javascript\jsp)不会拦截,采用aop的思想,可以在请求到达控 制器方法之前、控制方法执行之后视图返回之前和视图放回之后进行相应的操作。
自定义拦截器的步骤:
1)定义一个类实现HandlerInterceptor接口;
2)复写preHandle方法表示在请求到达控制器方法之前执行;
3)复写postHandle方法表示控制器方法执行之后视图返回之前执行;
4)复写afterCompletion方法表示视图返回之后执行。
5)上面三个方法按需要复习,也可以不复写。
6)在spring-mvc.xml中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path=”/**”>
<mvc:exclude-mapping path=”指定路径uri”>
<bean class=”编写的拦截器的全类名”/>
</mvc:interceptor>
</mvc:interceptors>
备注:如果配置了多个拦截器,则按配置的路径依次执行。
8、Spring MVC中的异常处理
(针对不同的异常显示不同的页面)
web项目中dao、service和controller层中都有可能出现异常,一旦出现异常,就应该 向前端页面显示指定的错误页面(避免把程序中的异常直接显示在前端页面)。在编写 dao\service\controller三层的时候,异常都直接抛出。spring mvc提供了异常处理器 HandlerExceptionResolver,可以在spring-mvc.xml中进行配置。
Spring MVC异常处理器:
1、spring mvc内置的默认异常处理器SimpleMappingExceptionResolver
2、自定义异常处理器:
步骤:
1)定义一个类实现HandlerExceptionResolver接口;
2)复写resolveException方法;
3)在spring-mvc.xml中配置(配置该类的Bean对象即可)。