SpringMVC&Maven进阶
作者:互联网
3. SpringMVC
3.1 了解SpringMVC
- 概述
- SpringMVC技术与Servlet技术功能等同,均属于web层开发技术
- 学习路线
- 请求与响应
- REST分割
- SSM整合
- 拦截器
- 目标:
- 掌握基于SpringMVC获取请求参数与响应json数据操作
- 熟练应用基于REST风格的请求路径设置与参数传递
- 能够根据实际业务建立前后端开发通信协议并进行实现
- 基于SSM整合技术开发任意业务模块功能
3.2 SpringMVC简介
3.2.1 SpringMVC概述
- SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
- 优点
- 使用简单,开发便捷(相比于Servlet)
- 灵活性强
3.2.2 SpringMVC入门案例
-
使用SpringMVC需要先导入SpringMVC坐标与Servlet坐标
<dependencies> <!--1.导入坐标--> <!--Servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <!--由于可能会和tomcat插件冲突,添加范围provided--> <scope>provided</scope> </dependency> <!--SpringMVC--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
-
创建SpringMVC控制器类(等同于Servlet功能)
//2.定义controller //2.1 使用@Controller定义bean @Controller public class UserController { //2.2 设置当前操作的访问路径 @RequestMapping("/save") //2.3 设置当前操作的返回值类型 @ResponseBody public String save(){ System.out.println("user save.."); return "{'module':'Spring MVC'}"; } }
-
初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应Bean
//3.创建SpringMVC的配置文件,加载Controller对应的bean @Configuration @ComponentScan("com.mark.controller") public class SpringMVC { }
-
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
//4.定义一个Servlet容器启动的配置类,在里面加载Spring的配置,告知服务器使用SpringMVC public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer { //加载SpringMVC容器配置 @Override protected WebApplicationContext createServletApplicationContext() { //原来Spring获取容器:ApplicationContext ctx= new AnnotationConfigApplicationContext() //获取容器 AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); //注册配置 ctx.register(SpringMVCConfig.class); return ctx; } //设置哪些请求归属SpringMVC处理 @Override protected String[] getServletMappings() { //所有请求都归SpringMVC处理 return new String[]{"/"}; } //加载Spring容器配置 @Override protected WebApplicationContext createRootApplicationContext() { return null; } }
注意:此时web.xml可以删除,之后配置tomcat服务器
<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>
这时浏览器访问http://localhost/save 即可看到结果 {'module':'Spring MVC'}
3.2.3 注解介绍
-
@Controller
-
类型:类注解
-
位置:SpringMVC控制器类定义上方
-
作用:设定SpringMVC的核心控制器bean
-
范例:
@Controller public class UserController { }
-
-
@RequestMapping
-
类型:方法注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径
-
范例:
@RequestMapping("/save") public void save(){ System.out.println("user save ..."); }
-
相关属性
- value(默认):请求访问路径
-
-
@ResponseBody
-
类型:方法注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法响应内容为当前返回值,无需解析
-
范例
@RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'info':'springmvc'}"; }
-
3.2.4 SpringMVC入门程序开发总结(1+N)
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
- 多次工作
- 定义处理请求的控制器类(@Controller)
- 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
3.2.5 Servlet配置类详解
-
AbstractDispatcherServletInitializer
类是SpringMVC提供的快速初始化Web3.0容器的抽象类 -
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
-
createServletApplicationContext()
方法:
创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围@Override protected WebApplicationContext createServletApplicationContext() { //原来Spring获取容器:ApplicationContext ctx= new AnnotationConfigApplicationContext() //获取容器 AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); //注册配置 ctx.register(SpringMVCConfig.class); return ctx; }
-
getServletMappings()
方法:
设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理@Override protected String[] getServletMappings() { //所有请求都归SpringMVC处理 return new String[]{"/"}; }
-
createRootApplicationContext()
方法:
如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletApplicationContext()。如果没有返回null即可@Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; }
-
3.2.6 入门案例工作流程分析
-
启动服务器初始化过程
- 服务器启动,执行ServletContainersInitConfig类,初始化web容器
- 执行createServletApplicationContext方法,创建了WebApplicationContext对象
- 加载SpringMvcConfig
- 执行@ComponentScan加载对应的bean
- 加载UserController,每个@RequestMapping的名称对应一个具体的方法
- 执行getServletMappings方法,定义所有的请求都通过SpringMVC
-
单次请求过程
- 发送请求localhost/save
- web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方
3.2.7 Controller加载控制与业务bena加载控制
-
不同的bean由不同的容器管理
- SpringMVC相关的bean:表现层bean
- Spring控制的bean:
- 业务bean(Service)
- 功能bean(DataSource等)
-
因为功能不同,如何避免Spring错误的加载到SpringMVC的bean?
- 加载Spring控制的bean的时候排除掉SpringMVC控制的bean
-
不同bean的加载控制:
-
SpringMVC相关bean加载控制
- SpringMVC加载的bean对应均在com.mark.controller包内
-
Spring相关bean加载控制
-
方式一:Spring加载的bean设定扫描范围为com.mark,排除掉controller包内的bean
@Configuration //@ComponentScan({"com.mark.service","com.mark.dao"}) //扫描com.mark下的所有,但是按照注解排除掉使用了Controller注解的bean @ComponentScan(value = "com.mark", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
-
方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包
@Configuration @ComponentScan({"com.mark.service","com.mark.dao"}) public class SpringConfig { }
-
方式三:不区分Spring与SpringMVC的环境,加载到同一个环境
-
-
-
相关注解介绍
-
@ComponentScan
-
类型:类注解
-
范例:
@Configuration @ComponentScan(value = "com.mark", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
-
属性
- excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)
- includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)
-
-
-
⭐配置Web容器启动类改进、简化:继承
AbstractAnnotationConfigDispatcherServletInitializer
类public class ServletContainerInitConfig 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[]{"/"}; } }
3.2.8 PostMan
- PostMan简介
- PostMan是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件
- 作用:常用于进行接口测试
- 特征:
- 简单
- 实用
- 美观
- 大方
- PostMan基础操作
- 注册登录
- 创建/进入工作空间
- 发起请求测试结果
3.3 请求与响应
3.3.1 请求映射路径
-
思考:
-
团队多人开发,每个人设置不同的请求路径,冲突如何解决?
-
设置模块名作为请求路径前缀
@Controller public class BookController { @RequestMapping("/book/save") @ResponseBody public String save(){ System.out.println("book save ..."); return "{'module':'book save'}"; } }
@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'}"; } }
-
-
-
注解介绍
-
名称:
@RequestMapping
-
类型:方法注解 类注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀
-
范例:
@Controller @RequestMapping("/book") public class BookController { @RequestMapping("/save") @ResponseBody public String save() { System.out.println("book save ..."); return "{'module':'book save'}"; } }
-
属性
- value(默认):请求访问路径,或访问路径前缀
-
3.3.2 各种请求参数传递
-
请求方式
- Get请求
- Post请求
-
Get请求参数
-
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数
@Controller @RequestMapping("/user") 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':'common param'}"; } }
请求参数名与形参变量名不同,使用
@RequestParam
绑定参数关系@Controller @RequestMapping("/user") public class UserController { //普通参数 @RequestMapping("/commonParam") @ResponseBody public String commonParam(@RequestParam("name") String username,int age){ System.out.println("普通参数传递 name ==>"+username); System.out.println("普通参数传递 age ==>"+age); return "{'module':'common param'}"; } }
这时发送请求的名字为name,username可以接收到name的值
-
POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
//POJO参数 @RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("POJO参数传递 user ==>"+user); return "{'module':'pojo param'}"; }
嵌套POJO参数:POJO对象中包含POJO对象
请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数public class User { private String name; private int age; private Address address; }
public class Address { private String province; private String city; }
-
数组参数:请求参数名与形参数组名相同且请求参数为多个,定义数组类型形参即可接收参数
@RequestMapping("arrayParam") @ResponseBody public String arrayParam(String[] hobby){ System.out.println(("数组参数传递 hobby ==> "+ Arrays.toString(hobby))); return "{'module':'array param'}"; }
-
集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
@RequestMapping("listParam") @ResponseBody public String listParam(@RequestParam List<String> hobby){ System.out.println("集合参数传递 hobby ==> "+ hobby); return "{'module':'list param'}"; }
-
-
Post请求参数
-
普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数,代码与Get相同。
-
Post请求中文乱码处理
在Servlet启动类配置中添加过滤器
//乱码处理 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; }
-
其他类型与Get方式规则一样
-
3.3.3 响应json数据
-
添加json数据转换相关坐标
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency>
-
设置发送json数据(请求body中添加json数据)
-
开启json数据格式的自动转换,在配置类中开启
@EnableWebMvc
@Configuration @ComponentScan("com.mark.controller") @EnableWebMvc public class SpringMvcConfig { }
-
使用
@RequestBody
注解将外部传递的json数组数据映射到形参的集合对象中作为数据集合类数据:
@RequestMapping("/listParamForJson") @ResponseBody public String listParamForJson(@RequestBody List<String> hobby){ System.out.println("list common(json)参数传递 list ==> "+hobby); return "{'module':'list common for json param'}"; }
POJO类数据:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数
@RequestMapping("/pojoParamForJson") @ResponseBody public String pojoParamForJson(@RequestBody User user){ System.out.println("pojo(json)参数传递 user ==> "+user); return "{'module':'pojo for json param'}"; }
POJO集合参数
@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'}"; }
3.3.4 日期类型参数传递
- 日期类型数据基于不同系统 格式也不尽相同
- 2000-04-18
- 2000/04/18
- 04/18/2000
- 接收形参时,根据不同的日期格式设置不同的接收方式,默认格式:yyyy/MM/dd。实用
@DateTimeFormat
设定日期时间型数据格式,属性:pattern = 日期时间格式字符串
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
System.out.println("参数传递 date ==> "+date);
System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
return "{'module':'data param'}";
}
/*
参数传递 date ==> Tue Apr 18 00:00:00 CST 2000
参数传递 date1(yyyy-MM-dd) ==> Tue Apr 18 00:00:00 CST 2000
参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> Tue Apr 18 00:05:20 CST 2000
*/
3.3.5 响应
-
响应页面(了解)
返回值为String类型,设置返回值为页面名称,即可实现页面跳转
@RequestMapping("/toJumpPage") public String toJumpPage(){ System.out.println("跳转页面"); return "page.jsp"; }
-
响应数据
-
文本数据(了解)
返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
@RequestMapping("/toText") @ResponseBody public String toText(){ System.out.println("返回纯文本数据"); return "response text"; }
-
json数据(重点)
-
响应POJO对象
返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO(){ System.out.println("返回json对象数据"); User user = new User(); user.setName("itcast"); user.setAge(15); return user; }
-
响应POJO集合对象
返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
@RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList(){ System.out.println("返回json集合数据"); User user1 = new User(); user1.setName("传智播客"); user1.setAge(15); User user2 = new User(); user2.setName("黑马程序员"); user2.setAge(12); List<User> userList = new ArrayList<User>(); userList.add(user1); userList.add(user2); return userList; }
-
-
3.4 REST风格
3.4.1 REST风格简介
- 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(删除)
- 上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范。
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts…… - 根据REST风格对资源进行访问称为RESTful
3.4.2 RESTful入门案例
-
步骤:
-
修改@RequestMapping路径为模块名称复数
@RequestMapping("/users")
-
设置请求行为(http请求动作)
@RequestMapping(value = "/users",method = RequestMethod.POST)
-
设定请求参数(路径变量)
@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id) { System.out.println("user delete..." + id); return "{'module':'user delete'}"; }
-
-
实现:
@Controller public class UserController { //设置当前请求方法为POST,表示REST风格中的添加操作 @RequestMapping(value = "/users",method = RequestMethod.POST) @ResponseBody public String save(@RequestBody User user) { System.out.println("user save..."+user); return "{'module':'user save'}"; } //设置当前请求方法为DELETE,表示REST风格中的删除操作 //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @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,表示REST风格中的修改操作 @RequestMapping(value = "/users",method = RequestMethod.PUT) @ResponseBody public String update(@RequestBody User user) { System.out.println("user update..." + user); return "{'module':'user update'}"; } //设置当前请求方法为GET,表示REST风格中的查询操作 //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 @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,表示REST风格中的查询操作 @RequestMapping( value = "/users",method = RequestMethod.GET) @ResponseBody public String getAll() { System.out.println("user getAll..."); return "{'module':'user getAll'}"; } }
-
POST、DELETE、PUT、GET分别对应增删改查
-
@PathVariable
注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同 -
截至目前,见到过的接收参数注解有三种
- @RequestParam:用于接收url地址传参或表单传参 绑定参数
- @RequestBody:用于接收json数据映射到形参中作为数据
- @PathVariable:用于接收路径参数,@RequestMapping中使用{参数名称}描述路径参数
-
应用
- 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
- 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
- 如果发送非json格式数据,选用@RequestParam接收请求参数
3.4.3 REST快速开发
在上面的案例中,有很多重复编写的代码
将每个方法的value中的模块名提取到类前
将每个方法的@ResponseBody提取到类前
而类的@Controller和@ResponseBody可以合并成@RestController
每个方法@PostMapping
中的请求行为设置可以用对应的@xxxMapping
注解替换
//@Controller
//@ResponseBody配置在类上可以简化配置,表示设置当前每个方法的返回值都作为响应体
//@ResponseBody
@RestController//使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
@RequestMapping("/books")
public class BookController {
//@RequestMapping( method = RequestMethod.POST)
@PostMapping//使用@PostMapping简化Post请求方法对应的映射配置
public String save(@RequestBody Book book) {
System.out.println("book save..." + book);
return "{'module':'book save'}";
}
//@RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
@DeleteMapping("/{id}")//使用@DeleteMapping简化DELETE请求方法对应的映射配置
public String delete(@PathVariable Integer id) {
System.out.println("book delete..." + id);
return "{'module':'book delete'}";
}
//@RequestMapping(method = RequestMethod.PUT)
@PutMapping//使用@PutMapping简化Put请求方法对应的映射配置
public String update(@RequestBody Book book) {
System.out.println("book update..." + book);
return "{'module':'book update'}";
}
//@RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
@GetMapping("/{id}")//使用@GetMapping简化GET请求方法对应的映射配置
public String getById(@PathVariable Integer id) {
System.out.println("book getById..." + id);
return "{'module':'book getById'}";
}
//@RequestMapping(method = RequestMethod.GET)
@GetMapping//使用@GetMapping简化GET请求方法对应的映射配置
public String getAll() {
System.out.println("book getAll...");
return "{'module':'book getAll'}";
}
}
3.4.4 案例:基于RESTful页面数据交互
POSTMan实现后台接口开发
@RestController
@RequestMapping("/books")
public class BookController {
/**
* 保存新增数据
* @param book
* @return
*/
@PostMapping
public String save(@RequestBody Book book) {
System.out.println("book save" + book);
return "{'module':'book save success'}";
}
/**
* 获取全部
* @return
*/
@GetMapping
public List<Book> getAll() {
Book book1 = new Book();
book1.setType("计算机");
book1.setName("SpringMVC入门");
book1.setDescription("我是小白");
Book book2 = new Book();
book2.setType("计算机");
book2.setName("SpringMVC实战");
book2.setDescription("我是大佬");
List<Book> list = new ArrayList<Book>();
list.add(book1);
list.add(book2);
return list;
}
}
实现页面数据交互:
由于在访问静态资源页面时,SpringMVC的Sevlet容器配置中设置了protected String[] getServletMappings() { return new String[]{"/"};}
会拦截所有请求,导致无法打开页面
因此创建SpringMvcSupport配置类,设置对静态资源的访问放行
@Configuration
public class SpringMVCSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//当访问/pages/???的时候,不要走MVC,走/pages目录下的内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
前端页面通过异步提交访问后台控制器
//添加
saveBook() {
axios.post("/books", this.formData).then((res) => {
});
},
//主页列表查询
getAll() {
axios.get("/books").then((res) => {
this.dataList = res.data;
});
},
3.5 ⭐SSM整合
3.5.1 整合配置
-
创建工程
-
SSM整合
-
Spring
-
SpringConfig
@Configuration @ComponentScan({"com.mark.service"}) @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) public class SpringConfig {}
-
-
MyBatis
-
JdbcConfig
public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
-
MyBatisConfig
public class MybatisConfig { @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setTypeAliasesPackage("com.mark.domain"); return factoryBean; } @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.mark.dao"); return msc; } }
-
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm_db jdbc.username=root jdbc.password=123
-
-
SpringMVC
-
SpringMVCConfig
@Configuration @ComponentScan({"com.mark.controller","com.mark.config"}) @EnableWebMvc public class SpringMvcConfig {}
-
ServletConfig
public class ServletConfig 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[]{"/"}; } @Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter =new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); return new Filter[]{characterEncodingFilter}; } }
-
SpringMVCSupport
@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/"); } }
-
-
3.5.2 功能模块开发
-
表与实体类
创建表:
-- ---------------------------- -- Table structure for tbl_book -- ---------------------------- DROP TABLE IF EXISTS `tbl_book`; CREATE TABLE `tbl_book` ( `id` int(11) NOT NULL AUTO_INCREMENT, `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of tbl_book -- ---------------------------- INSERT INTO `tbl_book` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕'); INSERT INTO `tbl_book` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想'); INSERT INTO `tbl_book` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式'); INSERT INTO `tbl_book` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手'); INSERT INTO `tbl_book` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者'); INSERT INTO `tbl_book` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新'); INSERT INTO `tbl_book` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖'); INSERT INTO `tbl_book` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉'); INSERT INTO `tbl_book` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术'); INSERT INTO `tbl_book` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中'); INSERT INTO `tbl_book` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍'); INSERT INTO `tbl_book` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
实体类:
@Getter @Setter @ToString public class Book { private Integer id; private String type; private String name; private String description; }
-
创建好所有的接口和实现类
-
dao(接口+自动代理实现)
public interface BookDao { //@Insert("insert into tbl_book values (null,#{type},#{name},#{description})") //上面语句中type指的是Book类的属性名,id需要为null //下面语句中第一个type指的是表里的属性名 第二个type指的是Book类的属性名,不需要为id赋值 /** * 保存 * @param book */ @Insert("insert into tbl_book (type, name, description) values (#{type}, #{name}, #{description})") public void save(Book book); /** * 修改/更新 * @param book */ @Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}") public void update(Book book); /** * 删除 * @param id */ @Delete("delete from tbl_book where id = #{id}") public void delete(Integer id); /** * 根据id获取book * @param id * @return */ @Select("select id, type, name, description from tbl_book where id = #{id}") public Book getById(Integer id); /** * 获取所有book * @return */ @Select("select id, type, name, description from tbl_book") public List<Book> getAll(); }
-
service(接口+实体类)
public interface BookService { /** * 保存 * @param book */ public boolean save(Book book); /** * 修改/更新 * @param book */ public boolean update(Book book); /** * 根据id删除 * @param id */ public boolean delete(Integer id); /** * 根据id查询 * @param id * @return */ public Book getById(Integer id); /** * 获取所有 * @return */ public List<Book> getAll(); }
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; @Override public boolean save(Book book) { bookDao.save(book); return true; } @Override public boolean update(Book book) { bookDao.update(book); return true; } @Override public boolean delete(Integer id) { bookDao.delete(id); return true; } @Override public Book getById(Integer id) { //Book book = bookDao.getById(id); //return book; return bookDao.getById(id); } @Override public List<Book> getAll() { return bookDao.getAll(); } }
-
controller
@RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; /** * 保存 * @param book */ @PostMapping public boolean save(@RequestBody Book book) { return bookService.save(book); } /** * 修改/更新 * @param book */ @PutMapping public boolean update(@RequestBody Book book) { return bookService.update(book); } /** * 根据id删除 * @param id */ @DeleteMapping("/{id}") public boolean delete(@PathVariable Integer id) { return bookService.delete(id); } /** * 根据id查询 * @param id * @return */ @GetMapping("/{id}") public Book getById(@PathVariable Integer id) { return bookService.getById(id); } /** * 获取所有 * @return */ @GetMapping public List<Book> getAll() { return bookService.getAll(); } }
3.5.3 接口测试
-
业务层接口测试(整合Junit)
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class BookServiceTest { @Autowired private BookService bookService; @Test public void testGetById() { Book book = bookService.getById(2); System.out.println(book); } @Test public void testGetAll() { List<Book> list = bookService.getAll(); System.out.println(list); } }
-
表现层接口测试(PostMan)
3.5.4 添加事务
-
在Spring配置类中打开使用注解式事务驱动
@EnableTransactionManagement
-
在JDBC配置类中添加事务管理器配置
PlatformTransactionManager
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ DataSourceTransactionManager ptm = new DataSourceTransactionManager(); ptm.setDataSource(dataSource); return ptm; }
-
给要添加事务的实现类的接口添加注解
@Transactional
3.5.5 表现层数据封装协议
-
前端接收数据格式
-
增删改:true
-
查单条:
{ "id": 1, "type": "计算机理论", "name": "Spring实战 第5版", "description": "Spring入门经典教程,深入理解Spring原理技术内幕" }
-
查全部:
[ { "id": 1, "type": "计算机理论", "name": "Spring实战 第5版", "description": "Spring入门经典教程,深入理解Spring原理技术内幕" }, { "id": 2, "type": "计算机理论", "name": "Spring 5核心原理与30个类手写实战", "description": "十年沉淀之作,手写Spring精华思想" } ]
-
-
统一格式,前端接收数据格式:封装数据到data属性中,封装操作到code属性中,封装特殊消息到message(msg)属性中
-
增删改:
{ "code":20031 "data":true }
-
查单条:
{ "code":20041 "data":{ "id": 1, "type": "计算机理论", "name": "Spring实战 第5版", "description": "Spring入门经典教程,深入理解Spring原理技术内幕" } }
-
查单条数据为空
{ "code":20040 "data":null "msg":"数据查询失败,请重试!" }
-
查全部:
{ "code":20041 "data":[ { "id": 1, "type": "计算机理论", "name": "Spring实战 第5版", "description": "Spring入门经典教程,深入理解Spring原理技术内幕" }, { "id": 2, "type": "计算机理论", "name": "Spring 5核心原理与30个类手写实战", "description": "十年沉淀之作,手写Spring精华思想" } ] }
-
-
设置统一数据返回结果类
public class Result { private Object data; private Integer code; private String msg; }
- Result类中的字段并不是固定的,可以根据需要自行删减
- 提供若干个构造方法,方便操作
3.5.5 表现层与前端数据传输数据协议实现
-
添加Result结果数据
@Getter @Setter public class Result { private Object data; private Integer code; private String msg; public Result(Integer code, Object data, String msg) { this.data = data; this.code = code; this.msg = msg; } public Result(Integer code, Object data) { this.data = data; this.code = code; } public Result() { } }
-
修改Controller的return数据
@RestController @RequestMapping("/books") public class BookController { @Autowired private BookService bookService; /** * 保存 * @param book */ @PostMapping public Result save(@RequestBody Book book) { boolean flag = bookService.save(book); return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag); } /** * 修改/更新 * @param book */ @PutMapping public Result update(@RequestBody Book book) { boolean flag = bookService.update(book); return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag); } /** * 根据id删除 * @param id */ @DeleteMapping("/{id}") public Result delete(@PathVariable Integer id) { boolean flag = bookService.delete(id); return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag); } /** * 根据id查询 * @param id * @return */ @GetMapping("/{id}") public Result getById(@PathVariable Integer id) { Book book = bookService.getById(id); Integer code = book != null ? Code.GET_OK : Code.GET_ERR; String msg = book != null ? "" : "数据查询失败,请重试"; return new Result(code, book, msg); } /** * 获取所有 * @return */ @GetMapping public Result getAll() { List<Book> books = bookService.getAll(); Integer code = books.isEmpty() ? Code.GET_OK : Code.GET_ERR; String msg = books.isEmpty() ? "" : "数据查询失败,请重试"; return new Result(code, books, msg); } }
3.5.6 异常处理器
程序开发过程中不可避免的会遇到异常现象
-
出现异常现象的常见位置与常见诱因如下
- 框架内部抛出的异常:因使用不合规导致
- 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
- 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
-
各个层级都会出现异常,异常处理代码书写在哪一层?
- 所有的异常均抛出到表现层进行处理
-
表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决?
- AOP思想
-
异常处理器
- 集中的、统一的处理项目中出现的异常
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(Exception.class) public Result doException(Exception ex){ System.out.println("异常别跑"); return new Result(666,null,"异常别跑"); } }
@RestControllerAdvice:声明一个类作为异常处理器
@ExceptionHandler:定义当前方法处理哪一种异常
-
项目异常分类
- 业务异常(BusinessException)
- 规范的用户行为产生的异常
- 不规范的用户行为操作产生的异常
- 系统异常(SystemException)
- 项目运行过程中可预计且无法避免的异常
- 其他异常(Exception)
- 编程人员未预期到的异常
- 业务异常(BusinessException)
-
项目异常处理方案
- 业务异常(BusinessException)
- 发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护(纳入预期范围内)
- 记录日志
- 业务异常(BusinessException)
-
实现:
-
新建exception包
-
创建Exception类,自定义系统、业务级异常
@Getter @Setter public class SystemException extends RuntimeException { private Integer code; public SystemException(Integer code, String message) { super(message); this.code = code; } public SystemException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; } }
@Getter @Setter public class BusinessException extends RuntimeException{ private Integer code; public BusinessException(Integer code, String message) { super(message); this.code = code; } public BusinessException(Integer code, String message, Throwable cause) { super(message, cause); this.code = code; } }
-
自定义异常编码
public class Code { public static final Integer SYS_ERR = 50001; public static final Integer SYS_TIMEOUT_ERR = 50002; public static final Integer SYS_UNKNOW_ERR = 59999; public static final Integer Business_ERR = 60002; }
-
将可能出现的异常进行包装,转换成自定义异常,触发自定义异常
@Override public Book getById(Integer id) { if (id == 1){ throw new BusinessException(Code.Business_ERR,"请不要乱来!"); } //将可能出现的异常进行包装,转换成自定义异常 try { int i = 1 / 0; }catch (Exception e){ throw new SystemException(Code.SYS_TIMEOUT_ERR,"服务器访问超时,请重试",e); } return bookDao.getById(id); }
-
在异常处理器中分别进行处理
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(SystemException.class) public Result doSystemException(SystemException ex){ //记录日志 //发送消息给运维 //发送邮件给开发文件,ex对象发送给开发人员 return new Result(ex.getCode(),null,ex.getMessage()); } @ExceptionHandler(BusinessException.class) public Result doBusinessException(BusinessException ex){ return new Result(ex.getCode(),null,ex.getMessage()); } @ExceptionHandler(Exception.class) public Result doException(Exception ex){ //记录日志 //发送消息给运维 //发送邮件给开发文件,ex对象发送给开发人员 return new Result(Code.SYS_UNKNOW_ERR,null,"系统繁忙,请稍后再试"); } }
-
3.5.7 前后台协议联调
-
列表功能
getAll() { //发送ajax请求 axios.get("/books").then((res)=>{ this.dataList = res.data.data; }) },
-
添加功能
//弹出添加窗口 handleCreate() { this.dialogFormVisible = true; },
//添加 handleAdd() { //发送ajax请求 axios.post("/books", this.formData).then((res) => { //console.log(res.data) if (res.data.code == 20011){ //如果操作成功,关闭弹窗,显示数据 this.dialogFormVisible = false; this.$message.success("添加成功") }else if (res.data.code ==20010){ this.$message.error("添加失败") }else { this.$message.error(res.data.msg) } }).finally(()=>{ this.getAll(); }) },
//重置表单 resetForm() { this.formData = {} },
//弹出添加窗口 handleCreate() { this.dialogFormVisible = true; this.resetForm(); },
-
修改功能
//弹出编辑窗口 handleUpdate(row) { //查询数据,根据id查询 axios.get("/books/"+row.id).then((res)=>{ if (res.data.code== 20041){ //展示弹层,加载数据 this.formData = res.data.data; this.dialogFormVisible4Edit = true; }else{ this.$message.error(res.data.msg) } }) },
//编辑 handleEdit() { //发送ajax请求 axios.put("/books", this.formData).then((res) => { //如果操作成功,关闭弹层,显示数据 if (res.data.code == 20031){ this.dialogFormVisible4Edit = false; this.$message.success("修改成功") }else if (res.data.code ==20030){ this.$message.error("修改失败") }else { this.$message.error(res.data.msg) } }).finally(()=>{ this.getAll(); }) },
-
删除功能
// 删除 handleDelete(row) { //弹出提示框 this.$confirm("此操作不可逆,永久删除数据,是否继续", "提示", { type: 'info' }).then(() => { //删除业务 //查询数据,根据id查询 axios.delete("/books/" + row.id).then((res) => { if (res.data.code == 20021) { this.$message.success("删除成功") } else { this.$message.error("删除失败") } }).finally(() => { this.getAll(); }); }).catch(() => { //取消删除 this.$message.info("取消删除操作") }); }
3.6 拦截器
3.6.1 拦截器概念
- 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
- 作用
- 在指定的方法调用前后执行预先设定的代码
- 组织原始方法的执行
- 拦截器和过滤器的区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
3.6.2 入门案例
-
制作拦截器功能类:声明拦截器的bean,并实现HanderInterceptor接口(注意:扫描加载bean)
@Component public class ProjectInterceptor implements HandlerInterceptor { //在原始被拦截之前运行的代码 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); //false时,只执行preHandle,终止原始操作的运行 return true; } //在原始被拦截之后运行的代码 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); } //在原始被拦截之后运行的代码并且在post之后 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); } }
-
配置拦截器的执行位置:定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法设定拦截的访问路径(注意:扫描加载配置)
@Configuration public class SpringMVCSupport extends WebMvcConfigurationSupport { @Autowired private ProjectInterceptor projectInterceptor; @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages"); } //可以配置多个拦截路径 @Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*"); } }
-
在访问books时输出:
preHandle book save...Book{书名='haha', 价格=200.0} postHandle afterCompletion
-
简化开发:
-
不需要SpringMVCSupport类,直接在SpringMvcConfig中实现(侵入式较强)
@Configuration @ComponentScan({"com.mark.controller"}) @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { @Autowired private ProjectInterceptor projectInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*"); } }
-
3.6.3 拦截器参数
-
前置处理
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String contentType = request.getHeader("Content-Type"); HandlerMethod hm=(HandlerMethod) handler; hm.getMethod(); System.out.println("preHandle..."+contentType); return true; }
- 参数
- request:请求对象
- response:响应对象
- handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
- 返回值
- 返回值为false,被拦截的处理器将不再执行
- 参数
-
后置处理
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle..."); }
- 参数
- modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
- 参数
-
完成后处理
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); }
- 参数
- ex:如果处理器执行过程中出现异常对象,可以针对异常情况精选单独处理
- 参数
3.6.4 拦截器链配置
当配置多个拦截器时,形成拦截器链
-
配置两个拦截器后运行顺序:
preHandle... preHandle...222 book getById...1 postHandle...222 postHandle... afterCompletion...222 afterCompletion
先进后出
-
拦截器链的运行顺序参照拦截器添加顺序为准
拦截器运行中断,post都不会执行
4. Maven进阶
4.1 分模块开发与设计
-
分模块开发意义
- 将原始模块按照功能拆分成若干个子模块,方便模块间的相互调用,接口共享
-
分模块开发步骤
- 创建Maven模块
-
书写模块代码
分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分
-
通过maven指令安装模块到本地仓库(install命令)
团队内部开发需要发布模块到团队内部可共享的仓库中(私服)
-
在主项目pom中引入各个模块坐标
4.2 依赖管理
依赖指当前项目运行所需的jar,一个项目可以设置多个依赖
格式:
<!--设置当前项目所依赖的所有jar-->
<dependencies>
<!--设置具体的依赖-->
<dependency>
<!--依赖所属群组的id-->
<groupId>com.mark</groupId>
<!--依赖所属项目的id-->
<artifactId>Maven_02_Pojo</artifactId>
<!--依赖版本号-->
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
-
依赖传递
- 依赖具有传递性
- 直接依赖:在当前项目中通过依赖配置建立的依赖关系
- 间接依赖:被建立依赖关系的资源如果依赖其他资源,当前项目简介依赖其他资源
- 依赖具有传递性
-
依赖传递冲突问题
- 路径优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
- 声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
- 特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的
-
可选依赖:
optional
标签。对外隐藏当前资源。——不透明可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递性
<dependency> <groupId>com.mark</groupId> <artifactId>Maven_02_Pojo</artifactId> <version>1.0-SNAPSHOT</version> <!--可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递性--> <optional>true</optional> </dependency>
-
排除依赖:主动端来依赖的资源。——不需要
在引入其他模块坐标时,该有不需要的依赖可使用排除依赖标签
exclusions
,被排除的资源无需指定版本<dependency> <groupId>com.mark</groupId> <artifactId>Maven_03_Dao</artifactId> <version>1.0-SNAPSHOT</version> <!--隐藏当前资源对应的依赖关系--> <exclusions> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency>
-
可选依赖和排除依赖区别:
- 可选依赖:控制当前模块资源能不能被别人发现
- 排除依赖:用别人的资源发现不好的资源可以去掉
4.3 聚合与继承
-
聚合
-
聚合就是将多个模块组织成一个整体,同时进行项目构建的过程称为聚合
-
聚合工程:通常是一个不具有业务功能的“空”工程(有且仅有一个pom文件)
-
作用:使用聚合工程可以将多个工程编组,通过对聚合工程进行构建,实现对所包含的模块进行同步构建
- 当工程中某个模块发生更新(变更)时,必须保障工程中与已更新模块关联的模块同步更新,此时可以使用聚合工程来解决批量模块同步构建的问题
-
步骤:
-
创建新的模块
-
修改打包方式为pom
<groupId>com.mark</groupId> <artifactId>Maven_00_parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging>
-
设置管理的模块名称
<!--设置管理的模块名称--> <modules> <module>../Maven_01_SSM</module> <module>../Maven_02_Pojo</module> <module>../Maven_03_Dao</module> </modules>
-
-
-
继承
-
概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。简单来说,父工程的依赖子工程可以使用。
-
作用:
- 简化配置
- 减少版本冲突
-
实现:
-
在父工程的pom中设置打包类型为pom
-
在父工程的pom文件中配置依赖关系(子工程将沿用父工程的依赖关系)
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency> ...... </dependencies>
-
在父工程中配置子工程可以选择的依赖
<!--定义依赖管理--> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
-
在子工程中配置当前工程所继承的父工程
<parent> <groupId>com.mark</groupId> <artifactId>Maven_00_parent</artifactId> <version>1.0-SNAPSHOT</version> <!--相对路径,可以快速地找到继承的工程。可以不写--> <relativePath>../Maven_00_parent/pom.xml</relativePath> </parent>
-
子工程这时就可以使用父工程的依赖,同时还可以配置父工程中可选的依赖坐标
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies>
子工程中使用父工程中的可选依赖时,仅需要提供群组id和项目id,无需提供版本,版本由父工程统一提供,避免版本冲突
子工程中还可以定义父工程中没有定义的依赖关系
-
-
-
聚合与继承的区别
- 作用
- 聚合用于快速构建项目
- 继承用于快速配置
- 相同点:
- 聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中
- 聚合与继承均属于设计型模块,并无实际的模块内容
- 不同点:
- 聚合是在当前模块中配置关系,聚合可以感知到参与聚合的模块有哪些
- 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己
- 作用
4.4 属性管理
-
属性的配置与使用
- 定义属性
<properties> <spring.version>5.2.10.RELEASE</spring.version> <junit.version>4.12</junit.version> </properties>
- 引用属性
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> </dependencies> <!--定义依赖管理--> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement>
-
配置文件加载属性
-
定义属性
<properties> <spring.version>5.2.10.RELEASE</spring.version> <junit.version>4.12</junit.version> <jdbc.url>jdbc:mysql://127.0.0.1:3306/ssm_db</jdbc.url> </properties>
-
配置文件中引用属性
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=${jdbc.url} jdbc.username=root jdbc.password=123
-
开启资源文件目录加载属性的过滤器
<build> <!--扩大maven构建范围--> <resources> <resource> <!--使指定的目录里的文件可以解析${}的格式--> <directory>${project.basedir}/src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
-
配置maven打war包时,可以忽略web.xml的检查
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins>
-
-
其他属性(了解)
- 属性列表
- 自定义属性(常用)
- 内置属性
- Setting属性
- Java系统属性
- 环境变量属性
- 属性列表
-
版本管理
- 工程版本:
- SNAPSHOT(快照版本)
- 项目开发过程中临时输出的版本,称为快照版本
- 快照版本会随着开发的进展不断更新
- RELEASE(发布版本)
- 项目开发到进入阶段里程碑后,向团队外部发布较为稳定的版本,这种版本所对应的构件文件是稳定的,即便进行功能的后续开发,也不会改变当前发布版本内容,这种版本称为发布版本
- SNAPSHOT(快照版本)
- 发布版本
- alpha版
- beta版
- 纯数字版
- 工程版本:
4.5 多环境配置与应用
-
多环境开发
- maven提供配置多种环境的设定,帮助开发者使用过程中快速切换环境
<!--配置多环境开发--> <profiles> <!--定义开发环境:生产环境--> <profile> <!--定义环境对应的唯一名称--> <id>env_dep</id> <!--定义环境中专属的属性值--> <properties> <jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url> </properties> <!--设定是否为默认启动环境--> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <!--定义开发环境:开发环境--> <profile> <id>env_pro</id> <properties> <jdbc.url>jdbc:mysql://127.2.2.2:3306/ssm_db</jdbc.url> </properties> </profile> <!--定义开发环境:测试环境--> <profile> <id>env_test</id> <properties> <jdbc.url>jdbc:mysql://127.3.3.3:3306/ssm_db</jdbc.url> </properties> </profile> </profiles>
-
使用多环境(构建过程)
mvn 指令 -P 环境定义id
范例:
mvn install -P env_pro
-
跳过测试
-
应用场景
- 功能更新中并且没有开发完毕
- 快速打包
- ......
-
方式一:快速跳过
-
方式二:配置跳过
<plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.12.4</version> <configuration> <!--false:不跳过所有test--> <skipTests>false</skipTests> <!--排除掉不参与测试的内容--> <excludes>**/BookServiceTest.java</excludes> </configuration> </plugin> </plugins>
-
方式三:命令跳过
mvn package -D skipTests
-
4.6 私服
-
私服简介
- 私服就是一台独立的服务器,用于解决团队内部的资源共享与资源同步问题
-
Nexus
- Sonatype公司的一款maven私服产品
- 下载地址:https://help.sonatype.com/repomanager3/product-information/download
-
使用:
-
启动服务器(命令行启动)
nexus.exe /run nexus
-
访问服务器(默认端口8081)
-
修改基础配置信息
- 安装路径下etc目录中nexus-default.properties文件保存有nexus基础配置信息,例如默认访问端口
-
修改服务器运行配置信息
- 安装路径下bin目录中nexus.vmoptions文件保存有nexus服务器启动的配置信息,例如默认占用内存空间
-
-
私服仓库分类
仓库组是小组内共享资源用的,宿主仓库是小组内自己用的,代理仓库是所有项目组公用的
-
资源上传与下载
-
本地仓库访问私服配置
-
创建自己的两个仓库
-
配置本地仓库对私服的访问权限:打开maven的settings.xml文件,找到servers
<!-- 配置访问私服的权限 --> <server> <!-- 私服中的服务器id名称 --> <id>mark-snapshot</id> <!-- admin --> <username>admin</username> <!-- 123 --> <password>123</password> </server> <server> <!-- 私服中的服务器id名称 --> <id>mark-release</id> <!-- admin --> <username>admin</username> <!-- 123 --> <password>123</password> </server>
-
设置仓库组管理范围
-
配置映射关系
<!-- 私服的访问路径 --> <mirror> <!-- 仓库组的id --> <id>maven-public</id> <mirrorOf>*</mirrorOf> <url>http://localhost:8081/repository/maven-public/h</url> </mirror>
-
-
私服资源上传与下载
-
配置工程保存在私服中的位置
<!--配置当前工程保存在私服中的具体位置--> <distributionManagement> <repository> <id>mark-release</id> <url>http://localhost:8081/repository/mark-release/</url> </repository> <snapshotRepository> <id>mark-snapshot</id> <url>http://localhost:8081/repository/mark-snapshot/</url> </snapshotRepository> </distributionManagement>
-
上传指令:deploy(在上传前要为所有的模块配置继承关系)
-
然后接可以在仓库中看到上传的资源了
-
因为pom.xml中设置了版本为SNAPSHOT,因此只上传到了snapshot仓库,当修改为RELEASE时便可上传到mark-release仓库中
<groupId>com.mark</groupId> <artifactId>Maven_00_parent</artifactId> <version>1.0-RELEASE</version> <packaging>pom</packaging>
-
-
可以更换中央仓库:
-
标签:return,进阶,SpringMVC,class,public,Maven,book,id,String 来源: https://www.cnblogs.com/hackertyper/p/16663274.html