简介
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
将后端服务器Servlet拆分成三层,分别是web
、service
和dao
- web层主要由servlet来处理,负责页面请求和数据的收集以及响应结果给前端
- service层主要负责业务逻辑的处理
- dao层主要负责数据的增删改查操作
servlet处理请求和数据的时候,存在的问题是一个servlet只能处理一个请求
针对web层进行了优化,采用了MVC设计模式,将其设计为controller
、view
和Model
- controller负责请求和数据的接收,接收后将其转发给service进行业务处理
- service根据需要会调用dao对数据进行增删改查
- dao把数据处理完后将结果交给service,service再交给controller
- controller根据需求组装成Model和View,Model和View组合起来生成页面转发给前端浏览器
- 这样做的好处就是controller可以处理多个请求,并对请求进行分发,执行不同的业务操作。
入门案例
1.使用SpringMVC技术需要先导入SpringMVC坐标与Servlet坐标
<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>
|
2.创建SpringMVC控制器类(等同于Servlet功能)
@Controller public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':'springmvc'}"; } }
|
3.初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean
@Configuration @ComponentScan("com.zx.controller") public class SpringMvcConfig { }
|
4.初始化SpringMVC容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; }
@Override protected String[] getServletMappings() { return new String[]{"/"}; }
@Override protected WebApplicationContext createRootApplicationContext() { return null; } }
|
注意事项
- 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环境
知识点1:@Controller
名称 |
@Controller |
类型 |
类注解 |
位置 |
SpringMVC控制器类定义上方 |
作用 |
设定SpringMVC的核心控制器bean |
知识点2:@RequestMapping
名称 |
@RequestMapping |
类型 |
类注解或方法注解 |
位置 |
SpringMVC控制器类或方法定义上方 |
作用 |
设置当前控制器方法请求访问路径 |
相关属性 |
value(默认),请求访问路径 |
知识点3:@ResponseBody
名称 |
@ResponseBody |
类型 |
类注解或方法注解 |
位置 |
SpringMVC控制器类或方法定义上方 |
作用 |
设置当前控制器方法响应内容为当前返回值,无需解析 |
入门案例工程流程分析
启动服务器初始化过程:
服务器启动,执行ServletContainersInitConfig类,初始化web容器
执行createServletApplicationContext方法,创建了WebApplicationContext对象
- 该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器
加载SpringMvcConfig配置类
执行@ComponentScan加载对应的bean
- 扫描指定包及其子包下所有类上的注解,如Controller类上的@Controller注解
加载UserController,每个@RequestMapping的名称对应一个具体的方法
- 此时就建立了
/save
和 save方法的对应关系
执行getServletMappings方法,设定SpringMVC拦截请求的路径规则
/
代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
单次请求过程:
- 发送请求
http://localhost/save
- web容器发现该请求满足SpringMVC拦截规则,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 上面的第五步已经将请求路径和方法建立了对应关系,通过/save就能找到对应的save方法
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
Controller加载控制与业务bean加载控制
SpringMVC相关bean(表现层bean),也就是controller包下的类
Spring控制的bean
- 业务bean(Service)
- 功能bean(DataSource,SqlSessionFactoryBean,MapperScannerConfigurer等)
@ComponentScan
@Configuration @ComponentScan(value="com.zx", excludeFilters=@ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
|
bean的加载格式
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; } }
|
—> 简化开发:
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; }
protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; }
protected String[] getServletMappings() { return new String[]{"/"}; } }
|
五种类型参数传递
普通参数
GET http://localhost/commonParamDifferentName?name=张三&age=18
|
@RequestMapping("/commonParamDifferentName") @ResponseBody public String commonParamDifferentName(@RequestPaam("name") String userName , int age){ System.out.println("普通参数传递 userName ==> "+userName); System.out.println("普通参数传递 age ==> "+age); return "{'module':'common param different name'}"; }
|
@RequestPaam("name")
解决参数不一致问题
注意:写上@RequestParam注解框架就不需要自己去解析注入,能提升框架处理性能
POJO类型参数
public class User { private String name; private int age; }
|
GET http://localhost/commonParamDifferentName?name=zhangxin&age=18
|
@RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return "{'module':'pojo param'}"; }
|
嵌套POJO类型参数
public class Address { private String province; private String city; } public class User { private String name; private int age; private Address address; }
|
GET http://localhost/commonParamDifferentName?name=zhangxin&age=18&address.city=hangzhou&address.province=zhejiang
|
@RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return "{'module':'pojo param'}"; }
|
数组类型参数
GET http://localhost/arrayParam?likes=game&likes=music&likes=travel
|
@RequestMapping("/arrayParam") @ResponseBody public String arrayParam(String[] likes){ System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes)); return "{'module':'array param'}"; }
|
集合类型参数
GET http://localhost/arrayParam?likes=game&likes=music&likes=travel
|
@RequestMapping("/listParam") @ResponseBody public String listParam(@RequestParam List<String> likes){ System.out.println("集合参数传递 likes ==> "+ likes); return "{'module':'list param'}"; }
|
知识点1:@RequestParam
名称 |
@RequestParam |
类型 |
形参注解 |
位置 |
SpringMVC控制器方法形参定义前面 |
作用 |
绑定请求参数与处理器方法形参间的关系 |
相关参数 |
required:是否为必传参数 defaultValue:参数默认值 |
JSON数据传输参数
步骤1:pom.xml添加依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
|
步骤2:PostMan发送JSON数据
步骤3:开启SpringMVC注解支持
@Configuration @ComponentScan("com.zx.controller")
@EnableWebMvc public class SpringMvcConfig { }
|
步骤4:参数前添加@RequestBody
@RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> likes){ System.out.println("list common(json)参数传递 list ==> "+likes); return "{'module':'list common for json param'}"; }
|
日期型参数传递
public String dataParam(@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") Date date){}
|
内部实现原理
public interface Converter<S, T> { @Nullable T convert(S source); }
|
请求与响应
@ResponseBody
设置当前控制器返回值作为响应体
响应页面(了解)
@RequestMapping("/toPage") public String toPage(){ return "page.jsp"; }
|
响应文本数据(了解)
@RequestMapping("/toText") @ResponseBody public String toText(){ teturn "response text"; }
|
响应json数据(对象转json)
@RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){} teturn new User(); }
|
响应json数据(对象集合转json数组)
@RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList(){ List<User> userList = new ArrayList<User>(); return userList; }
|
内部原理:HttpMessageConverter接口
RESTful
简介
关于资源命名建议:总是使用单数,因为(a)它是一致的,(b)它直接映射到单数类和表名,(c)英语中有些复数名词是不规则的(不可预测的)。
REST(Representational State Transfer),表述性状态传递,它是一种软件架构风格
,(访问网络资源的格式。)
按照REST风格访问资源时使用行为动作
区分对资源进行了何种操作
http://localhost/users
查询全部用户信息 GET(查询)
http://localhost/users/1
查询指定用户信息 GET(查询)
http://localhost/users
添加用户信息 POST(新增/保存)
http://localhost/users
修改用户信息 PUT(修改/更新)
http://localhost/users/1
删除用户信息 DELETE(删除)
入门案例
1.设定http请求动作
@RequestMapping(value = "/users",method = RequestMethod.POST)
|
or
@PostMapping(value = "/users")
|
2.设定请求参数
@DeleteMapping(value = "/users/{id}") @ResponseBody public String delete(@PathVariable Integer id) { System.out.println("user delete..." + id); return "{'module':'user delete'}"; }
|
知识点1:@PathVariable
名称 |
@PathVariable |
类型 |
==形参注解== |
位置 |
SpringMVC控制器方法形参定义前面 |
作用 |
绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
@RequestBody
、@RequestParam
、@PathVariable
- 区别
- @RequestParam用于接收url地址传参或表单传参
- @RequestBody用于接收json数据
- @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
- 应用
- 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
- 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
快速开发
@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(默认):请求访问路径 |
对静态资源的访问放行
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
|
使能够扫描SpringMvcSupport
@Configuration @ComponentScan({"com.zx.controller","com.zx.config"})
@EnableWebMvc public class SpringMvcConfig { }
|
修改books.html页面
<!DOCTYPE html>
<html> <head> <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: {}, },
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>
|