一、SpringMVC请求流程
- 用户发送请求,到前端控制器(servlet,DispatcherServlet)配置在web.xml中;
- 前端控制器去找处理器映射器(HandlerMapping) 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理拦截器一并返回给 DispatcherServlet;
- 根据处理器映射器返回的处理器,DispatcherServlet 会找“合适”的处理器适配器(HandlerAdapter);
- 处理器适配器HandlerAdapter 会去执行处理器(Handler开发的时候会被叫成Controller也叫后端控制器)执行前会有转换器、数据绑定、校验器等等完成上面这些才会去正式执行Handler
- 后端控制器Handler执行完成之后返回一个ViewResolver对象
- 处理器适配器HandlerAdapter会将这个ModelAndView返回前端控制器DispatcherServlet 前端控制器会将ModelAndView对象交给视图解析器ViewResolver
- 视图解析器ViewResolver解析ViewResolver对象之后返回逻辑视图
- 前端控制器DispatcherServlet对逻辑视图进行渲染 (数据填充) 之后返回真正的物理View并响应给浏览器
二、配置文件
1. web.xml
<!-- 1. 配置DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 2. 配置初始化参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc2.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. springmvc.xml
<!-- 1. 注解的扫描 -->
<context:component-scan base-package="com.lyzzz"/>
<!-- 自动配置RequestMappingHandlerMapping
和RequestMappingHandlerAdapter注解的驱动 -->
<mvc:annotation-driven/>
<!-- 2. 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3. 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 不拦截登录请求 -->
<!--<mvc:exclude-mapping path="/login"/>-->
<bean class="com.lyzzz.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!-- 当设置多个拦截器时,先按顺序调用preHandler方法
然后逆序调用每个拦截器的postHandler和afterCompletion方法-->
</mvc:interceptors>
<!-- 4. 异常处理器 -->
<bean id="customExceptionResolver" class="com.lyzzz.exception.CustomExceptionResolver"></bean>
<!-- 5. 前端控制器拦截的是 / 释放静态资源的handler -->
<mvc:default-servlet-handler/>
三、@RequestMapping注解
@RequestMapping
- value: 可配置多个地址
@RequestMapping(value = {"/method1","/method2","/method3"})
public String method()
- method:配置请求方式
@RequestMapping(method = {RequestMethod.POST,RequestMethod.GET})
public void method()
- 如有多个重复地址,可在类上添加@RequestMapping
@Controller
@RequestMapping("/param")
public class ParamController
- 响应数据格式consumes:指定处理请求的提交内容的类型(content-Type)
@RequestMapping(consumes = "application/json;charset=utf-8")
// 接收json
public void method()
- 读取数据格式produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
@RequestMapping(produces = "application/json;charset=utf-8")
// 返回json
public void method()
- params:指定request中必须包含某些参数值,才让该方法处理
@RequestMapping(params = {"/num"})
public void method()
- headers:指定request中必须包含某些指定的header值,才让该方法处理请求
@RequestMapping(headers = {"\"Referer=http://www.hello.com/\""})
public void method()
四、@ResponseBody和 @RequestBody
- @ResponseBody 发送 json
// 在方法上添加@ResponseBody,恒不跳转
@RequestMapping(value = "/method1",produces = "application/json;charset=utf-8")
@ResponseBody
public String method1(){
String json = "{\"name\":\"zs\",\"age\":\"18\"}";
return json;
}
- @RequestBody 接收 json 封装参数
@RequestMapping(value = "/method2" ,method = {RequestMethod.POST})
@ResponseBody
// @RequestBody 接收json 封装参数
public Teacher method2(@RequestBody Teacher teacher){
System.out.println(teacher);
return teacher;
}
要添加jackson jar包
* tomcat报错conf>catalina.properties
* 添加 ,\ jackson-databind-2.10.0.jar,jackson-annotations-2.10.0.jar,jackson-core-2.10.0.jar
<!-- jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
@RestController
@RestController
//设置该类的所有方法返回 JSON 数据
//等价于 @Controller + @RequestBody
五、reseful @PathVariable
@RequestMapping("/method6/{name}/{age}")
public String method6(@PathVariable("name") String name, @PathVariable("age") int age){
System.out.println(name);
System.out.println(age);
return "param";
}
六、封装数据
- 方法参数不同 @RequestParam
@RequestMapping("/method2")
// 参数名称不同@RequestParam(value = "names")
public String method2(@RequestParam(value = "names",required = true) String name, @RequestParam(defaultValue = "20") Integer age){
System.out.println(name);
System.out.println(age);
return "param";
}
- 日期类型
@RequestMapping(value = "/method3")
public String method3(Date birthday){
System.out.println(birthday);
return "param";
}
// 处理日期格式
@InitBinder
public void bindValue(WebDataBinder binder){
binder.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
// JavaBean属性
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
七、返回值
/**
* Controller中方法的返回值
* */
// 1. 返回ModelAndView
@RequestMapping("method1")
public ModelAndView method1(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("result","返回值是modelandview");
modelAndView.setViewName("method1");
return modelAndView;
}
// 2. 返回void
@RequestMapping("method2")
public void method2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 在controller形参上可以定义 request和response,并指定响应结果
// request
request.setAttribute("request","request");
request.getRequestDispatcher("/WEB-INF/view/method1.jsp").forward(request,response);
// session
HttpSession session = request.getSession();
session.setAttribute("response","response1");
// 不能直接访问WEB-INF下的文件,受保护
response.sendRedirect("/index.jsp");
// 响应json
response.setContentType("application/json;charset=utf-8");
response.getWriter().println("{\"name\":\"中国\"}");
String method3 = (String) request.getAttribute("method3");
System.out.println(method3);
String name = request.getParameter("name");
System.out.println(name);
}
// 3. 返回字符串
@RequestMapping("method3")
public String method3(HttpServletRequest request){
/**
* 返回逻辑视图名
* controller方法返回字符串可以指定逻辑视图名
* 通过视图解析为物理视图地址
*
* */
// 转发操作
request.setAttribute("method3","转发到另一个");
return "method1"; //转发跳转到页面
return "forward:/return/method2"; // 转发到另一个controller
return "redirect:/return/method2?name=zsss"; // 重定向到另一个controller
}
八、拦截器
* 8. 拦截器 interceptor
* 在controller之后view视图解析之前执行
* 过滤器和拦截器的区别
* 1. 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
* 2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
* 3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
* 4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
* 5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
*
* |-- 拦截器配置
* |-- 编写拦截器类,实现HandlerInterceptor接口,重写方法
* |-- springmvc.xml中配置拦截器
* <!-- 配置拦截器 -->
* <mvc:interceptors>
* <mvc:interceptor>
* <!-- 匹配url路径,如果不配置或者 设置为 "/**" 将拦截所有controller -->
* <mvc:mapping path="/**"/>
* <!-- 不拦截登录请求 -->
* <!--<mvc:exclude-mapping path="/login"/>-->
* <bean class="com.lyzzz.interceptor.MyInterceptor"/>
* </mvc:interceptor>
* <!-- 当设置多个拦截器时,先按顺序调用preHandler方法
* 然后逆序调用每个拦截器的postHandler和afterCompletion方法-->
* </mvc:interceptors>
* |-- preHandle()
* 在控制器方法调用前执行,返回值为是否中断
* true表示继续执行
* false则会中断后续的所有操作,所以我们使用response来继续响应后续请求
*
* |-- postHandle()
* 在控制器方法调用后,解析视图前调用
* 可以对模型和视图进一步进行修改或渲染
* 可在ModelAndView中加入数据,比如当前时间
*
* |-- afterCompletion()
* 整个请求完成,即视图渲染结束后调用,
* 可做资源清理工作,或日志记录