1. servlet
1.1配置servlet
SpringMVC中, 只有一个servlet,就是DispatcherServlet, 所有的请求,都通过这个servlet转发到controller, 我们需要在web.xml中配置一个servlet指向DispatcherServlet。
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 下面这个mvc.xml文件是我放在resources目录下的一个配置springmvc的文件-->
<param-value>classpath:mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
1.1 设置url-pattern的三种方式
从上面, 我们可以看到在servlet-mapping中有一个url-pattern, 这个属性的意思是,匹配什么规则的url进来执行springmvc的逻辑。那么设置规则,常用的有三种方式:
- 设置为
*.do``,*.action
访问以do
或者action
结尾的所有路径都可以进行映射,比如访问xxx.do
,aaa.do
- 设置为
/
这种就是匹配所有的路径,但是不会拦截jsp文件 - 设置
/**
这种会匹配所有的路径
使用最后面2种,都会有一种问题, 那就是静态资源(css,js,html等)也会拦截,那么怎么解决了? 有如下2中方案:
- 在mvc.xml中配置默认的servlet处理器,让dispatcherServlet不处理静态资源,直接交给tomcat处理
<mvc:default-servlet-handler></mvc:default-servlet-handler>
- 在mvc.xml中配置resources,指定以某个字符串开头的都定位到某个目录下面去(资源目录)
<mvc:resources mapping="/resources/**" location="classpath:/"></mvc:resources>
2. 请求参数
请求路径的映射需要使用到@RequestMapping注解, 这个注解可以标注在类上, 也可以标注在方法上,实际开发中, 一般是在类上和方法上同时使用, 在类上使用填写模块名, 方法上使用填写具体的方法名, 那么完整的请求路径就是类上的路径和方法上的路径拼接而成。
2.1 简单类型的绑定
获取简单类型的数据, 只需要在方法的形参中,直接定义就可以接受,注意需要形参名
和前端传过来的参数名一致
才可以接受得到。
@RequestMapping("/test5")
public void test5(String name) {
System.out.println(name);
return;
}
如果说,前端是个比较有个性的小伙, 就是不按你的命名来, 但是你又想使用自己的命名规则, 你就可以使用@RequestParam注解在转换一下
@RequestMapping("/test5")
// 前端传过来的名为username, 用@RequestParam("username")接受后, 定义为name
public void test5(@RequestParam("username") String name) {
System.out.println(name);
return;
}
2.2 时间类型的绑定
时间类型的转换, 不能像基础类型一样,直接定义为Date类型的变量就可以直接转换, 因为前端传过来的是一个字符串,那么我们需要做的是把字符串手动转换为Date类型。 怎么做了? SpringMVC为我们提供了Converter接口, 我们只需要实现这个接口,就可以完成字符串到Date的转化。
// Converter<String, Date> string表示需要被转化的变量类型, Date表示转化之后的类型
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
// 转化逻辑
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(s);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
配置完上面的转化器, 我们还需要在mvc.xml中配置一个格式转化器的bean,并把我们写的转换器通过赋值的形式传递进去。
<bean id="conversionServiceBean"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.sherlock.converter.DateConverter"></bean>
</set>
</property>
</bean>
最后我们就可以在contorller中直接使用了
@RequestMapping("/test7")
public void test7(Date date) {
System.out.println(date);
}
2.3 POJO类型的绑定
我们只需要在方法的形参上面, 直接定义为需要接受的POJO类型就可以了,比如我们有一个user的pojo类,我们需要接受一个user类型的数据,我们可以这样写
public class User {
private String name;
private Integer age;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
// controller中获取
@RequestMapping("/test6")
public void test6(User user) {
System.out.println(user);
}
2.4 POJO包装对象的绑定
这个跟pojo对象一样, 需要注意的就是,前端传值的方式.
public class Address {
private String city;
@Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
'}';
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
- 以
x-www-form-urlencode
方式传值,以上面user对象为例, 需要传address.city
这个字符串作为key
,后台才能接受。 - 以
application/json
方式储值,以上面对象为例,需要传{“address”:{“city”}}这样填写,后台才能正常接受并赋值
2.5 RESTFUL风格的参数绑定
restful风格,最主要的路径特定就是,路径中会有可变的参数,比如/user/1,/user/2,那么后台如何获取这个1或者2了,可以使用@PathVariable
这个注解来获取。 比如:
@RequestMapping("/user/{id}")
public void test5(@PathVariable("id") Integer id) {
System.out.println(id);
return;
}
3. 返回类型
3.1 返回视图
3.1.1 绑定ModelAndView
@RequestMapping("/test1")
public ModelAndView test1(ModelAndView modelAndView) {
//向model中放入数据
modelAndView.addObject("name", "sherlock");
// 设置需要跳转的页面
modelAndView.setViewName("test");
return modelAndView;
}
在上面为什么setViewName可以直接写test
,而不写/WEB-INF/jsp/test.jsp
,那是因为在mvc.xml中配置视图的前后缀,springmvc会自动帮我们拼接。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
3.1.2 绑定ModelMap
@RequestMapping("/test2")
public String test2(ModelMap modelMap) {
modelMap.addAttribute("name", "sherlock");
return "test";
}
3.1.3 绑定Model
@RequestMapping("/test4")
public String test4(Model model) {
model.addAttribute("name", "sherlock");
return "test";
}
3.1.4 绑定Map
@RequestMapping("/test3")
public String test3(Map<String, Object> map) {
map.put("name", "sherlock");
return "test";
}
3.1.5 ModelAndView,ModelMap,Model,Map的联系
上面的4种写法,都可以得到同样的结果。为什么可以得到同样的结果了,我们来分析一下。
1.ModelAndView
这个类中,有两个属性,其中有一个就是ModelMap
类型的。
2.ModelMap继承了LinkedHashMap
, 而LinkedHashMap继承了HashMap,实现了map
3.ModelMap
,Model
,Map
在运行中, 都是BindingAwareModelMap
类型
4.BindingAwareModelMap
继承了ExtendedModelMap
, ExtendedModelMap
继承了ModelMap
实现了Model
3.2 返回json字符串
需要返回json字符串,首先就需要引入json的依赖了, springmvc默认是使用jackson,所以使用引入jackson的依赖。
<!-- 引入的依赖版本不能太高,我最开始是引入的最新的,后台编译的时候,
报错了,报错的原因是因为jdk版本不匹配,所以就降低了版本号-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
然后需要在方法上或者类上@ResponseBody
注解,然后返回的类型直接为POJO类型就可以了。
@ResponseBody
@RequestMapping("/test9")
public User test9() {
User user = new User();
user.setAge(12);
user.setName("12");
return user;
}
4. 高级应用
4.1过滤器
过滤器这个层面是再tomcat层的, 所以配置文件需要写在web.xml中。我们以一个实际的需求为例, 在post请求中,如果传递到后端的值有中文,使用默认的编码的话,会是乱码,所以我们需要转化编码为UTF-8,那么我们就可以使用过滤器来配置一下。
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern> <!-- 拦截所有的路径-->
</filter-mapping>
4.2拦截器
拦截器是springmvc中的,会有三个时机可以拦截, 第一个是方法执行前
, 第二个是方法执行后
,第三个是视图完成之后
。那么对于这三个时机,我们在时机项目中, 最常见的是使用第一个在方法执行前, 我们可以做一些权限校验。主要是实现HandlerInterceptor这个接口。
public class MyIntercept01 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("01===>preHandle");
// 实际开发中,我们再这里进行权限判断
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("01===>postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("01===>afterCompletion");
}
}
然后我们还需要在mvc.xml中配置这个监听器才会生效
<mvc:interceptors>
<!-- 可以配置多个-->
<mvc:interceptor>
<mvc:mapping path="/**"/> <!-- 拦截的路径-->
<mvc:exclude-mapping path="/test"/><!-- 排序的路径-->
<bean class="com.sherlock.intercept.MyIntercept01"></bean> <!-- 自定义的拦截器-->
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.sherlock.intercept.MyIntercept02"></bean>
</mvc:interceptor>
</mvc:interceptors>
当我们配置多个的时候,拦截器的执行顺序,跟配置的顺序相关. 具体方法执行的顺序为
MyIntercept01.preHandle->MyIntercept02.preHandle->MyIntercept02.postHandle->MyIntercept01.postHandle->MyIntercept02.afterCompletion->MyIntercept01.afterCompletion
preHandle方法为正序
, postHandle,afterCompletion方法为倒叙
4.3统一异常处理
异常处理我们可以统一处理,从而优雅的实现具体的逻辑代码。在业务层具体抛出不同类型的异常,我们可以定义多个自己想处理的异常来统一接受处理这些异常
定义一个类
@RestControllerAdvice // 这个注解必须要
public class MyException {
// 这个注解必须要, 参数为需要处理的异常类型,可以捕获这个类型以及它的子类
@ExceptionHandler(Exception.class)
public void dealException() {
System.out.println("dealException");
}
}
4.4multipart(文件上传)数据处理
&emsp;文件上传,我们首先需要引入一个依赖common-file
,然后在方法的形参上使用MultipartFile类型来接受文件。
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
@RequestMapping("/test8")
public void test8(MultipartFile name) throws IOException {
// name这个变量里面就封装了文件的所有信息
// 比如获取文件的原始文件名
String originalFilename = name.getOriginalFilename();
// 下面这个方法,可以把当前的文件,复制到磁盘上去
File finalFile = new File("/tmp");
name.transferTo(finalFile);
}