SpringMVC
1. 基本概念
1.1. 执行过程
执行步骤
- 发起请求到前端控制器(DispatcherServlet)
- 前端控制器请求HandlerMapping查找 Handler
- 可以根据xml配置、注解进行查找
- 处理器映射器HandlerMapping向前端控制器返回Handler
- 前端控制器调用处理器适配器HandlerAdapter去执行Handler
- 处理器适配器去执行Handler
- Handler执行完成给适配器返回ModelAndView
- 处理器适配器向前端控制器返回ModelAndView
- ModelAndView是springmvc框架的一个底层对象,包括 Model和view
- 前端控制器请求视图解析器ViewReslover去进行视图解析
- 根据逻辑视图名解析成真正的视图(jsp)
- 视图解析器向前端控制器返回View
- 前端控制器进行视图渲染
- 视图渲染将模型数据(在ModelAndView对象中)填充到request域
- 前端控制器向用户响应结果
2. 配置使用
2.1. 配置DispatcherServlet(前端控制器),web.xml内添加
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 配置映射,支持RESTful,需额外配置静态文件 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
2.2. 导入MVC命名空间约束
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
2.3. 非注解配置处理器适配器和映射器。(一般不用)
<!-- 配置处理器适配器 -->
<!-- 简单的控制器处理器适配器,它支持所有实现了Controller接口的Handler控制器 -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
<!-- http 请求处理器适配器,它要求编写的Handler需要实现HttpRequestHandler接口 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
<!-- 配置处理器映射器,多个映射器可以并存 -->
<!-- BeanNameUrlHandlerMapping,根据对象的名字来进行处理器映射,此时该对象要继承AbstractController 实现handlerRequestInternal方法;对请求的处理主要在该方法中完成 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<!-- 对应Handler的配置,对象配置时的name属性必须为 "/xxx.action"的形式,即为访问的URL名-->
<bean name="/index.action" class="com.lh.controller.testController"></bean>
<!-- SimpleUrlHandlerMapping,简单url映射,可以统一配 -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/items1.action">controller的bean id</prop>
<prop key="/items2.action">controller的bean id</prop>
</props>
</property>
</bean>
<!-- ControllerClassNameHandlerMapping,对应Handler的类名必须为xxxController,访问地址为输入/xxxController或 /xxxcontroller或/xxx -->
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean>
2.4. 使用注解配置(常用方式)
<!-- 默认加载注解适配器和映射器 -->
<mvc:annotation-driven />
<!-- 指定需要扫描的包,省去配置用注解的bean类 -->
<context:component-scan base-package="spring.controller"></context:component-scan>
2.5. 配置视图解析器
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath中必须包含jstl -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<!-- 配置前后缀,在写jsp路径时可以省略前后缀部分 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
2.6. 文件上传
<!-- 文件上传配置,上传拦截,如最大上传值及最小上传值 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="maxUploadSize" value="104857600"></property>
<property name="maxInMemorySize" value="4096"></property>
<property name="defaultEncoding" value="utf-8"></property>
</bean>
2.7. 静态资源
<!-- 静态文件地址配置,所有mapping都到location里进行查找 -->
<mvc:resources location="upload/img/" mapping="/upload/img/**" />
<mvc:resources location="static_resources/css/" mapping="/styles/**" />
3. 注解
@Controller:标识Handler类,修饰类
@RestController :Spring4之后新加入的注解,原来返回json需要@ResponseBody和@Controller配合。即@RestController是@ResponseBody和@Controller的组合注解。
@RequestBody:将页面的json字符串转成pojo对象。
@ResponseBody :注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,需要注意的呢,在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
@RequestMapping("/login")
@ResponseBody
public User login(User user){
return user;
}
// 效果等同于如下代码:
@RequestMapping("/login")
public void login(User user, HttpServletResponse response){
response.getWriter.write(JSONObject.fromObject(user).toString());
}
@RequestMapping(value="/item")或@RequestMapping("/item)作用于类和方法,标识映射URL,带/为绝对路径(推荐)
value的值是数组,可以将多个url映射到同一个方法或类。
method的值为RequestMethod.GET/POST,多值用{,}
@PathVariable():为支持REST,用于匹配RequestMapping中的{xxx}占位符参数
@RequestParam(value,required) 把request的参数绑定到方法的形参上
value是匹配传递的参数名,等效于Request.getParamrter()
required默认为true,表示必须包含对应的参数
@ModelAttribute(value)
作用于方法:
- 该方法会在此controller每个方法执行前被执行。
- 如果方法有返回值,则使用Model的addAttribute添加该返回值为属性。
- 属性名为value值,如果没有value值则为首字母小写的类型名。
如下所示和@RequestMapping一起注释一个方法时,方法返回值不在表示视图名称,视图名称由RequestToViewNameTranslator根据请求"/helloWorld.do"转换为逻辑视图helloWorld。并且执行了model.addAttribute("attributeName","hi")把返回值存进了model里。
1 @Controller
2 public class HelloWorldController {
3 @RequestMapping(value = "/helloWorld.do")
4 @ModelAttribute("attributeName")
5 public String helloWorld() {
6 return "hi";
7 }
8 }
作用于形参:
会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用。
@SessionAttributes("userSession")只能修饰类,可以将Model中的属性同步到session当中,通过匹配参数名或者类型
- String[] value:要保存到session中的参数名称
- Class[] typtes:要保存的参数的类型,和value是并集关系,即互相独立起作用。
@RequestParam()自定义绑定,把请求里的参数绑定到形参里
- value:传入key/value的key,即参数名称
- required:true必须传此参数,否则报400 Required Integer parameter 'XXXX' is not present
- defaultValue:默认值,没同名参数时的默认值
4. 参数绑定
4.1. 默认支持参数类型(直接写在方法形参就可使用)
- HttpServletRequest获取请求信息
- HttpServletResponse处理响应信息
- HttpSession得到session中存放的对象
- Model/ModelMap向页面传递数据(底层就是写入request域)
- RedirectAttributes用于重定向存储数据
4.2. 简单类型绑定
- 基本数据类型的值,请求的参数名和形参名一致可传值
- pojo绑定(和简单类型绑定同时生效,即同名两个都获得值)
- 形参pojo的属性名和请求参数名相同即可传值
- 请求参数名为item.name则匹配,某个pojo形参的item属性的name属性
- pojo绑定的同时会注入pojo到request域,基础类型则不会。
- 数组绑定:页面多个参数name相同,形参同名数组类型即可
- list绑定:页面参数名写成:items[0].name的形式
- map绑定:页面参数名写成:items['name']的形式
4.3. 自定义Converter
配置方式
<!-- conversion-service属性填写自定义的转换器服务的bean的id -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- conversionService转换器服务,多个转换器配置在list里 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 转换器 -->
<property name="converters">
<list>
<bean class="cn.itcast.ssm.controller.converter.CustomDateConverter"/>
</list>
</property>
</bean>
编写自定义Converter类
// 接口的泛型就是需要转换的类型,一般是String转成目标类型
public class CustomDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return simpleDateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
原理分析:页面的参数名用来匹配形参的参数名,然后会根据形参的具体类型去自动匹配对应的转换器(如果有的话),把转换后的结果注入到变量中。
5. 异常处理
5.1. 局部异常处理
@ExceptionHandler:定义在Controller内部,作用于异常处理方法上.
例:
@ExceptionHandler(value={IOException.class,SQLException.class})
public String exp(Exception ex,HttpServletRequest request) { }
5.2. 全局异常处理
实现HandlerExceptionResolver接口,并配bean或者注解@Component就可以生效。
配置SimpleMappingExceptionResolver
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 定义默认的异常处理页面,默认异常会跳转到error.jsp -->
<property name="defaultErrorView" value="error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常也页名作为值 -->
<property name="exceptionMappings">
<props>
<prop key="IOException">error/ioexp</prop>
<prop key="java.sql.SQLException">error/sqlexp</prop>
</props>
</property>
</bean>
6. 上传文件
需要jar包:commons-fileupload/io
前台form属性enctype="multipart/form-data"
6.1. 配置CommonsMultipartResolver
<!-- 文件上传配置,上传拦截,如最大上传值及最小上传值 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="maxUploadSize" value="104857600"></property>
<property name="maxInMemorySize" value="4096"></property>
<property name="defaultEncoding" value="utf-8"></property>
</bean>
6.2. 编写Controller方法
public String toRegister(HttpServletRequest request, @RequestParam("pic") MultipartFile pic) {
String filePath = request.getSession().getServletContext().getRealPath("upload/img");
String picName = UUID.randomUUID()+ pic.getOriginalFilename().substring(pic.getOriginalFilename().lastIndexOf('.'));
File file = new File(filePath + File.separator + picName);
try {
pic.transferTo(file);
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
return "register";
}
6.3. MultipartFile接口
方法:
- String getOriginalFileName() 返回文件名.后缀
- void transferTo(File file) 把文件存储到file里
6.4. 虚拟目录
在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:
<Context docBase="F:\develop\upload\temp" path="/pic" reloadable="false"/>
访问http://localhost:8080/pic
即可访问F:\develop\upload\temp
下的图片
7. 拦截器
7.1. 实现HandlerInterceptor接口
public class HandlerInterceptor1 implements HandlerInterceptor {
//进入 Handler方法之前执行
//用于身份认证、身份授权
//比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//return false表示拦截,不向下执行
//return true表示放行
return false;
}
//进入Handler方法之后,返回modelAndView之前执行
//应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
//执行Handler完成执行此方法
//应用场景:统一异常处理,统一日志处理
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
7.2. 执行顺序
- preHandle按拦截器定义顺序调用
- postHandler按拦截器定义逆序调用
- afterCompletion按拦截器定义逆序调用
- preHandle在拦截器链内它之前的所有拦截器方法返回true时调用。
- postHandler在拦截器链内它之前的所有拦截器方法返回true时调用。
- afterCompletion只要其自身的preHandle返回true就调用,反之返回false或不执行时都不调用。
7.3. 配置拦截器
方式一(不推荐)
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
方式二
<!--拦截器 -->
<mvc:interceptors>
<!--多个拦截器,顺序执行 -->
<mvc:interceptor>
<!--匹配mapping的才会被拦截器拦截-->
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>