文章主要内容:
- HelloWorld
- DispatherServlet配置
- RequestMapping注解
- 获取请求数据
- 处理模型数据
- @ModelAttribute
- 转发与重定向
SpringMVC
一、 SpringMVC核心技术
1 HelloWorld
1.1 导入依赖包
1.2 配置 web.xml 的 DispatcherServlet
在项目的web.xml中配置DispatcherServlet:
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</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>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
主要修改:
<param-value>classpath:springmvc.xml</param-value>
<url-pattern>/</url-pattern>
1.3 创建index.jsp、success.jsp及controller
index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<a href="helloWorld">HELLO world</a>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h1>success</h1>
</body>
</html>
controller
package com.cyt.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class FirstController {
@RequestMapping("/helloWorld")
public ModelAndView helloworld() {
System.out.println("helloworld");
ModelAndView mv = new ModelAndView("/WEB-INF/views/success.jsp");
return mv;
}
}
在jsp中
<a href="helloWorld">HELLO world</a>
对应controller中@RequestMapping("/helloWorld")
这里用到的 ModelAndView 这种方式只在helloworld做介绍,很少实际应用此方法
1.4 创建springmvc.xml并配置
其实就是创建一个 spring bean configuration file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">
<context:component-scan base-package="com.cyt.controller"></context:component-scan>
</beans>
选择bean, context, mvc,并添加组件扫描<context:component-scan base-package="com.cyt.controller"></context:component-scan>
运行index.jsp即会跳转到success.jsp
1.5 配置视图解析器
上面在controller中用ModelAndView的方法跳转页面,但更多是用解析器操作:
- 在springmvc.xml中配置bean:
<bean id="" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
- 改动controller
@Controller
public class FirstController {
@RequestMapping("/helloWorld")
public String helloworld() {
System.out.println("helloworld");
return "success";
}
}
2 DispatcherServlet配置
2.1 <url-pattern>设置
- /* 的意思是“所有的请求”
- / 的意思是“所有的末被其它servlets接收并处理的请求”
在DispatcherServlet中,如果用了 /* 那么就会处理所有的请求,这意味着也会比JSP servlet优先处理.jsp请求。所以这也是为什么在DispacherServlet中通常配置的是<url-pattern>/</url-pattern>
。通常情况下,只有在 Filter 中才会使用 /* ,这样可以监听所有的request请求
2.2 servlet-handler 和 annotation-driven
若将 DispatcherServlet 请求映射配置为 /, 则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求(如加入的JQuery), SpringMVC 会将他们当成一个普通请求处理, 因找不到对应处理器将导致错误。
解决:
在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>。配置后,原来的请求又会出现问题,需要配置<mvc:annotation-driven />
将上面例子做改动:
springmvc.xml 添加:
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>
在WebContent目录下添加resources文件夹,文件夹下添加a.txt
aaa
bbb
ccc
ddd
这样可直接访问resources下的a.txt : http://localhost:8080/springmvc/resources/a.txt 无需其他映射
如果不添加
<mvc:annotation-driven></mvc:annotation-driven
>,则反而会使 helloWorld 的映射失效
3 RequestMapping注解
Spring通过@Controller注解找到相应的控制器类后,还需要知道控制器内部对每一个请求是如何处理的,这就需要使用@RequestMapping注解类型,它用于映射一个请求或一个方法。使用时,可以标注在一个方法或一个类上。
示例所用index,jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h1>RequestMapping test</h1>
<form action="login" method="post">
Username<input type="text" name="username"/><br><br>
Password<input type="text" name="password"/><br><br>
<input type="submit" value="Submit"/>
</form>
<br>
<a href="helloWorld">hello world</a>
</body>
</html>
3.1 标注在方法上:
@RequestMapping(value = "/login",params = {"username=jinondo","password"},method = RequestMethod.GET)
public String login() {
System.out.println("Do Login");
return "success";
}
- value/path =====》 RequestMapping 中默认配置value的属性,或者是path,两者为别名关系,功能一致:
@RequestMapping(value = "/login")
或@RequestMapping(path= "/login")
- params =====》为跳转所携带的参数
params = {"username","password"}
表示必须有两个参数分别为username和password
params = {"username=jinondo","password"}
表示必须有两个参数分别为username和password,且username的值为jinondo
params = {"!username","password"}
前面加个感叹号!,表示不能出现username的参数
- method =====》为对应请求方法为GET或POST
例如上面jsp中的表单:
<form action="login" method="post">
Username<input type="text" name="username"/><br><br>
Password<input type="text" name="password"/><br><br>
<input type="submit" value="Submit"/>
</form>
method应和RequestMapping中method属性对应上才能正常运行
- 使用@PostMapping等
请求方法共有:
–@GetMapping
–@PostMapping
–@PutMapping
–@DeleteMapping
使用@PostMapping(path = "/login",params = {"username","password"})
和使用@RequestMapping(value = "/login",params = {"username","password"},method = RequestMethod.POST)
效果一致
3.2 标注在类上:
@RequestMapping("/test")
@Controller
public class FirstController {
@RequestMapping("/helloWorld")
public String helloworld() {
System.out.println("helloworld");
return "success";
}
......
}
第一行@RequestMapping("/test")
表示映射的地址得添加一个/test,即/test/helloWorld:
<a href="test/helloWorld">hello world</a>
4 获取请求数据
4.1 @RequestParam获取请求参数
index.jsp中添加:
<h1>RequestMapping test</h1>
<form action="${pageContext.request.contextPath }/test/addPerson" method="post">
IdCard:<input type="text" name="idCard"/><br><br>
Name:<input type="text" name="name"/><br><br>
Age:<input type="text" name="age"/><br><br>
<input type="submit" value="Submit"/>
</form>
<br>
controller:
@PostMapping("/addPerson")
public String add(@RequestParam(value="card", required = false)String idCard,String name,
@RequestParam(value="age", required = false, defaultValue = "0")String age) {
System.out.println(idCard);
System.out.println(name);
System.out.println(age);
return "success";
}
这里的 @PostMapping("/addPerson") 和
action="${pageContext.request.contextPath }/test/addPerson"
对应因为上个例子中RequestMapping设置在类前的@RequestMapping("/test")
- value属性:如果不配置value属性,则controller函数参数中的idCard必须与表单中的name一致为idCard;如果配置@RequestParam中的value属性则可以指定页面中的name属性的别名。
- required:默认为true,true为请求数据必须有name符合的指定参数,否则会报错,false则不会报错,设置为null
- defaultValue:设置没有同名参数时的默认值。
4.2 @CookieValue获取cookie
可以使用@CookieValue获取sessionId:
@PostMapping("/addPerson")
public String add(@RequestParam(value="idCard", required = false)String idCard,String name,
@RequestParam(value="age", required = false, defaultValue = "0")String age, @CookieValue("JSESSIONID")String sessionId) {
System.out.println(idCard);
System.out.println(name);
System.out.println(age);
System.out.println("cookie:JSESSION:" + sessionId);
return "success";
}
@CookieValue("JSESSIONID")String sessionId
获取session id
4.3 使用POJO获取参数
- 创建bean
public class Person {
private String idCard;
private String name;
private Integer age;
private Address address;
public class Address {
private String city;
private String province;
- controller
@PostMapping("/pojo")
public String pojo(Person person) {
System.out.println(person);
return "success";
}
- index.jsp
<h1>POJO test</h1>
<form action="${pageContext.request.contextPath }/test/pojo" method="post">
IdCard:<input type="text" name="idCard"/><br><br>
Name:<input type="text" name="name"/><br><br>
Age:<input type="text" name="age"/><br><br>
City:<input type="text" name="address.city"/><br><br>
Province:<input type="text" name="address.province"/><br><br>
<input type="submit" value="Submit"/>
</form>
<br>
- 必须保持参数和类的属性名一致
- 如果类中有关联关系,则用级联方法传递数据,例如 name=“address.city” 和 name=“address.province”
4.4 使用 Servlet API 作为入参
controller
@PostMapping("/pojo")
public String pojo(Person person,HttpServletRequest request,HttpServletResponse response, HttpSession session) throws IOException {
System.out.println(person);
System.out.println(request.getContextPath());
System.out.println(response.getWriter().getClass().getName());
System.out.println(session);
return "success";
}
即使用原生的servlet的api
4.5 @PathVariable
此注解用于数据在url中,需要在controller中获取时。
例如
index.jsp:
<a href="${pageContext.request.contextPath }/test/order/20">Get Order</a>
controller:
@GetMapping("/order/{id}")
public String getOrder(@PathVariable("id")Integer orderId) {
System.out.println("ID : " + orderId);
return "success";
}
url为:http://localhost:8080/springmvc/test/order/20
需要获取到 20 则用此方法
5 处理模型数据
5.1 Model
提交页面index.jsp
<h1>Model test</h1>
<form action="${pageContext.request.contextPath }/test/model" method="post">
IdCard:<input type="text" name="idCard"/><br><br>
Name:<input type="text" name="name"/><br><br>
Age:<input type="text" name="age"/><br><br>
City:<input type="text" name="address.city"/><br><br>
Province:<input type="text" name="address.province"/><br><br>
<input type="submit" value="Submit"/>
</form>
<br>
- 使用 Model.addAttribute(Object arg0)
使用参数只有一个的重载函数,表示把数据装入request域中,在页面取出数据时只能使用类名的小写取出:
@PostMapping("/model")
public String model(Person person,Model model) {
System.out.println(person);
System.out.println("Do Logic");
Date current = new Date();
model.addAttribute(current);
return "success";
}
success.jsp:
<body>
<h1>success</h1>
<hr>
<h2>model result</h2>
Current:${requestScope.date }
</body>
注意 ${requestScope.date },如果有多个同类型的则必须指定名字
- Model.addAttribute(String agrg0, @Nullable Object arg1)
使用有两个参数的addAttribute则表示指定名称装入request域。
@PostMapping("/model")
public String model(Person person,Model model) {
System.out.println(person);
System.out.println("Do Logic");
Date current = new Date();
model.addAttribute("current",current);
return "success";
}
success.jsp中改为:
Current:${requestScope.current }
<body>
<h1>success</h1>
<hr>
<h2>model result</h2>
Current:${requestScope.current } <br> <br>
Person: ${requestScope.person } <br> <br>
</body>
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
在model(即controller)函数形参中的数据会默认传到request域中,故刚刚我们没有在model中传入person类的数据,但依然能够在success.jsp中的requestScope中获取到,名称为类名小写,数据为我们提交页面(index.jsp)的表单中的数据。
可以重新设置一样的名称把表单提交的数据(即形参中的数据)覆盖掉,例如
Person per = new Person("aa123", "Tom", 20, null);
model.addAttribute("person",per);
5.2 Map
Map 和 Model 的使用并基本一致,无太大区别:
index.jsp:
<h1>Map Test</h1>
<a href="${pageContext.request.contextPath }/test/map">map test</a>
<br>
Controller:
@GetMapping("/map")
public String map(Map<String, Object> map) {
Date date = new Date();
map.put("time", date);
return "success";
}
success.jsp:
<h2>map test</h2>
Time:${requestScope.time }<br> <br>
5.3 ModelAndView
第一节使用过的那个ModelAndView,实际开发较少使用。
index.jsp
<h1>ModelAndView Test</h1>
<a href="${pageContext.request.contextPath }/test/testModelAndView">ModelAndView test</a>
<br>
controller:
@GetMapping("/testModelAndView")
public ModelAndView modelAndView() {
ModelAndView mv = new ModelAndView("success");
mv.addObject("message", "the end of the fxxking world");
return mv;
}
success,jsp
<h2>ModelAndView result</h2>
Message: ${requestScope.message }<br><br>
5.4 @SessionAttributes
如果需要往session域里放数据,可以用@SessionAttributes这个注解,但存在着问题!!!
例如:
在controller类前加上此注解:
@SessionAttributes("current")
@RequestMapping("/test")
@Controller
public class FirstController {
......
即在前面Model例子中有一个current的数据在调用model函数时会被加入session中,故在跳转其他页面,发送其他请求时,也可以取到这个数据(由于session的作用域)
index.jsp:
<h1>Map Test</h1>
<a href="${pageContext.request.contextPath }/test/map">map test</a>
<br>
<h1>Model test</h1>
<form action="${pageContext.request.contextPath }/test/model" method="post">
IdCard:<input type="text" name="idCard"/><br><br>
Name:<input type="text" name="name"/><br><br>
Age:<input type="text" name="age"/><br><br>
City:<input type="text" name="address.city"/><br><br>
Province:<input type="text" name="address.province"/><br><br>
<input type="submit" value="Submit"/>
</form>
<br>
success.jsp:
<h2>map result</h2>
Time:${requestScope.time }<br> <br>
Current from session :${sessionScope.current } <br> <br>
<h2>model result</h2>
Current from request :${requestScope.current } <br> <br>
Current from session :${sessionScope.current } <br> <br>
执行完会发现就算发送另一个请求,current这个数据依然可以从request域中取到,其实这个@SessionAttributes注解是把数据放入整个模型中,也放入session中了,故如果真要把数据放入session,可以使用原生的httpsession的api。
6 @ModelAttribute
此例子需要新创建一系列文件如下:
Student.java
public class Student {
private String id;
private String name;
private Integer age;
private Integer score;
student.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h1>Insert</h1>
<form action="${pageContext.request.contextPath }/student/update" method="post">
<input type="hidden" name="id" value="1"/>
Name:<input type="text" name="name" value="Jinondo"/><br><br>
Age:<input type="text" name="age" value="21"/><br><br>
<input type="submit" value="Submit"/>
</form>
<h1>Insert</h1>
<form action="${pageContext.request.contextPath }/student/insert" method="post">
Name:<input type="text" name="name"/><br><br>
Age:<input type="text" name="age"/><br><br>
Score:<input type="text" name="score"/><br><br>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
studentSuccess.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>student Success</title>
</head>
<body>
<h1>student Success Page</h1>
</body>
</html>
6.1 在方法定义上使用
在方法定义上使用 @ModelAttribute 注解:
Spring MVC 在调用目标处理方法前,会先逐个调用在方法级上标注了**@ModelAttribute** 的方法。
设计controller:
studentController.java
@RequestMapping("/student")
@Controller
public class StudentController {
private static final String SUCCESS = "studentSuccess";
@ModelAttribute
public void prepareModel(Integer id,HttpServletRequest request,Model model) {
System.out.println("prepare model data ......");
String servletPath = request.getServletPath();
// 判断是否是要执行update操作,即下一步的操作
if (servletPath.equals("/student/update")) {
System.out.println("Read student info from database by id" + id);
// 模拟从数据库中取出id为1的原来的数据
Student student = new Student("1", "jinondo", 21, 100);
model.addAttribute("student", student);
}
}
@PostMapping("/insert")
public String insert(Student student) {
System.out.println("insert student info ...... " + student);
return SUCCESS;
}
@PostMapping("/update")
public String update(Student student) {
System.out.println("update student info : " + student);
return SUCCESS;
}
}
update更改数据后控制台输出:
prepare model data ......
Read student info from database by id1
update student info : Student [id=1, name=cyt, age=123, score=100]
使用此注解意义就在于 更新数据时有一些数据项是不允许更改的,但当要把表单提交的新的对象数据输入到数据库时,那个不允许更新的数据项则会是null等,则保存到数据库的是错误的数据。
使用@ModelAttribute则可以提前准备数据,让框架内部自动为我们绑定数据,例如上面的例子中,首先prepareModel的方法把原始的数据对象给了model,在update是则会取出student这个对象进行数据绑定,更改的数据在对象中进行更改,score没有更改,也在初始时已绑定到对象中,故不会造成数据出错。
6.2 在方法入参前使用
在方法的入参前使用 @ModelAttribute 注解:
-
可以从隐含对象中获取隐含的模型数据,再与请求参数 绑定
-
将方法入参对象添加到模型中
其实功能就是在方法入参时更改你的数据的“键”,如果那个键在模型中有,就直接进行参数绑定,没有就新建一个放入模型进行数据绑定
例如:
studentController.java
@PostMapping("/update")
public String update(@ModelAttribute("student")Student student) {
System.out.println("update student info : " + student);
return SUCCESS;
}
在形参前注解,表示把student数据键设置为student,正如上面我们在prepareModel中在模型中传入了一个键为student的数据,两者一致则直接拿来做数据绑定,如果我们设置为@ModelAttribute("stu")
,则会新建一个键为stu的数据传入模型,这时模型就有了两个Student对象,上一节的效果则不复存在,即update时score会为null(指键为stu的那个对象)。
7 转发与重定向
7.1 redirect 重定向
用前面例子的代码演示:
@PostMapping("/insert")
public String insert(Student student) {
System.out.println("insert student info ...... " + student);
String redirectURL = "redirect:/test/testModelAndView";
return redirectURL;
}
将controller方法的返回字符串前加上redirect:
+ 要重定向的url路径
这里的路径可以是请求:
String redirectURL = "redirect:/test/testModelAndView";
也可以是静态资源:
String redirectURL = "redirect:/index.jsp";
也可以是url:
String redirectURL = "redirect:http://www.instagram.com";
7.2 forward 请求转发
@PostMapping("/insert")
public String insert(Student student) {
System.out.println("insert student info ...... " + student);
String redirectURL = "forward:/index.jsp";
return redirectURL;
}