1、请求URL和方法的映射
@RequestMapping注解来进行URL和方法的映射
可能的情况:
①一个类里面,每个方法的URL,没有相同的部分,于是每个方法上面,都各自指定一个URL。
②一个类里面,所有的方法,前面部分都是一样的,比如:
/user/add、/user/list、/user/view、/user/update
此时可以在类上面使用 @RequestMapping(value=”/user”),此时方法上面就只需要:@RequestMapping(value=”add”)。
HTTP的请求方法:
GET
DELETE 删除服务器的数据
PUT 新增数据
HEAD 只返回响应头
浏览器只能发送GET、POST请求,其他的请求浏览器无法直接发送。可以借助一些手段发送GET、POST之外的其他请求。
2、视图解析器
作用:简化返回的视图路径。在SpringMVC里面,支持多种视图,如JSP、xml
举个较常用的视图解析器,更多可查看Spring官方文档①hello-servlet.xml
注意:如果使用UrlBasedViewResolver,需要加上jstl的jar包,否则抛出java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config
在输入访问路径后,配置以上内容后会自动加上前缀/WEB-INF/content/,后缀.jsp
②控制器
web项目结构:
测试:
3、接收浏览器的参数
index1.jsp
①在SpringMVC里面接收请求参数,通过方法的参数传入的
i.参数和jsp页面传过来的一致
ii.参数和jsp页面传过来的不一致。需要加上@RequestParam注解。推荐无论是否相同都加上。
测试:
4、请求参数自动转换为Java对象
平时情况:
①Employee.java
②index2.jsp
③EmployeeController.java
@InitBinder注解的作用:任意的方法,如果使用了这个注解,就可以得到WebDataBinder对象,用于注册自定义转换器、验证器等。这个注解的方法在进行参数绑定之前执行一次。
PropertyEditor是属性编辑器的接口,它规定了将外部设置值转换为内部JavaBean属性值的转换接口方法
特殊情况:
情况1:如果出现两个相同类型不同格式的对象
①Employee.java出现两个Date,但格式不同
public class Employee {
private String name;
// 默认SpringMVC基本上无法把String转换为Date,需要自定义转换器
private Date birthday;
private int age;
private Date updateTime;
//getter、setter
@Override
public String toString() {
return "Employee [name=" + name + ", birthday=" + birthday + ", age=" + age + ", updateTime=" + updateTime
+ "]";
}
}
②EmployeeController.java
此处注意,必须要有一个指定字段,否则会有一个出错
@Controller
@RequestMapping("/employee")
public class EmployeeController {
@InitBinder
public void initBinder(WebDataBinder binder){
binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){
// 把字符串转换为目标数据类型的对象
// 转换以后,需要调用父类里面的setValue方法设置Java对象。
@Override
public void setAsText(String text) throws IllegalArgumentException {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date d = format.parse(text);
super.setValue(d);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 指定某个字段专用的转换器
binder.registerCustomEditor(Date.class, "updateTime", new PropertyEditorSupport(){
@Override
public void setAsText(String text) throws IllegalArgumentException {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date d = format.parse(text);
super.setValue(d);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
@RequestMapping
public String save(Employee e){
System.out.println(e);
return "/save.jsp";
}
}
③测试:
情况2:请求参数包含多个对象的时候
①index3.jsp
②Department.java
③DepartmentController.java
要通过 @InitBinder 指定对象的前缀
@Controller
@RequestMapping("/department")
public class DepartmentController {
//要通过 @InitBinder 指定对象的前缀
@InitBinder("employee")
public void employee(WebDataBinder binder){
binder.setFieldDefaultPrefix("employee.");
}
@InitBinder("dept")
public void dept(WebDataBinder binder){
binder.setFieldDefaultPrefix("department.");
}
@RequestMapping
public String save(@ModelAttribute("employee") Employee employee,@ModelAttribute("dept") Department department){
System.out.println("------"+employee);
System.out.println("------"+department);
return "department/save";
}
}
④测试:
总结:
a.如果请求参数里面,只有一个对象的数据,只需要把对象的类型作为方法的参数即可,不需要任何的额外注解。
b.有一些参数不能正常转换为对象需要的数据,则要用 @InitBinder 注册自定义的转换器。
c.如果请求参数里面包含多个对象的数据,这些数据需要有【前缀】,并且每个对象,都要有一个对应的 @InitBinder 注解的方法。同时Action方法的参数需要使用@ModelAttribute 注解对应@InitBinder 的名字。
5、请求响应方式(导航)
①forward
直接返回一个jsp的路径,就会forward到jsp。可以结合ViewResolver来找到对应的JSP文件。
②redirect
i.使用 redirect: 开头。重定向到根目录下的a.jsp文件
ii.使用RedirectView。重定向到根目录下的a.jsp文件
③响应体
i.使用@ResponseBody注解方法,直接把方法的返回值作为内容返回给浏览器
测试:
ii.使用ResponseEntity对象返回,可以自定义返回的响应头、响应内容
测试:
6、
①获取请求头
使用方法的参数来进行获取,获取的时候,在参数前面使用 @RequestHeader 注解
②.获取Cookie值
③.操作Session
每个Controller只负责管理自己的session里面的值。Session是整个用户会话共享的,在Controller里面如果要把数据放入session里面,需要先在类上面使用 @SessionAttributes注解标明哪些对象要放入session里面,当方法执行完成以后,Spring调用session.setAttribute方法放入session。
在Controller里面,通过Model、ModelAndView对象,把数据放到【模型】里面,在方法执行完成以后,检查【模型】里面的对象的名字,是否在@SessionAttributes注解里面,如果在则放入session。
i.通过Model传值给session
下面例子中key1,key2是没存进session的,因为@SessionAttributes注解没有表明
@Controller
@RequestMapping("/session")
@SessionAttributes({"key3","key4"})
public class TestSessionAction {
// 通过Model传值给session
@RequestMapping("/1")
public String test1(Model model){
model.addAttribute("key1", "val1");
model.addAttribute("key2", "val2");
return "session";
}
}
ii.通过ModelAndView传值给session
@RequestMapping("/2")
public ModelAndView test2(){
ModelAndView mv = new ModelAndView();
mv.addObject("key3", "val3");
mv.addObject("key4", "val4");
mv.setViewName("session");
return mv;
}
iii.删除当前Controller存入session的对象
@RequestMapping("/3")
public String test3(SessionStatus status){
//Mark the current handler's session processing as complete,
//allowing for cleanup of session attributes.
status.setComplete();
return "session";
}
7、Servlet API的使用
在任何的MVC框架里面,都不推荐直接使用ServletAPI,但是几乎所有的框架,都提供了使用Servlet API的方式。
Spring提供了两种使用Servlet API的方式
①在方法参数里面,直接使用 HttpServletRequest、HttpServletResponse等作为参数,Spring MVC会自动把对象传入进来。
@RequestMapping("/1")
public String test(HttpServletRequest req,HttpServletResponse resp){
req.setAttribute("key1", "val1");
req.getSession().setAttribute("key1", "val1");
System.out.println("--test1--");
return "test";
}
@RequestMapping("/3")
public String test3(HttpSession session){
String p = (String)session.getAttribute("key1");
System.out.println("test3:"+p);
return "test";
}
②使用WebRequest、HttpSession的对象。Spring MVC为了避免直接使用Servlet API造成的不良影响、又能够提供完全的Servlet API调用的控制,所以封装了一个叫做WebRequest的对象,通过这个对象几乎能够完成所有的Servlet API的工作。
@RequestMapping("/2")
public String test2(WebRequest weq){
String p = weq.getParameter("key1");
System.out.println(p);
//通过WebRequest将属性放入,不能使用SessionStatus.setComplete()这种方法删除
//只能使用WebRequest的removeAttribute
weq.setAttribute("key2", "", WebRequest.SCOPE_REQUEST);
// 从request里面删除对象
weq.removeAttribute("key2", WebRequest.SCOPE_REQUEST);
// session
weq.setAttribute("yyy", "", WebRequest.SCOPE_SESSION);
return "test";
}
8、拦截器
拦截器配置在【-servlet.xml】文件里面,仅在当前模块里面有效。
所有的拦截器,都需要实现org.springframework.web.servlet.HandlerInterceptor,或者继承
org.springframework.web.servlet.handler.HandlerInterceptorAdapter
①hello-servlet.xml<mvc:interceptors>
<!-- hello模块全局拦截器 -->
<bean class="cony.action.TestInterceptor" />
<mvc:interceptor>
<!--设置拦截路径 -->
<mvc:mapping path="/a/2"/>
<!-- 局部拦截器 -->
<bean class="cony.action.TestInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
②TestInterceptor.java
public class TestInterceptor extends HandlerInterceptorAdapter {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle");
// 返回true,后面的业务才会继续
return true;
}
}
③TestInterceptor2.java
public class TestInterceptor2 extends HandlerInterceptorAdapter{
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion2");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle2");
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle2");
// 返回true,后面的业务才会继续
return true;
}
}
④测试:
i.
ii.可以看到局部拦截器先执行,然后才是全局拦截器
⑤拦截器方法说明
preHandle:每次进入拦截器,最先执行的方法。
如果这个方法返回false,不会继续执行后面的业务
postHandle:在进入方法、实际的业务方法执行完成以后,执行forward之前。
afterCompletion:整个请求完成的时候执行,完成forward之后执行
执行这个方法的时候,连jsp也已经执行完成。