SpringMVC配置
入门案例-环境配置
- 创建maven-webapp项目,如下图所示
-
在属性配置界面添加一对key-value,用于避免自动下载一大堆插件,因为会很慢
key:
archetypeCatalog
value:internal
-
自己配好文件夹
- 导入依赖包
spring-context是spring的核心框架
spring-web和spring-webmvc用于支持springMVC框架和web应用
javax.servlet-api和jsp-api用于支持servlet和jsp
junit用于支持单元测试,方便测试的时候使用
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.26.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.26.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.26.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
</dependency>
</dependencies>
-
建立springmvc配置文件
springmvc.xml
,并导入约束。此处需要导入两个命名空间,一个是xmlns:context
用于支持spring注解,xmlns:mvc
用于支持springmvc注解<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
-
web.xml配置servlet拦截
然后为了让我们写的springmvc.xml被识别到,需要指出它在哪。于是我们用
<init-param>
去说明。
配置filter是为了配置编码为UTF-8。所有的请求过来都会用UTF-8去解码。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dispatcherServlet</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>
<!-- 表示该servlet的启动顺序-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
入门案例-编写代码
-
建立index.jsp页面,包含链接
<a href="hello">click</a>
,指向hello
路径(相对路径)<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>hello</title> </head> <body> <h1>hello SpringMVC</h1> <a href="hello">click</a> </body> </html>
-
编写Controller类HelloController
-
@Controller
是spring的注解,用于注册Bean,与@Component
,@Service
,@Repositry
作用相同。为了区分这是个控制器而用了它。 -
@RequestMapping("/hello")
用于指定匹配的url,这里url为"/hello"
,需要包含/
- 要求有
@RequestMapping
注解的方法返回值是String
或者ModelAndView
,这里先写String。返回值是要跳转的页面名称,这个要结合后面视图解析器来用
package com.chajiu.contorller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloController { @RequestMapping("/hello") public String sayHello(){ System.out.println("hello SpringMVC"); return "success"; } }
-
-
编写要跳转的页面success.jsp
位置:在/WEB-INF/pages/下面,要和后面配置视图解析器对应
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>success</title>
</head>
<body>
<h1>success!</h1>
</body>
</html>
-
配置springmvc.xml
@RequestMapping
注解的方法返回的String都会先到视图解析器里,把前缀
和后缀
加上,拼接成完整的要跳转的页面的路径,再去访问。<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--开启spring注解支持,让spring扫描包--> <context:component-scan base-package="com.chajiu.contorller"></context:component-scan> <!--开启SpringMVC注解的支持--> <mvc:annotation-driven/> <!--配置视图解析器--> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/pages/"></property> <!--后缀--> <property name="suffix" value=".jsp"></property> </bean> </beans>
入门案例-启动
配好tomcat,启动,发现成功跳转。
@RequestMapping注解
用于匹配url。
类上也可以用它,表示一级拦截方式,方法上是二级拦截。
比如类上有@RequestMapping("/usr"),方法上有@RequestMapping("/hello"),那么只有"/usr/hello"的请求才能进入该方法。
属性:
value
:等同path,用于指定请求的urlpath
:等同value-
method
:规定接受url的请求方式,取值有GET,用于查询 HEAD, POST,用于增 PUT,用于更新 PATCH,用于更新 DELETE,用于删除 OPTIONS, TRACE;
-
params
:用于规定上传的参数必须包含指定的key@RequestMapping(path = "/hello",params = {"username","password"})
请求参数绑定
1. 基本数据类型
url:
hello?username=ccc&password=123
方法:
@RequestMapping("/hello")
public String getAccount(String username, String password){...}
属性名称和形参名称对应即可。
2. pojo类型
public class Account {
private Integer id;
private String username;
private String password;
//getter
//setter
如何注入Account?
提交的属性名和Account里属性名对应即可,方法参数直接写Account。如果没对上,则对应属性为null
<form action="param/saveAccount">
username:<input type="text" name="username"><br>
password:<input type="text" name="password"><br>
<input type="submit" value="submit">
</form>
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/saveAccount")
public String saveAccount(Account account){
System.out.println(account);
return "success";
}
}
pojo里包含引用类型怎么办
public class Account {
private Integer id;
private String username;
private String password;
<!--多了个user-->
private User user;
映射方法不变,jsp中修改input的name:
<form action="param/saveAccount">
username:<input type="text" name="username"><br>
password:<input type="text" name="password"><br>
name:<input type="text" name="user.name"><br>
age:<input type="text" name="user.age"><br>
<input type="submit" value="submit">
</form>
怀疑这个请求参数绑定的方式应该是形参的Bean加上提交的属性名,组成全限定类名再注入。
因此注入Account的User的name就要是com.chajiu.pojo.Accout.user.name,因此html中要写user.name
3. 集合类型
比如User有个List集合:
public class User {
private Integer id;
private String name;
private Integer age;
private List<Role> roles;
jsp要上传User数据,同时包含多个角色信息,那怎么办?
在name那做文章:
<form action="param/saveUser" method="post">
name:<input type="text" name="name"><br>
age:<input type="text" name="age"><br>
role:<input type="text" name="roles[0].role"><br>
<input type="submit" value="submit">
</form>
使用集合的属性名[i].子属性名
表示。List中第一个role是roles[0].roleName
,那么第二个role就是roles[1].roleName
如果是Map怎么表示?
role["key"].roleName
自定义类型转换器
提交数据都是字符串,如何会封装到Integer
,double
?springMVC有转换器会帮我们把常见的数据类型进行转换再存入pojo中。
但是有时候这种转换不是我们想要的效果。比如String到Date,他给的转换只能转1998/12/28这种类型,对于1998-12-28将无能为力,报错。
所以我们有时候需要自定义类型转换器。
自定义一个类,实现Convert<s,t>接口:
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
if (s==null)
throw new RuntimeException("请输入数据");
DateFormat dateFormat=new SimpleDateFormat("yyyy-mm-dd");
try {
return dateFormat.parse(s);
} catch (Exception e) {
throw new RuntimeException("输入格式有误");
}
}
}
修改springmvc.xml:
- 定义
ConversionServiceFactoryBean
的bean,并把你自己写的转换器注入converters
这个set集合中。 - 让mvc使用你这个
ConversionServiceFactoryBean
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.chajiu.converter.StringToDateConverter"></bean>
</set>
</property>
</bean>
<!-- 添加conversion-service,指向自己的转换器bean-->
<mvc:annotation-driven conversion-service="conversionService"/>
但是在修改后,只支持1998-12-28了。哈哈哈
拿到Servlet原生API
如何在方法中拿到request和response呢?
只要在你的方法形参中加入他们,springmvc在调用这个方法时就会自动绑定进去。
@RequestMapping("/saveUser")
public String savesUser(User user, HttpServletRequest req, HttpServletResponse res){
System.out.println(user);
System.out.println(req);
System.out.println(res);
HttpSession session = req.getSession();
System.out.println(session);
return "success";
}
常用注解
RequestParam
情况1:请求属性名和方法形参名对不上就没法匹配了吗?
比如你的url是 /user?name=aaa
,而方法形参为 String username
。
正常情况无法匹配,但是我们可以手动修改映射关系:
@RequestMapping("/requestParam")
public String testRequestParam(@RequestParam(name="name") String username){
意为让请求中的name
去匹配username
。
同时我们看出该注解只能修改基本类型的映射关系。对于pojo实体类无法起作用。
RequestBody
用于获取请求体内容。直接使用得到是 key=value&key=value...结构的数据。
get 请求方式不适用。
@RequestMapping("/requestBody")
public String testRequestBody(String username,@RequestBody String body){
PathVaribale
用于绑定占位符和形参,{sid}
为占位符。
使用占位符可以匹配多种url。特别是在RESTful风格的开发中,类似 /pathVariable/10
,/pathVariable/12
都会匹配到该方法。
@RequestMapping("/pathVariable/{sid}")
public String testPathVariable(@PathVariable(name="sid") String id){
RequestHeader
获取指定请求头部,比如我想获取Accept头部信息:
@RequestMapping("/requestHeader")
public String testRequestMapping(@RequestHeader("Accept") String accept){
CookieValue
用于获得指定的cookie值.
同样是绑定到某个形参上使用
@RequestMapping("/cookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String jsessionid){
ModelAttribute
给model添加属性的。在执行所有控制方法前先执行。
应用场景:要在控制方法执行前对请求参数做某些操作,比如修改值,填充null。
两种使用方式:
- 修饰方法
- 修饰形参
修饰方法:
- 方法有返回值时,据说自动会把返回值作为value,小写类名作为key放入modelMap中,也就是放入requestScope中。
- 也可以@ModelAttribute(value="abc")来指定key为abc
@ModelAttribute
public Account show(String username,String password){
Account account=new Account();
account.setId(1);
account.setUsername(username);
account.setPassword(password);
return account;
}
-
方法没返回值时,要放入需要先显式地绑定modelMap,然后用put方法自己放进去
@ModelAttribute public void show(String username, String password, Map<String , Account> accountMap){ Account account=new Account(); account.setId(1); account.setUsername(username); account.setPassword(password); accountMap.put("acco",account); }
至于这里为什么声明了一个Map呢?因为modelMap是他实现类。接口 指向实现类是可以的。下面请看关系图:
可以看出其实modelMap其实就是个Map,里面可以存放键值对。
修饰形参:
从modelMap中取value给形参赋值
-
下面就把acco对应的对象和形参account绑定了
@RequestMapping("/modelAttribute") public String testModelAttribute(@ModelAttribute("acco") Account account){ System.out.println(account); return "success"; }
SessionAttributes
为了减少耦合,所以在把键值对放入requestScope中时,我们没有直接用元素的ServletAPI,而是用了一个中间对象ModelMap,把键值对交给他,他自动会存入RequestScope中。
那么如何不直接操作Session而把键值对放入SessionScope中呢?
使用SessionAttributes
注解,会把modelMap中指定的键值对放入SessionScope中。
需要在类上使用:
@Controller
@SessionAttributes(value={"username"})
@RequestMapping("/anno")
public class AnnoController {
会把modelMap中key为username
的属性放入SessionScope。当然可以指定多个。
方法中获取SessionScope中的对象:
- 使用ModelMap的get方法。
- 这个方法也能从requestScope中获取对象。
@RequestMapping("/getSessionAttribute")
public String getSessionAttribute(ModelMap modelMap){
String uname= (String) modelMap.get("username");
System.out.println(uname);
return "sessionAttributePage";
}
其实我感觉,SpringMVC把jsp的四大作用域都封装在了ModelMap中。所以用ModelMap.get()方法应该也是从四大域中取值。只是查找的顺序不同。从而避免我们之间操作四大域,减少耦合。