- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
【前置内容】Spring 学习笔记全系列传送门:
Spring学习笔记 - 第一章 - IoC(控制反转)、IoC容器、Bean的实例化与生命周期、DI(依赖注入) 。
Spring学习笔记 - 第二章 - 注解开发、配置管理第三方Bean、注解管理第三方Bean、Spring 整合 MyBatis 和 Junit 案例 。
Spring学习笔记 - 第三章 - AOP与Spring事务 。
SpingMVC 学习笔记全系列传送门:
- 【本章】SpringMVC学习笔记 - 第一章 - 工作流程、Bean加载控制、请求与响应(参数接收与内容返回)、RESTful
@ComponentScan
@RequestParam
@ResponseBody
@PathVarlable
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架 。
优点 。
- 使用简单、开发便捷(相比于Servlet)
- 灵活性强
SpringMVC主要负责的就是 。
- controller如何接收请求和数据
- 如何将请求和数据转发给业务层
- 如何将响应数据转换成json发回到前端
三层架构与MVC模式 。
浏览器发送一个请求给后端服务器,后端服务器现在是使用Servlet来接收请求和数据 。
如果所有的处理都交给Servlet来处理的话,所有的东西都耦合在一起,对后期的维护和扩展极为不利 。
将后端服务器Servlet拆分成三层,分别是 web 、 service 和 dao 。
servlet处理请求和数据的时候,存在的问题是一个servlet只能处理一个请求 。
针对web层进行了优化,采用了MVC设计模式,将其设计为 controller 、 view 和 Model 。
随着互联网的发展,上面的模式因为是同步调用,性能慢慢的跟不是需求,所以异步调用慢慢的走到了前台,是现在比较流行的一种处理方式 。
- SpringMVC是基于Spring的,在pom.xml只导入了
spring-webmvc
jar包的原因是它会自动依赖spring相关坐标- AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
- AbstractDispatcherServletInitializer提供了三个接口方法供用户实现
- createServletApplicationContext方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围
- getServletMappings方法,设定SpringMVC对应的请求映射路径,即SpringMVC拦截哪些请求
- createRootApplicationContext方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式和createServletApplicationContext相同。
- createServletApplicationContext用来加载SpringMVC环境
- createRootApplicationContext用来加载Spring环境
创建 Maven-webapp 项目,整理包结构 。
导入依赖 。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>priv.dandelion</groupId>
<artifactId>01_quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建 Controller 。
// 定义Controller,声明为Spring的bean
@Controller
public class UserController {
// 设置当前操作的访问路径
@RequestMapping("/save")
// 设置当前操作的返回值类型
@ResponseBody
public String save() {
System.out.println("user----save");
// 相应的内容直接返回
return "{'hello':'springmvc'}";
}
}
创建配置类 。
// 创建SpringMVC的配置文件,加载controller对应的bean
@Configuration
@ComponentScan("priv.dandelion.controller")
public class SpringMvcConfig {
}
定义 Servlet 容器启动的配置类( 代替 web.xml ) 。
// 定义一个servlet容器启动的配置类,在此处加载spring配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
// 加载SpringMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
// 注册配置
ctx.register(SpringMvcConfig.class);
// tomcat服务器启动时就可以加载到SpringMvcConfig.class
return ctx;
}
// 设置那些请求归属于SpringMVC处理
@Override
protected String[] getServletMappings() {
// 将所有请求交给SpringMVC处理
return new String[]{"/"};
}
// 加载Spring容器配置,此处暂时未用到
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
@Controller 。
名称 | @Controller |
---|---|
类型 | 类注解 |
位置 | SpringMVC控制器类定义上方 |
作用 | 设定SpringMVC的核心控制器bean |
@RequestMapping 。
名称 | @RequestMapping |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
@ResponseBody 。
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
包含关系:
- Web容器
- ServletContext
- WebApplicationContext
- UserController
- /save -> save() 【SpringMVC 的映射并不是放在 bean 中管理的】
服务器启动,执行 web 服务器配置类 ServletContainersInitConfig,初始化web容器 。
- 功能类似于以前的web.xml
执行createServletApplicationContext方法,创建了WebApplicationContext对象(存在于 ServletContext 中) 。
该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器 。
// 加载SpringMVC容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); // 注册配置 ctx.register(SpringMvcConfig.class); // tomcat服务器启动时就可以加载到SpringMvcConfig.class return ctx; }
加载SpringMvcConfig配置类,以在下一步加载所需的bean 。
// 创建SpringMVC的配置文件,加载controller对应的bean @Configuration @ComponentScan("priv.dandelion.controller") public class SpringMvcConfig { }
执行@ComponentScan加载对应的bean 。
扫描指定包及其子包下所有类上的注解,如Controller类上的@Controller注解 。
加载UserController,每个@RequestMapping的名称对应一个具体的方法 。
此时就建立了 /save 和 save() 方法的对应关系 。
// 定义Controller,声明为Spring的bean @Controller public class UserController { // 设置当前操作的访问路径 @RequestMapping("/save") // 设置当前操作的返回值类型 @ResponseBody public String save() { System.out.println("user----save"); // 相应的内容直接返回 return "{'hello':'springmvc'}"; } }
执行getServletMappings方法,设定SpringMVC拦截请求的路径规则 。
/ 代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求 / 代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求 。
// 设置那些请求归属于SpringMVC处理 @Override protected String[] getServletMappings() { // 将所有请求交给SpringMVC处理 return new String[]{"/"}; }
发送请求 http://localhost/save 。
web容器发现该请求满足SpringMVC拦截规则,将请求交给 SpringMVC 处理 。
解析请求路径 /save 。
由 /save 匹配执行对应的方法 save() 。
执行 save() 。
检测到有 @ResponseBody 直接将 save() 方法的返回值作为响应体返回给请求方 。
问题:
- 哪些 bean 交给 SpringMVC 管理,哪些包交给 Spring 管理
- 因为功能不同,如何避免 Spring 错误加载到 SpringMVC 的 bean
包结构 。
config目录存入的是配置类,本篇和前面的内容已经写过的配置类有
controller 目录存放的是 SpringMVC 的 controller 类 。
service 目录存放的是 service 接口和实现类 。
dao 目录存放的是 dao/Mapper 接口 。
管理 。
加载Spring控制的bean的时候排除掉 SpringMVC 控制的 bean 。
创建 Web 的 Maven 项目,删除 web.xml 配置文件 。
依赖 。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>priv.dandelion</groupId>
<artifactId>02_bean_load</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建对应的配置类 。
替代 web.xml 的 web 服务器配置类 ServletContainersInitConfig 。
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
SpringMVC 配置类 。
@Configuration
@ComponentScan("priv.dandelion.controller")
public class SpringMvcConfig {
}
Spring 配置类 。
@Configuration
@ComponentScan("priv.dandelion")
public class SpringConfig {
}
实体类 。
public class User {
private Integer id;
private String name;
private Integer age;
// Getter
// Settrt
// toString
}
Dao 接口 。
public interface UserDao {
@Insert("insert into tbl_user(name,age)values(#{name},#{age})")
public void save(User user);
}
Service 实现类 (接口不表) 。
@Service
public class UserServiceImpl implements UserService {
public void save(User user) {
System.out.println("user service ...");
}
}
Controller 。
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
}
web服务器启动时加载配置类的相关配置 。
标准方式 。
- 方式
- 修改 createRootApplicationContext() 方法中的内容
- 与 createServletApplicationContext() 基本相同但是加载 SpringConfig.class
- 说明
- createServletApplicationContext() 加载的是 SpringMVC 环境配置
- createRootApplicationContext() 加载的是 Spring 环境配置
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
简化方式 。
说明:
- AbstractDispatcherServletInitializer 类包含一个子类 AbstractAnnotationConfigDispatcherServletInitializer 可以简化配置
- 方法名中包含 RootConfig 的是 Spring 的配置,包含 ServletConfig 的是对 SpringMVC 的配置,与标准方式中的相同
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
bean 加载控制方式 。
方案一:修改Spring 配置类,精准扫描 。
说明:
- 此处使用了MyBatis技术且是自动代理,所以可以不扫Dao
- 但是建议按照标准开发规则书写,通用性强
@Configuration
@ComponentScan({"priv.dandelion.service","priv.dandelion.dao"})
public class SpringConfig {
}
方案二 。
说明:
扫描所有的包 。
但是使用过滤器进行排除 。
- 过滤的类型是按注解过滤,过滤Controller注解
使用到的属性:
excludeFilters属性:设置扫描加载bean时,排除的过滤规则 。
type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除 。
- ANNOTATION:按照注解排除
- ASSIGNABLE_TYPE:按照指定的类型过滤
- ASPECTJ:按照Aspectj表达式排除,基本上不会用
- REGEX:按照正则表达式排除
- CUSTOM:按照自定义规则排除
大家只需要知道第一种ANNOTATION即可 。
classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean 。
@Configuration
@ComponentScan(
value = "priv.dandelion",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
方式三(不区分 Spring 与 SpringMVC 的环境)【此处不做详细说明】 。
@ComponentScan
名称 | @ComponentScan |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置spring配置类扫描路径,用于加载使用注解格式定义的bean |
相关属性 | excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)和具体项(classes) includeFilters:加载指定的bean,需要指定类别(type)和具体项(classes) |
本小节注意:
- 当类上和方法上都添加了
@RequestMapping
注解,前端发送请求的时候,要和两个注解的value值相加匹配才能访问到。- @RequestMapping注解value属性前面加不加
/
都可以
依赖 。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>priv.dandelion</groupId>
<artifactId>03_request_mapping</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置类 。
Spring 配置类(此处未使用到) 。
@Configuration
@ComponentScan(value = "priv.dandelion",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
SpringMVC 配置类 。
@Configuration
@ComponentScan("priv.dandelion.controller")
public class SpringMvcConfig {
}
web服务器配置类 (简化配置) 。
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
}
Controller 。
UserController 。
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(){
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
}
BookController 。
@Controller
public class BookController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("book save ...");
return "{'module':'book save'}";
}
}
以上环境准备完成后,启动服务器时会报错 。
[INFO] Initializing Servlet 'dispatcher' [WARNING] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'userController' method priv.dandelion.controller.UserController#save() to { /save}: There is already 'bookController' bean method priv.dandelion.controller.BookController#save() mapped. [ERROR] Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'userController' method priv.dandelion.controller.UserController#save() to { /save}: There is already 'bookController' bean method priv.dandelion.controller.BookController#save() mapped. at ... at ... ...
从错误信息可知 。
http://localhost/save
http://localhost/save
http://localhost/saved
的时候,到底是访问 UserController 还是 BookController,就会出现冲突 解决方案:为不同模块设置模块名作为请求路径前置 。
http://localhost/book/save
http://localhost/user/save
方案一(耦合度高不推荐):对每一个资源的 RequestMapping 进行修改 。
UserController 。
@Controller
public class UserController {
@RequestMapping("/user/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
@RequestMapping("/user/delete")
@ResponseBody
public String delete(){
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
}
BookController(不表) 。
方案二:为 Controller 添加一个整体的 RequestMapping(称为 请求路径前缀 ),其他不变 。
UserController 。
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(){
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
}
BookController(不表) 。
依赖、配置类见 3.1.1 。
实体类 。
Address 。
public class Address {
private String province;
private String city;
// Getter,Setter,toString不表
}
User 。
public class User {
private String name;
private int age;
// Getter,Setter,toString不表
}
Controller 。
@Controller
public class UserController {
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(){
return "{'module':'commonParam'}";
}
}
Get请求与参数
http://localhost/commonParam?name=dandelion&age=18
POST请求与参数
- 发送Post请求时,参数放在请求体中,若使用PostMan工具,参数需要写在Body模块中,发送表单数据时使用
x-www-from-urlencoded
(form-data
除了发送表单之外还可以发送文件)
GET请求 。
接收参数 。
@Controller
public class UserController {
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name, int age){
System.out.println("普通参数name:"+ name);
System.out.println("普通参数age:"+ age);
return "{'module':'commonParam'}";
}
}
GET中文乱码:配置pom.xml 。
Tomcat8.5以后的版本已经处理了中文乱码的问题,但是IDEA中的Tomcat插件目前只到Tomcat7,所以需要修改pom.xml来解决GET请求中文乱码问题 。
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port><!--tomcat端口号-->
<path>/</path> <!--虚拟目录-->
<uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集-->
</configuration>
</plugin>
</plugins>
</build>
POST请求 。
接收参数 。
POST请求接收参数代码与GET请求一致 。
POST中文乱码问题:设置过滤器 。
在web服务器配置类 ServletContainersInitConfig 中重写 getServletFilters() 方法,创建所需的过滤器对象,并设置编码字符集为 UTF-8 。
CharacterEncodingFilter 是在 spring-web 包中,所以用之前需要导入对应的 jar 包 。
import org.springframework.web.filter.CharacterEncodingFilter;
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
普通参数的基本使用已经实现过,详见 3.2.2 参数传递及中文乱码处理方案 。
解决请求中的参数名称和 Controller 方法的参数不一致问题 。
- 当出现请求中的参数名称和Controller中方法的参数名不匹配时,无法正常接收到参数
- 使用 @RequestPaam() 注解修饰不一致的参数,为其指定需要匹配的请求参数
@Controller
public class UserController {
// `http://localhost/commonParam?username=dandelion&age=12`
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(@RequestParam("username") String name, int age){
System.out.println("普通参数name:"+ name);
System.out.println("普通参数age:"+ age);
return "{'module':'commonParam'}";
}
}
- 直接使用一个实体类作为形参,框架会使用 setter 自动将数据进行写入
- 实体类中的属性名称需要和请求参数的名称保持一致,否则接收不到
- 若实体类中没有对应的 setter 可以和请求参数的参数名匹配,则默认零假空(未进行写入),可以使用该特性在实际开发中减少工作量
实体类 。
public class User {
private String name;
private int age;
// Getter,Setter,toString不表
}
Controller 。
@Controller
public class UserController {
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("POJO参数:"+ user);
return "{'module':'pojoParam'}";
}
}
对于嵌套的POJO类型,在进行参数传递时,也要使用嵌套的形式来书写请求参数名称 。
http://localhost/pojoParam?name=dandelion&age=12&address.province=hubei&address.city=wuhan 。
实体类 。
User 。
public class User {
private String name;
private int age;
private Address address;
// Getter,Setter,toString不表
}
Address 。
public class Address {
private String province;
private String city;
// Getter,Setter,toString不表
}
Controller 。
Controller 部分代码与 3.3.3 嵌套 POJO 类型参数一致 。
- 请求参数为数组时,不同的数组元素使用相同的请求参数名称
- 接收请求参数时,使用数组作为形参
请求 。
http://localhost/arrayParam?person=zhangsan&person=lisi&person=wangwu
接收 。
@Controller
public class UserController {
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] person){
System.out.println("数组参数:"+ Arrays.toString(person));
return "{'module':'arrayParam'}";
}
}
请求 。
与数组参数的请求方式相同 。
接收 。
错误案例 。
以下代码段运行时会报错: NoSuchMethodException: java.util.List.<init>() ,缺少构造,(内心:废话 List 接口哪来的构造) 。
严重: Servlet.service() for servlet [dispatcher] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No primary or default constructor found for interface java.util.List] with root cause java.lang.NoSuchMethodException: java.util.List.<init>()
@Controller
public class UserController {
@RequestMapping("/listParam")
@ResponseBody
public String listParam(List<String> person){
System.out.println("集合参数:"+ person);
return "{'module':'listParam'}";
}
}
原因及解决方案 。
问题原因 。
SpringMVC 将 List 看做是一个 POJO 对象来处理,将其创建一个对象并准备把前端的数据封装到对象中,但是 List 是一个接口无法创建对象,所以报错.
解决方案:使用 @RequestParam 注解 。
- 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam 绑定参数关系
- 显而易见,对于简单数据类型使用数组会比集合更简单些。
@Controller
public class UserController {
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> person){
System.out.println("集合参数:"+ person);
return "{'module':'listParam'}";
}
}
@RequestParam
名称 | @RequestParam |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定请求参数与处理器方法形参间的关系 |
相关参数 | required:是否为必传参数 defaultValue:参数默认值 |
- 参数放在请求体中,若使用 PostMan 工具,参数需要写在Body模块中,发送表单数据时使用
raw
,并将数据格式修改为 JSON
参数分类 。
// json普通数组
["value1","value2","value3",...]
// json对象
{"key1":"value1","key2":"value2",...}
// json对象数组
[{"key11":"value11",...},{"key21":"value21",...}]
准备工作 。
添加依赖 。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
开启SpringMVC注解驱动,用于开启 json 数据类型自动转换: @EnableWebMvc 。
@Configuration
@ComponentScan("priv.dandelion.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
参数前添加@RequestBody 。
- 使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
- 区别于 @RequestParam,本小节总结部分会进行说明
- 区别于 @ResponseBody,书写要正确
- 下文中展示
JSON 。
["zhangsan","lisi","wangwu"]
接收 。
@RequestMapping("/listParamForJson")
@ResponseBody
// //使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
public String listParamForJson(@RequestBody List<String> person){
System.out.println("list common(json)参数传递 list:" + person);
return "{'module':'list common for json param'}";
}
- 若 JSON 对象中的 key 与实体类中的 setter 名称(标准书写)不能匹配时,不执行 setter ,实体类中的数据不变(不进行其他操作时默认为零假空)
- 同理,若不传递某一属性的值,实体类中的数据不变(不进行其他操作时默认为零假空)
JSON 。
// 单个POJO
{
"name":"dandelion",
"age":12
}
// 嵌套POJO
{
"name1":"dandelion",
"age":12,
"address":{
"province":"provinceName",
"city":"cityName"
}
}
实体类 。
见 3.3.3 。
接收 。
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user:"+user);
return "{'module':'pojo for json param'}";
}
JSON 。
[
{"name":"dandelion","age":15,"address":{"province":"provinceName","city":"cityName"}},
{"name":"dandelion000","age":12}
]
接收 。
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
System.out.println("list pojo(json)参数传递 list:"+list);
return "{'module':'list pojo for json param'}";
}
知识点1: @EnableWebMvc 。
名称 | @EnableWebMvc |
---|---|
类型 | 配置类注解 |
位置 | SpringMVC配置类定义上方 |
作用 | 开启SpringMVC多项辅助功能 |
知识点2: @RequestBody 。
整理 。
名称 | @RequestBody |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次 |
@RequestBody与@RequestParam区别 。
区别 。
应用 。
接收案例 。
请求 。
http://localhost/dataParam?date=2022/02/22
接收 。
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date, Date date1){
System.out.println("参数传递 date:"+date);
System.out.println("参数传递 date1:"+date1);
return"{'module':'data param'}";
}
结果 。
发送请求后会发现该部分代码会报错,但若请求参数中只有date而没有date1时则正常接收 。
报错信息:
方法参数类型不匹配,在将 String 转换为 Date 时出现问题,转换失败 。
[WARNING] Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2022-02-22'; nested exception is java.lang.IllegalArgumentException]
解决方案(接收任意日期格式的方法):使用 @DateTimeFormat 指定日期格式 。
请求 。
http://localhost/dataParam?date=2022/02/22&date1=22-02-2022&date2=2022-02-22 22:22:22
接收 。
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
@DateTimeFormat(pattern = "dd-MM-yyyy") Date date1,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date2){
System.out.println("参数传递 date:"+ date);
System.out.println("参数传递 date1(dd-MM-yyyy):"+ date1);
System.out.println("参数传递 date2(yyyy-MM-dd HH:mm:ss):"+ date2);
return"{'module':'data param'}";
}
相关知识点 。
@DateTimeFormat 。
名称 | @DateTimeFormat |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参前面 |
作用 | 设定日期时间型数据格式 |
相关属性 | pattern:指定日期时间格式字符串 |
内部实现原理 。
SpringMVC中提供了很多类型转换接口和实现类,其中有 Converter 接口 。
Converter 接口 。
Converter所属的包为 org.springframework.core.convert.converter 。
框架中有提供很多对应Converter接口的实现类,用来实现不同数据类型之间的转换,如
请求参数年龄数据(String→Integer) 。
日期格式转换(String → Date) 。
/**
* S: the source type
* T: the target type
*/
public interface Converter<S, T> {
@Nullable
//该方法就是将从页面上接收的数据(S)转换成我们想要的数据类型(T)返回
T convert(S source);
}
HttpMessageConverter 接口 。
该接口是实现对象与 JSON 之间的转换工作, 使用时在SpringMVC的配置类把@EnableWebMvc当做标配配置上去,不省略 。
依赖 。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>priv.dandelion</groupId>
<artifactId>05_response</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置类 。
服务器配置类 。
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
}
SpringMVC 配置类 。
@Configuration
@ComponentScan("priv.dandelion.controller")
public class SpringMvcConfig {
}
实体类 。
public class User {
private String name;
private int age;
//getter...setter...toString省略
}
webapp下创建页面 page.jsp 。
<html>
<body>
<h2>Hello Spring MVC!</h2>
</body>
</html>
Controller 。
@Controller
public class UserController {
}
- 注意此处不能使用@ResponseBody,否则会将返回值内容作为字符串返回给前端
- 注意进行页面跳转时,返回值为页面名称,返回值类型为字符串
@RequestMapping("/toJumpPage")
public String toJumpPage() {
System.out.println("跳转页面");
return "page.jsp";
}
- 注意此处 @ResponseBody 注解就不能省略
- 如果省略了会把
response text
当前页面名称去查找,如果没有回报404
@RequestMapping("/toText")
@ResponseBody
public String toText() {
System.out.println("返回纯文本数据");
return "response text";
}
准备工作:
开启SpringMVC注解驱动,用于开启 json 数据类型自动转换: @EnableWebMvc 。
@Configuration @ComponentScan("priv.dandelion.controller") @EnableWebMvc public class SpringMvcConfig { }
@RequestMapping("/toJsonPOJO")
@ResponseBody
public User toJsonPOJO() {
System.out.println("返回JSON数据对象");
User user = new User();
user.setName("dandelion");
user.setAge(12);
return user;
}
此处返回的是POJO的集合,基本数据类型的集合同理 。
@RequestMapping("/toJsonList")
@ResponseBody
public List<User> toJsonList() {
System.out.println("返回JSON数据对象");
User user1 = new User();
user1.setName("dandelion");
user1.setAge(12);
User user2 = new User();
user2.setName("dandelion000");
user2.setAge(15);
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
@ResponseBody
整理 。
名称 | @ResponseBody |
---|---|
类型 | 方法\类注解 |
位置 | SpringMVC控制器方法定义上方和控制类上 |
作用 | 设置当前控制器返回值作为响应体, 写在类上,该类的所有方法都有该注解功能 |
相关属性 | pattern:指定日期时间格式字符串 |
说明 。
该注解可以写在类上或者方法上 。
写在类上就是该类下的所有方法都有@ReponseBody功能 。
当方法上有@ReponseBody注解后 。
此处又使用到了类型转换,内部还是通过Converter接口的实现类完成的,所以Converter除了前面所说的功能外,它还可以实现
对象转Json数据(POJO -> json) 。
集合转Json数据(Collection -> json) 。
REST(Representational State Transfer),表现形式状态转换,它是一种软件架构风格 。
REST风格与传统风格的区别 。
传统风格资源描述形式 。
http://localhost/user/getById?id=1
查询id为1的用户信息 http://localhost/user/saveUser
保存用户信息 REST风格描述形式 。
http://localhost/user/1
http://localhost/user
REST风格的优点 。
REST风格的使用 。
按照REST风格访问资源时使用 行为动作 区分对资源进行了何种操作 。
http://localhost/users
查询全部用户信息 http://localhost/users/1
查询指定用户信息 http://localhost/users
添加用户信息 http://localhost/users
修改用户信息 http://localhost/users/1
删除用户信息 请求方式:按照不同的请求方式代表不同的操作类型 。
注意 。
RESTful 。
依赖 。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>priv.dandelion</groupId>
<artifactId>06_rest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置类 。
服务器配置类 。
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
}
SpringMVC配置类,开启json数据类型自动转换 。
@Configuration
@ComponentScan("priv.dandelion.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
实体类 。
User 。
public class User {
private String name;
private int age;
//getter...setter...toString省略
}
Book 。
public class Book {
private String name;
private double price;
//getter...setter...toString省略
}
Controller 。
UserController 。
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(@RequestBody User user) {
System.out.println("user save..."+user);
return "{'module':'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String delete(Integer id) {
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
@RequestMapping("/update")
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update..." + user);
return "{'module':'user update'}";
}
@RequestMapping("/getById")
@ResponseBody
public String getById(Integer id) {
System.out.println("user getById..." + id);
return "{'module':'user getById'}";
}
@RequestMapping("/findAll")
@ResponseBody
public String getAll() {
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
}
BookController 。
@Controller
public class BookController {
@RequestMapping(value = "/books",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
@RequestMapping(value = "/books/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
@RequestMapping(value = "/books",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
@RequestMapping(value = "/books/{id}",method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
@RequestMapping(value = "/books",method = RequestMethod.GET)
@ResponseBody
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
- 将之前的增删改查替换成RESTful的开发方式
- 修改前: 新增: /save ,修改: /update,删除 /delete...
- 修改后:
- 增删改查: /users
- 根据GET查询、POST新增、PUT修改、DELETE删除对方法的请求方式进行限定
新增 。
请求 。
POST http://localhost/users
接收 。
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody User user) {
System.out.println("user save..."+user);
return "{'module':'user save'}";
}
删除 。
请求 。
DELETE http://localhost/users/1
接收 。
使用REST风格时,参数写在请求路径中 。
接收时对参数使用 @PathVariable 注解,在资源路径的参数位置使用 {} ,其中的值应和参数名称保持一致 。
若参数名称和 {} 中的内容不一致,应手动进行绑定,使用 @PathVariable 注解的 value 属性,如下:
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable("id") Integer userId) {}
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
修改 。
请求 。
PUT http://localhost/users
接收 。
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update..." + user);
return "{'module':'user update'}";
}
查询 。
查询单个 。
请求 。
GET http://localhost/users/1
接收 。
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id) {
System.out.println("user getById..." + id);
return "{'module':'user getById'}";
}
查询所有 。
请求 。
GET http://localhost/users
接收 。
@RequestMapping(value = "/users", method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
@PathVarlable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
区别 。
应用 。
未简化代码 。
@Controller
public class BookController {
@RequestMapping(value = "/books",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
@RequestMapping(value = "/books/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
@RequestMapping(value = "/books",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
@RequestMapping(value = "/books/{id}",method = RequestMethod.GET)
@ResponseBody
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
@RequestMapping(value = "/books",method = RequestMethod.GET)
@ResponseBody
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
简化过程(参见注释) 。
- 将请求路径中共同的部分抽取
- 将方法上@RequestMapping中不需要的value属性省略
- 将所有方法上的@ResponseBody抽取
- 存在@Controller和@ResponseBody,可以使用@RestController代替
- 使用@PostMapping、@DeleteMapping等注解代替@RequestMapping
// @Controller
// 3.所有方法均需要@ResponseBody,抽取
// @ResponseBody
@RestController // 4.其中包含了@Controller和@ResponseBody
// 1.以前缀的形式将请求路径中相同的内容抽取出来
@RequestMapping("/books")
public class BookController {
// 2.value属性已经完全被抽取,可以省略
// @RequestMapping(method = RequestMethod.POST)
// 5.简化上一行代码,下同
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
// @RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
// @RequestMapping(value = "/{id}",method = RequestMethod.GET)
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
// @RequestMapping(method = RequestMethod.GET)
@GetMapping
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
简化后代码 。
@RestController
@RequestMapping("/books")
public class BookController {
@PostMapping
public String save(@RequestBody Book book){
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id){
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
@PutMapping
public String update(@RequestBody Book book){
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
@GetMapping
public String getAll(){
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
知识点1:@RestController 。
名称 | @RestController |
---|---|
类型 | 类注解 |
位置 | 基于SpringMVC的RESTful开发控制器类定义上方 |
作用 | 设置当前控制器类为RESTful风格, 等同于@Controller与@ResponseBody两个注解组合功能 |
知识点2:@GetMapping @PostMapping @PutMapping @DeleteMapping 。
名称 | @GetMapping @PostMapping @PutMapping @DeleteMapping |
---|---|
类型 | 方法注解 |
位置 | 基于SpringMVC的RESTful开发控制器方法定义上方 |
作用 | 设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作, 例如@GetMapping对应GET请求 |
相关属性 | value(默认):请求访问路径 |
依赖 。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>priv.dandelion</groupId>
<artifactId>07_rest_case</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置类 。
服务器配置类 。
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[]{characterEncodingFilter};
}
}
SpringMVC配置类 。
@Configuration
@ComponentScan("priv.dandelion.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
实体类 。
public class Book {
private Integer id;
private String type;
private String name;
private String description;
//setter...getter...toString略
}
控制器 。
@Controller
public class BookController {
}
@RestController
@RequestMapping("/books")
public class BookController {
@PostMapping
public String save(@RequestBody Book book) {
System.out.println("book save ==> "+ book);
return "{'module':'book save success'}";
}
@GetMapping
public List<Book> getAll() {
System.out.println("book getAll is running ...");
List<Book> bookList = new ArrayList<Book>();
Book book1 = new Book();
book1.setType("计算机");
book1.setName("SpringMVC1");
book1.setDescription("123");
bookList.add(book1);
Book book2 = new Book();
book2.setType("计算机");
book2.setName("SpringMVC2");
book2.setDescription("234");
bookList.add(book2);
Book book3 = new Book();
book3.setType("计算机丛书");
book3.setName("SpringMVC3");
book3.setDescription("345");
bookList.add(book3);
return bookList;
}
}
页面准备 。
环境:vue.js + axios + elementui 。
页面 。
<!DOCTYPE html>
<html>
<head>
<!-- 页面meta -->
<meta charset="utf-8">
<title>SpringMVC案例</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="../plugins/elementui/index.css">
<link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/style.css">
</head>
<body class="hold-transition">
<div id="app">
<div class="content-header">
<h1>图书管理</h1>
</div>
<div class="app-container">
<div class="box">
<div class="filter-container">
<el-input placeholder="图书名称" style="width: 200px;" class="filter-item"></el-input>
<el-button class="dalfBut">查询</el-button>
<el-button type="primary" class="butT" @click="openSave()">新建</el-button>
</div>
<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
<el-table-column type="index" align="center" label="序号"></el-table-column>
<el-table-column prop="type" label="图书类别" align="center"></el-table-column>
<el-table-column prop="name" label="图书名称" align="center"></el-table-column>
<el-table-column prop="description" label="描述" align="center"></el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="mini">编辑</el-button>
<el-button size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
class="pagiantion"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
layout="total, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</div>
<!-- 新增标签弹层 -->
<div class="add-form">
<el-dialog title="新增图书" :visible.sync="dialogFormVisible">
<el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="图书类别" prop="type">
<el-input v-model="formData.type"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="图书名称" prop="name">
<el-input v-model="formData.name"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input v-model="formData.description" type="textarea"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="saveBook()">确定</el-button>
</div>
</el-dialog>
</div>
</div>
</div>
</div>
</body>
<!-- 引入组件库 -->
<script src="../js/vue.js"></script>
<script src="../plugins/elementui/index.js"></script>
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script src="../js/axios-0.18.0.js"></script>
<script>
var vue = new Vue({
el: '#app',
data:{
dataList: [],//当前页要展示的分页列表数据
formData: {},//表单数据
dialogFormVisible: false,//增加表单是否可见
dialogFormVisible4Edit:false,//编辑表单是否可见
pagination: {},//分页模型数据,暂时弃用
},
//钩子函数,VUE对象初始化完成后自动执行
created() {
this.getAll();
},
methods: {
// 重置表单
resetForm() {
//清空输入框
this.formData = {};
},
// 弹出添加窗口
openSave() {
this.dialogFormVisible = true;
this.resetForm();
},
//添加
saveBook () {
axios.post("/books",this.formData).then((res)=>{
});
},
//主页列表查询
getAll() {
axios.get("/books").then((res)=>{
this.dataList = res.data;
});
},
}
})
</script>
</html>
页面访问存在问题及解决方案 。
存在问题:无法访问到页面 。
报错内容 。
[WARNING] Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
[WARNING] No mapping for GET /pages/books.html
原因 。
现在发送请求 http://localhost/pages/books.html 访问这个页面,但是被SpringMVC拦截,认为应该有一个 Controller 中方法的 RequestMapping 叫这个名字 。
解决方案 放行非SpringMVC的请求 。
- 遇到这种情况 SpringMVC 应当放行,由 web 服务器进行处理
- web 服务器配置类中 getServletMappings 拦截了所有的请求,交给 SpringMVC 处理,需要为 SpringMVC 的配置添加过滤,放行页面请求
- 对页面的过滤一般单独抽成一个功能类
功能类 SpringMvcSupport 。
- 需要继承 WebMvcConfigurationSupport,覆写 addResourceHandlers ,操作 registry 属性
- 添加 @Configuration 注解,以用于包扫描
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
// 添加资源过滤
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 当访问页面时,如果请求的是/pages/**,不走MVC,走/pages目录下的内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
// ...
}
}
配置包扫描 。
@Configuration
@ComponentScan({"priv.dandelion.controller","priv.dandelion.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
最后此篇关于SpringMVC学习笔记-第一章-工作流程、Bean加载控制、请求与响应(参数接收与内容返回)、RESTful的文章就讲到这里了,如果你想了解更多关于SpringMVC学习笔记-第一章-工作流程、Bean加载控制、请求与响应(参数接收与内容返回)、RESTful的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
简而言之:我想从可变参数模板参数中提取各种选项,但不仅通过标签而且通过那些参数的索引,这些参数是未知的 标签。我喜欢 boost 中的方法(例如 heap 或 lockfree 策略),但想让它与 S
我可以对单元格中的 excel IF 语句提供一些帮助吗? 它在做什么? 对“BaselineAmount”进行了哪些评估? =IF(BaselineAmount, (Variance/Baselin
我正在使用以下方法: public async Task Save(Foo foo,out int param) { ....... MySqlParameter prmparamID
我正在使用 CodeGear RAD Studio IDE。 为了使用命令行参数测试我的应用程序,我多次使用了“运行 -> 参数”菜单中的“参数”字段。 但是每次我给它提供一个新值时,它都无法从“下拉
我已经为信用卡类编写了一些代码,粘贴在下面。我有一个接受上述变量的构造函数,并且正在研究一些方法将这些变量格式化为字符串,以便最终输出将类似于 号码:1234 5678 9012 3456 截止日期:
MySql IN 参数 - 在存储过程中使用时,VarChar IN 参数 val 是否需要单引号? 我已经像平常一样创建了经典 ASP 代码,但我没有更新该列。 我需要引用 VarChar 参数吗?
给出了下面的开始,但似乎不知道如何完成它。本质上,如果我调用 myTest([one, Two, Three], 2); 它应该返回元素 third。必须使用for循环来找到我的解决方案。 funct
将 1113355579999 作为参数传递时,该值在函数内部变为 959050335。 调用(main.c): printf("%d\n", FindCommonDigit(111335557999
这个问题在这里已经有了答案: Is Java "pass-by-reference" or "pass-by-value"? (92 个回答) 关闭9年前。 public class StackOve
我真的很困惑,当像 1 == scanf("%lg", &entry) 交换为 scanf("%lg", &entry) == 1 没有区别。我的实验书上说的是前者,而我觉得后者是可以理解的。 1 =
我正在尝试使用调用 SetupDiGetDeviceRegistryProperty 的函数使用德尔福 7。该调用来自示例函数 SetupEnumAvailableComPorts .它看起来像这样:
我需要在现有项目上实现一些事件的显示。我无法更改数据库结构。 在我的 Controller 中,我(从 ajax 请求)传递了一个时间戳,并且我需要显示之前的 8 个事件。因此,如果时间戳是(转换后)
rails 新手。按照多态关联的教程,我遇到了这个以在create 和destroy 中设置@client。 @client = Client.find(params[:client_id] || p
通过将 VM 参数设置为 -Xmx1024m,我能够通过 Eclipse 运行 Java 程序-Xms256M。现在我想通过 Windows 中的 .bat 文件运行相同的 Java 程序 (jar)
我有一个 Delphi DLL,它在被 Delphi 应用程序调用时工作并导出声明为的方法: Procedure ProduceOutput(request,inputs:widestring; va
浏览完文档和示例后,我还没有弄清楚 schema.yaml 文件中的参数到底用在哪里。 在此处使用 AWS 代码示例:https://github.com/aws-samples/aws-proton
程序参数: procedure get_user_profile ( i_attuid in ras_user.attuid%type, i_data_group in data_g
我有一个字符串作为参数传递给我的存储过程。 dim AgentString as String = " 'test1', 'test2', 'test3' " 我想在 IN 中使用该参数声明。 AND
这个问题已经有答案了: When should I use "this" in a class? (17 个回答) 已关闭 6 年前。 我运行了一些java代码,我看到了一些我不太明白的东西。为什么下
我输入 scroll(0,10,200,10);但是当它运行时,它会传递字符串“xxpos”或“yypos”,我确实在没有撇号的情况下尝试过,但它就是行不通。 scroll = function(xp
我是一名优秀的程序员,十分优秀!