创建Java Enterprise项目,选择Maven管理依赖
注意:由于SpringMVC并没有集成Web容器,所以仍需要依靠Tomcat等Web容器启动
<!-- war包打包 -->
<packaging>war</packaging>
<dependencies>
<!-- springmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- jsp -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- war包打包插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.0</version>
</plugin>
</plugins>
</build>
在web/WEB-INF/
目录或webapp/WEB-INF/
目录下,修改web.xml
文件,配置SpringMVC的前端(核心)控制器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- 项目访问路径 -->
<display-name>myweb</display-name>
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 开启一个专门用来支持WEB项目的WebApplicationContext容器 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 此处为项目编译后spring配置文件路径 -->
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!-- 正数:项目在启动时就会创建DispatcherServlet;负数:会在第一次访问时创建 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--此处使用*.do来标记动态请求,如果使用/的话,会导致静态请求404-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置自动扫描注解路径 -->
<context:component-scan base-package="top.ygang.*"></context:component-scan>
<!-- 开启MVC注解支持 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!--
静态资源处理方式一:交给Tomcat默认的defaultServlet处理
<mvc:default-servlet-handler></mvc:default-servlet-handler>
-->
<!--
静态资源处理方式二:将静态资源交给SpringMVC处理
mapping:指的是以static开头的url资源路径
location:指的是静态资源存放路径,多个路径可以使用逗号分隔
-->
<mvc:resources mapping="/static/**" location="classpath:/static/"></mvc:resources>
<!-- 视图解析器、设置controller转发、重定向路径默认前缀后缀,可选 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
在Spring2.5之前的版本,没有加入SpringMVC相关注解,所以自定义Controller需要实现rg.springframework.stereotype.Controller
接口,并且实现handleRequest
方法,然后在Spring配置文件applicationContext.xml
中配置bean
bean
标签对应的name
即为该Controller映射的路径
注意:这种方法基本现在已经没人用了,都是基于注解进行开发的
public class TestController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
return new ModelAndView("test");
}
}
<bean name="/test" class="top.ygang.springmvc_demo.controller.TestController"></bean>
Spring 2.5 版本新增了 Spring MVC 注解功能,用于替换传统的基于 XML 的 Spring MVC 配置。
不是所有的业务都需要持久层、业务层、控制层,可以根据自己的实际需求搭配
实体Entity
一般作为数据库表实体出现,封装数据库表实体数据
public class TestEntity {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "TestEntity{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
持久层Dao
一般作为与数据库交互层出现,主要是数据持久化相关代码逻辑
@Repository
public class TestDao {
public TestEntity test(){
TestEntity testEntity = new TestEntity();
testEntity.setAge(18);
testEntity.setName("lucy");
return testEntity;
}
}
业务层Service
一般作为实际业务层出现,主要是业务相关代码逻辑
@Service
public class TestService {
@Autowired
private TestDao testDao;
public TestEntity test(){
return testDao.test();
}
}
info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
name: ${entity.name}<br/>
age: ${entity.age}
</body>
</html>
控制层Controller
控制层中一般不会编写业务逻辑,主要是接收、响应前端数据,以及异常响应的处理
@Controller
public class InfoController{
@Autowired
private TestService testService;
@GetMapping("/info")
public ModelAndView info(){
ModelAndView modelAndView = new ModelAndView("info");
modelAndView.addObject("entity",testService.test());
return modelAndView;
}
}
控制层方法的形参,在满足条件的情况下,一般可以由SpringMVC框架自动注入(传参)
可以直接获取域对象:HttpServletRequest,HttpServletResponse,HttpSession,方便控制请求和响应
@PostMapping("/login")
public ModelAndView login(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
String loginName = request.getParameter("loginName");
String password = request.getParameter("password");
System.out.println(loginName);
System.out.println(password);
ModelAndView mv = new ModelAndView();
mv.setViewName("sysmag/main");
return mv;
}
@RequestMapping("/login")
public String login(@CookieValue("JSESSIONID") String sessionID) {
System.out.println(sessionID);
return "sysmag/main";
}
@RequestMapping("/login")
public String login(@RequestHeader("User-Agent") String userAgent) {
System.out.println(userAgent);
return "sysmag/main";
}
RequestEntity是封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()
获取请求头信息,通过getBody()
获取请求体信息,泛型为请求体数据类型
@PostMapping("/test")
public String test(RequestEntity<String> entity){
HttpHeaders headers = entity.getHeaders();
String body = entity.getBody();
System.out.println(headers);
System.out.println(body);
return "info";
}
除了通过域对象获取请求参数外,我们可以通过SpringMVC封装的参数解析器,接收解析常用类型的请求参数
参数拼接在url上,例如:http://localhost:8080/test?name=lucy&age=18
// 可以分别接收每个属性
@GetMapping("/test")
public String test(String name,int age){
System.out.println(name);
System.out.println(age);
return "info";
}
可以使用@RequestParam
注解指定参数名称,以及是否必传,默认情况下如果少参数会抛出异常
@GetMapping("/test")
public String test(@RequestParam(value = "name",required = false)String name, int age){
System.out.println(name);
System.out.println(age);
return "info";
}
还可以通过实体类接收
class User{
private String name;
private int age;
public User(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@GetMapping("/test")
public String test(User user){
System.out.println(user);
return "info";
}
也可以通过Map进行接收,必须使用@RequestParam
修饰
@GetMapping("/test")
public String test(@RequestParam Map<String,Object> map){
System.out.println(map);
return "info";
}
urlParams也可以传递数组或List,必须使用@RequestParam
修饰
http://localhost:8080/test?id=1&id=2
@GetMapping("/test")
public String test(@RequestParam int[] id){
System.out.println(Arrays.toString(id));
return "info";
}
@GetMapping("/test")
public String test(@RequestParam List<Integer> id){
System.out.println(id);
return "info";
}
rest风格参数,例如:http://localhost:8080/test/lucy/18
@GetMapping("/test/{name}/{age}")
public String test(@PathVariable("name") String name,@PathVariable("age") int age){
System.out.println(name);
System.out.println(age);
return "info";
}
存在于请求体的参数,常见的由application/json
、 multipart/form-data
、application/x-www-form-urlencoded
等
@PostMapping("/test")
public String test(String name, int age){
System.out.println(name);
System.out.println(age);
return "info";
}
也可以使用实体接收
class User{
private String name;
private int age;
public User(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@PostMapping("/test")
public String test(User user){
System.out.println(user);
return "info";
}
同样支持Map、Array、List
一般作为前端文件上传到后端的方式,SpringMVC的FormHttpMessageConverter
不支持读取该Content-Type
,所以如果需要解析这种类型
1、需要引入common-fileupload依赖
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2、Spring配置文件applicationContext.xml
中配置解析器
<!-- application/multipart-file解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置编码集 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 单次文件上传时的最大容量,单位:byte eg:100M=1024*1024*100,-1 表示不做限制-->
<property name="maxUploadSize" value="104857600"/>
<!-- 每次读取文件时,以1KB的方式来读取 -->
<property name="maxInMemorySize" value="1024"/>
</bean>
3、控制层接收文件字段
@PostMapping("/test")
public String test(String name,MultipartFile file){
System.out.println(name);
System.out.println(file.getOriginalFilename());
return "info";
}
默认的@RequestBody
,只会接收Body中的值,不会进行解析,例如json则需要我们自己解析
{
"name": "lucy",
"age": 18
}
@PostMapping("/test")
public String test(@RequestBody String json){
System.out.println(json);
return "info";
}
//{
// "name": "lucy",
// "age": 18
//}
我们可以定义json消息转换器,来自动解析application/json
类型数据
1、导入jackson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
2、使用@RequestBody
接收即可
@PostMapping("/test")
public String test(@RequestBody Map<String,Object> map){
System.out.println(map);
return "info";
}
或者使用实体接收
class User{
private String name;
private int age;
public User(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@PostMapping("/test")
public String test(@RequestBody User user){
System.out.println(user);
return "info";
}
ModelAndView 是Spring MVC框架中默认的返回类型,Model代表是模型数据,View代表的是需要跳转的页面(但是只是一个逻辑视图的名字),可以通过addObject进行传递数据,可以通过在构造器参数,或setViewName进行页面的跳转,return该对象即可
@RequestMapping("/login")
public ModelAndView login(String loginName){
System.out.println(loginName);
ModelAndView mv = new ModelAndView();
mv.setViewName("sysmag/main");
return mv;
}
默认是转发
String返回的字符串,就是需要跳转的页面,如果返回值是null,Spring MVC框架将会自动将请求路径作为即将要跳转的页面,所以尽量不要返回 null
@RequestMapping("/login")
public String login(String loginName){
System.out.println(loginName);
return "sysmag/main";
}
"forward:/sys/login02"
注意:forward后一定要是完整路径,可以用来转发控制层的路径
@RequestMapping("/login")
public String login(String loginName){
System.out.println(loginName);
return "forward:/sys/login02";
}
"redirect:/sys/login02"
@RequestMapping("/login")
public String login(String loginName){
System.out.println(loginName);
return "redirect:/sys/login02";
}
ModelAndView可以指定视图文件(.jsp
)路径,也可以给视图进行传参
@GetMapping("/info")
public ModelAndView info(ModelAndView modelAndView){
User user = new User();
user.setId(3L);
user.setUserName("王五");
modelAndView.addObject("user",user);
modelAndView.setViewName("userView");
return modelAndView;
}
ModelMap对象主要用于传递控制方法处理数据到结果页面,他的作用类似于request对象的setAttribute方法的作用,用来在一个请求过程中传递处理的数据
addAttribute(String key,Object value);
modelmap本身不能设置页面跳转的url地址别名或者物理跳转地址,那么我们可以通过控制器方法的返回值来设置跳转url地址别名或者物理跳转地址。
@RequestMapping("/useModelMap")
public String useModelMap(ModelMap modelMap) {
User user = new User();
user.setId(3L);
user.setUserName("王五");
modelMap.addAttribute("user", user);
return "userView";
}
ModelMap
主要用于传递控制器方法处理数据到页面。等同于ModelMap。
@RequestMapping("/userModel")
public String userModel(Model model) {
User user = new User();
user.setId(2L);
user.setUserName("李四");;
model.addAttribute("user", user);
return "userView";
}
org.springframework.http.converter.HttpMessageConverter
,报文消息转换器,主要用来控制层接收、响应消息时,对消息进行解析
public interface HttpMessageConverter<T> {
// 检查clazz对象是否能转换为mediaType表示的数据类型,这个mediaType是前端页面请求时设定的ContentType格式!
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
// 如果canRead方法返回值为true则调用read方法将数据进行格式转换!
T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
// 检查clazz对象是否能转换为mediaType类型,此时的mediaType表示后端想要响应给前端的数据格式!
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
// 如果canWrite返回值为true,则将数据进行格式的转换然后响应!
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
// 获取该转换器支持的mediaType,即ContentType
List<MediaType> getSupportedMediaTypes();
}
默认情况下SpringMVC框架在配置文件中不进行注解驱动<mvc:annotation-driven>
的支持情况下,会默认给应用使用4个实现类用于接受/响应数据的转换,在开启后会增加到7个左右(版本不同)。
实现类 | 说明 |
---|---|
StringHttpMessageConverter | 负责读取、写出String格式的数据 |
MappingJackson2HttpMessageConverter | 利用jackson的ObjectMapper读写Json数据,需要引入jackson依赖 |
StringHttpMessageConverter | 将请求信息转为字符串 |
FormHttpMessageConverter | 可以读写application/x-www-form-urlencoded 的数据,但只能用来写multipart/form-data 的数据, |
XmlAwareFormHttpMessageConverter | 扩展与FormHttpMessageConverter,如果部分表单属性是XML数据,可用该转换器进行读取 |
ResourceHttpMessageConverter | 读写org.springframework.core.io.Resource 对象 |
BufferedImageHttpMessageConverter | 读写BufferedImage对象 |
ByteArrayHttpMessageConverter | 读写二进制数据 |
SourceHttpMessageConverter | 读写java.xml.transform.Source 类型的对象 |
MarshallingHttpMessageConverter | 通过Spring的org.springframework,xml.Marshaller和Unmarshaller 读写XML消息 |
Jaxb2RootElementHttpMessageConverter | 通过JAXB2读写XML消息,将请求消息转换为标注的XmlRootElement和XmlType连接的类中 |
MappingJacksonHttpMessageConverter | 利用Jackson开源包的ObjectMapper读写JSON数据 |
RssChannelHttpMessageConverter | 读写RSS种子消息 |
AtomFeedHttpMessageConverter | 和RssChannelHttpMessageConverter能够读写RSS种子消息 |
web.xml
文件中添加过滤器
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>