1 SpringBoot入门
1.1 传统 Spring 开发的问题
(1)Spring的优点
Spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的EnterpriseJavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的Java对象(Plain Ordinary Java Object,POJO)实现了EJB的功能。
(2)Spring的问题
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编写配置占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。
1.2 SpringBoot简介
1.2.1 什么是Spring Boot
使用Spring开发Java项目,被诟病最多的是项目搭建过程太繁琐,尤其是要找到合适的Spring版本以及需要整合的其他框架的兼容版本,需要花费大量的时间去试错;哪怕找对了所有依赖JAR包,要配置好这些框架,也需要编写不少的配置文件(XML)。
SpringBoot是一个快速搭建Spring项目的脚手架,几乎不需要什么工作量就可以快速整合Spring及其它常用框架,快速构建应用,而而无需考虑包依赖、框架版本和复杂的配置问题。
传统的SSM框架项目的开发过程:
Spring Boot项目的开发过程
1.2.2 Spring Boot 的特性
(1)能够快速创建基于 Spring 的应用程序;
(2)简化部署:对于Web项目,Spring内置Web容器,能够直接使用 main 方法启动内嵌的 Tomcat,不需要部署 war 包文件
(4)简化依赖:提供约定的起步依赖(Starter)来简化 Maven 配置
(5)自动化配置,根据项目的 Maven 依赖配置, Spring boot 自动 配置 Spring、 Spring mvc 等
(6)提供了程序的健康检查等功能 ;
(7)可以完全不使用 XML 配置文件,采用注解和Java配置。
1.2.3 Spring Boot 的四大核心功能
(1)自动配置:针对常用的Spring功能及其它框架的整合,SpringBoot提供自动配置。对于不能自动化的少量配置,Spring Boot 提供了统一的配置文件(application.properties / application.yml)(porperties优于yml执行),配置更集中,便于管理。
(2)起步依赖(Starter):Spring Boot 把常用的一组依赖包打包成一个套装,然后加上自动化配置信息,制作成“起步依赖”包。“起步依赖”一方面解决了JAR包版本冲突问题,另一方面极大的简化了配置。
(3)提供 SpringBoot CLI,可以基于命令行进行快速操。
(4)提供Actuator模块:用于观察和监控Spring项目运行状况。
1.2.4 Spring Boot 开发版本推荐
➢ 如果是使用 eclipse,推荐安装 Spring Tool Suite (STS)插件
➢ 如果使用 IDEA 旗舰版,自带了 Spring Boot 插件 ➢ 推荐使用 Maven 3.3+
➢ 推荐使用 Java 8, Spring Boot 2.x 系列需要至少 Java8
1.2.5 体验Spring Boot CLI
以下用一个极端的例子来演示Spring Boot 有多自动化。
搭建一个SpringMVC项目需要的步骤大家都应该心里有数了,但如果使用Spring Boot的命令行,我们就可以什么都不做了。
(1)安装Spring Boot CLI
下载:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#getting-started-installing-the-cli 解压并添加bin路径(如D:\dev\spring-2.2.0.RELEASE\bin)到环境变量Path
(2)编写控制器
@RestController
public class HelloController{
@GetMapping("/")
public String hello(){
return "Hello Spring Boot!";
}
}
(3)命令行中(cmd)运行
spring run HelloController.java
2 创建SpringBoot工程
2.1 创建Spring Boot 项目
2.1.1 创建的三种方式 (推荐第二种)
(1)直接在Spring官网提供的start页面创建maven项目:https://start.spring.io/
(2)使用 Idea 提供的 “New Project > Spring Initializr” 菜单向导
(3)使用Spring Tool Suite(STS)提供的“File > New > Spring Starter Proejct” 菜单向导
2.1.2 创建第一个SpringBoot项目
(1)添加SpringBoot的起步依赖
1)starter-parent:SpringBoot要求,项目要继承SpringBoot的起步依赖spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5.RELEASE</version>
</parent>
2)SpringBoot要集成SpringMVC进行Controller的开发,所以项目要导入web的启动依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
(2)编写 SpringBoot 启动类
@SpringBootApplication
public class MyFirstBootApplication {
public static void main(String[] args) {
SpringApplication.run(MyFirstBootApplication.class);
}
}
(3)添加控制器类,并运行启动类
@RestController
public class HelloController{
@GetMapping("/hello")
public String hello(){
return "Hello Spring Boot!";
}
}
注意,上述的 @RequetController 注解 是一个复合注解,代表了 @Controller 和 @ResponseBody,即指定当前控制器方法的返回值会作为响应体直接返回,即对象通常序列化为JSON格式输出。
2.1.3 代码解析
(1)启动类
@SpringBootApplication //标注SpringBoot的启动类,该注解具备多种功能(@Configuration+@ComponentScan等)。
SpringApplication.run(MyFirstBootApplication.class) //代表运行SpringBoot的程序,参数为SpringBoot启动类对象
(2)项目结构:
static: 存放静态资源,如图片、 CSS、 JavaScript 等
templates:存放 Web 页面的模板文件
application.properties:用于存放程序的各种依赖模块 的配置信息,比如 服务端口,数据库连接配置等
2.2 SpringBoot工程热部署
2.2.1 SpringBoot热部署工具——devtools
我们在开发中反复修改类、页面等资源,每次修改后都是需要重新启动才生效,这样每次启动都很麻烦,浪费了大的时间。
在 pom.xml 中添加”devtools“ (如下配置)就可以实现在修改代码后不重启就能生效,我们称之为热部署。
<!--热部署配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.2.2 IDEA 进行 SpringBoot 热部署失败问题的解决
我们在IDEA中使用SpringBoot的devtools工具时可能会失效。出现这种情况,并不是热部署配置问题,其根本原因是因为 IEDA 默认情况下不会自动编译,需要对 IDEA 进行自动编译的设置,如下:
(1)在设置(Settings)中启用自动编译
(2)在维护(Maintenance)中的注册(Registry)中勾选运行时自动编译选项
同时按住 Ctrl + Shift + Alt + / 然后进入Registry ,勾选自动编译并调整延时参数:
compiler.automake.allow.when.app.running -> 自动编译
compile.document.save.trigger.delay -> 自动更新文件(针对静态文件如JS CSS的更新,将延迟时间减少)
2.3 SpringBoot 原理分析(重点)
(1)起步依赖原理分析
查看spring-boot-starter-parent的pom文件**,上面的spring-boot-starter-dependencies的pom.xml中我们可以发现,一部分坐标的版本、依赖管理、插件管理已经定义好,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了。所以起步依赖的作用就是进行依赖的传递**
(2)分析spring-boot-starter-web
按住Ctrl点击pom.xml中的spring-boot-starter-web,跳转到了spring-boot-starter-web的pom.xml。发现spring-boot-starter-web就是将web开发要使用的 spring-web、spring-webmvc等坐标进行了“打包”,这样我们的工程只要引入spring-boot-starter-web起步依赖的坐标就可以进行web开发了,同样体现了依赖传递的作用。
(3)自动配置原理解析
按住Ctrl点击查看启动类MySpringBootApplication上的注解@SpringBootApplication注解@SpringBootApplication的源码其中, @SpringBootConfiguration:等同于 @Configuration,既标注该类是Spring的一个配置类 @EnableAutoConfiguration:SpringBoot自动配置功能开启
@SpringBootApplication
public class MyFirstBootApplication {
public static void main(String[] args) {
SpringApplication.run(MyFirstBootApplication.class); }
}
}
3 SpringBoot的配置文件
使用 Spring Boot 开发项目时,很多配置都是 Spring Boot Starter 默认配置好的,然而在实际开发中,项目的配置需求是灵活多变的,我们经常需要调整这些默认配置。
3.1 Spring Boot 的配置参数
不同于传统开发中每个框架都有自己的配置文件,Spring Boot 提供了统一的配置(application.yml或application.porperties),并通过各种 Starter 套件提供了数百个可以配置的参数,这些配置参数基本可以满足我们日程开发的需要。
3.1.1 Spring Boot 参数的配置方法
Spring Boot提供了多种方式来实现微调配置:如命令行参数、JNDI属性、JVM系统属性、操作系统的环境变量、application配置文件(application.properties 或 application.yml)等等。下面演示最简单的两种配置方式:命令行参数和application配置文件。
创建一个 “spring-boot-starter-web” 项目。
(1)使用命令行参数
SpringBoot项目可以通过“mvn package”打包并通过“java -jar”执行。
在项目根(pom.xml)目录下执行编译和运行:
mvn package
java -jar .\target\demo-0.0.1-SNAPSHOT.jar
运行时,我们还可以通过命令行参数指定微调配置,例如:启动端口号
java -jar .\target\demo-0.0.1-SNAPSHOT.jar --server.port=9092
**(2)使用aplication.properties **
server.port=9091
(3)使用application.yml(或 application.yaml)
server:
port:9091
(4)使用外置的application配置文件
进入 target 目录,在 Spring Boot 项目打包的 JAR 旁边添加外置的 application.yml
server:
port:9093
使用命令启动 JAR
java -jar .\demo-0.0.1-SNAPSHOT.jar
3.1.2 SpringBoot配置的几种途径和优先级
(1)配置优先级
Spring Boot 项目如果在多个地方配置了相同的参数,则按以下优先级进行加载:
- 命令行参数;2) java:comp/env里的JNDI属性;3) JVM系统属性;4) 操作系统环境变量;5) 应用程序以外的application配置文件;6) 打包在应用程序内的application配置文件;7)@PropertySource标注的属性源;8) 默认值。
(2)application配置文件(application.properties和application.yml)可以放置的位置
- 外置,应用程序运行目录的/config子目录;2) 外置,应用程序运行目录里;3) 内置,在config包内;4) 内置,在classpath根目录。
3.2 yaml 配置文件简介
yml 或 yaml (Yet Another Markup Language)是一种直观的能够被电脑识别的的数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁。YML文件的扩展名可以使用 .yml 或者 .yaml。
yml配置文件的语法
(1)配置普通数据
语法:注意 value 之前有一个空格
key: value
(2)配置对象数据(二级属性)
语法:注意key1前面的空格个数不限定,在yml语法中,相同缩进代表同一个级别
key:
key1: value1
key2: value2
示例代码:
person:
name: haohao
age: 31
addr: beijing
#或者
person: {name: haohao,age: 31,addr: beijing}
(3)配置Map数据(同上面的对象写法)
person:
name: haohao
age: 31
addr: beijing
#或者
person: {name: haohao,age: 31,addr: beijing}
(4)配置数组(List、Set)数据
key:
- value1
- value2
#或者:
key: [value1,value2]
示例:
city:
- Beijing
- Tianjin
city: [Guangzhou, Shenzhen]
#数组对象
person:
- name: accp
age: 20
- name: zhangsan
age: 21
person: [{name:accp, age:20}, {name:zhangsan, age:21}]
3.3 多环境配置
在实际开发的过程中,我们的项目会经历很多的阶段:开发、测 试、上线。
每个阶段的环境配置也会有所不同。例如:服务器IP和端口、部署路径、数据库连接等。
为了方便在不同的环境之间切换,SpringBoot 提供了多环境配置(Profiles)。
SpringBoot多环境配置具体规则如下:
(1)可以为每个环境创建一个配置文件,命为“application-环境标识.properties“或“application-环境标识.yml“。例如:
总配置文件: application.yml 或 application.properties
开发环境配置文件: application-dev.yml 或 application-dev.properties
测试环境配置文件: application-test.yml 或 application-test.properties
生产环境配置文件: application-product.yml application-product.properties
(2)在总配置文件(application.properties /application.ym)中进行环境的激活
等号右边的值和配置文件的环境标识名一致, 可以更改总配置 文件的配置, 重新运行 Application,查看启动的端口及上下文根。
在总的配置文件application.properties中激活配置文件
#SpringBoot 的总配置文件
#激活开发环境
#spring.profiles.active=dev
#激活测试环境
#spring.profiles.active=test
#激活生产环境
spring.profiles.active=product
3.4 自定义配置
SpringBoot的配置参数定义在 SpringBoot 的核心配置文件中。除了内置的配置项,我们还可以在自定义配置,然后采用特定注解把自定义配置注入到代码中使用。
3.4.1 使用注解@Value注入
通过@Value注解可以将配置文件中的值映射到一个Spring管理的Bean的属性上。
例如:
application.yml配置如下
person:
name: zhangsan
age: 18
实体Bean代码如下:
@Controller
public class QuickStartController {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private Integer age;
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "springboot 访问成功! name="+name+",age="+age; }
}
}
3.4.2 使用 @ConfigurationProperties 注解注入
@ConfigurationProperties注解将一组配置映射成一个对象,注入到托管Bean中。用于一组多个自定义配置项的情况。
通过注解
@ConfigurationProperties(prefix="配置文件中的key前缀")
可以将配置文件中的配置自动与数据实体进行映射,配置项通过实体的set方法注入(数据实体必须提供set方法)
@Controller
@ConfigurationProperties(prefix = "person")
public class QuickStartController {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "springboot 访问成功! name="+name+",age="+age;
}
}
解决警告问题:如果使用@ConfigurationProperties时出现警告,可以添加以下依赖
<!--解决使用 @ConfigurationProperties 注解出现警告问题-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
3.5 日志配置
Java 开源世界有不少日志工具,如:log4j、slf4j、log4j2 和 logback 等。
logback 和log4j2 都是log4j 的升级版,logback 在性能上比Log4j 更加高效,Spring Boot 把LogBack 作为首选的日志记录框架,spring-boot-starter-web 依赖默认包含了logback 的相关依赖。
3.5.1 使用 application.properties设置 logback
通过 application.properties文件,可以实现部分日志配置。
logging.config | 加载指定位置的日志配置文件 |
---|---|
logging.file.max**-history** | 要保留的存档日志文件的最大数目 |
logging.file.max**-size** | 日志文件的最大占用空间,单位为M |
logging.level.* | 设置某个包范围下的日志级别,比如logging.level.org.springframework=DEBUG |
logging.pattern.console | 设置在控制台输出的日志的格式 |
logging.pattern.dateformat | 设置在控制台输出的日志的日期格式 |
logging.pattern.file | 定义输出到日志文件的日志格式 |
logging.pattern.level | 定义日志的输出级别 |
以下示例,通过application.properties配置logback的日志文件路,并设置对不同范围的日志的等级(输出mapper的调试信息)。
TRACE < DEBUG < INFO < WARN < ERROR
logging.file.path=d:/logback-logs/ #设置文件输出路径
logging.level.root=WARN #根日志输出级别为WARN
logging.level.com.bjpowernode.mapper=DEBUG #csdn.mapper输出日志为DEBUG
3.5.2 在代码中使用日志输出
通过 LoggerFactory 我们可以得到 日志输出对象 Logger,在业务代码中可以使用 Logger 输出调试信息。
Logger logger = LoggerFactory.getLogger(this.getClass());
3.5.3 使用logback配置文件
如果需要更详细的日志配置,可以使用单独的日志配置文件
application.yml 中可以通过logging.config 属性指定自定义日志配置文件的位置
logging.config=日志配置文件的位置和名称
默认情况下,Spring Boot 项目可以读取resources 资源目录中特定名字的日志配置文件,如:logback-spring.xml、logback.xml
Spring Boot 推荐使用带有-spring 的文件名作为日志配置,即使用logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--输出日志到控制台-->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<layout>
<pattern>
[%p]%d - %msg%n
</pattern>
</layout>
</appender>
<!--输出日志到文件-->
<appender name="fileLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>DENY</onMatch>
</filter>
<encoder>
<pattern>
[%p]%d - %msg%n
</pattern>
</encoder>
<!--指定文件的输出位置-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>
C:/logs/%d.log
</fileNamePattern>
</rollingPolicy>
</appender>
<root level="debug">
<appender-ref ref="consoleLog"></appender-ref>
<appender-ref ref="fileLog"></appender-ref>
</root>
</configuration>
4 SpringBoot集成 MyBatis
4.1 SpringBoot 集成 MyBatis
(1)添加起步依赖和驱动包
<!--MyBatis 整合 SpringBoot 的起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--MySQL 的驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
(2)添加数据源配置信息
在 Springboot 的核心配置文件 application.properties 中配 置数据源
#数据库连接信息
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/myCinema?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
#spring集成Mybatis环境
#实体别名扫描包
mybatis.type-aliases-package=com.bjpowernode.model
#加载Mybatis映射文件
mybatis.mapper-locations=classpath:mappers/*Mapper.xml
(3)向 Spring 托管 Mapper 接口代理对象
在 SpringBoot 中向托管 MyBatis 的Mapper接口代理对象有两种方法
其一:使用@Mapper注解标记Mapper接口,Spring会找到@Mapper标记的接口并生成代理对象托管
@Mapper
public interface CategoryMapper{
//省略方法
}
其二:在启动类上使用 @MapperScan注解指定Mapper接口所在包,Spring会对其下的接口进行扫描并批量托管
@MapperScan("com.bjpowernode.mapper")
@SpringBootApplication
public class MyBatisBootApplication {
//省略main
}
(4)使用单元测试
创建SpringBoot项目时,一般都会默认带上测试的起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
直接创建单元测试,使用@SpringBootTest注解标记测试类,即可实现单元测试功能
@SpringBootTest
public class CategoryMapperTest{
@Autowired
private CategoryMapper categoryMapper;
@Test
public void test(){
categoryMapper.selectAll().forEach(x->System.out.println)
}
}
4.2 Spring Boot 事务支持
Spring Boot 使用事务非常简单,底层依然采用的是 Spring 本身提 供的事务管理
(1) 在SpringBoot启动类中使用注解 @EnableTransactionManagement 开启 事务支持 (默认开启)
(2)在访问数据库的 Service 方法上添加注解 @Transactional 即可
//启动类开启事务支持
@SpringBootApplication
@MapperScan(basePackages = "com.bjpowernode.mapper")
@EnableTransactionManagement //开启事务支持(可选项,但@Transactional 必须添加)
public class Application {
}
@Transactional 添加在serviceImpl 开启事务回滚
4.3 使用 Mybatis 反向工程生成接口、映射文件以及实体 bean
(1)拷贝Mybatis 反向工程配置文件GeneratorMapper.xml到项目的根目录下
(2)根据项目及表的情况,修改 GeneratorMapper.xml 配置
如果使用高版本的MySQL, 驱动类需要变更为: com.mysql.cj.jdbc.Driver;url 后面应该加属性 nullCatalogMeansCurrent=true,否则生成有问题。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 指定连接数据库的JDBC驱动包所在位置,指定到你本机的完整路径 -->
<classPathEntry location="F:\m2\repository\mysql\mysql-connector-java\8.0.25\mysql-connector-java-8.0.25.jar"/>
<!-- 配置table表信息内容体,targetRuntime指定采用MyBatis3的版本 -->
<context id="tables" targetRuntime="MyBatis3">
<!-- 抑制生成注释,由于生成的注释都是英文的,可以不让它生成 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mycinema"
userId="root"
password="hx12345678">
</jdbcConnection>
<!-- 生成model类,targetPackage指定model类的包名,
targetProject指定生成的model放在接口工程下面-->
<javaModelGenerator targetPackage="com.powernode.model"
targetProject="E:\08.springboot\springboot-dubbo\interface\src\main\java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="false" />
</javaModelGenerator>
<!-- 生成MyBatis的Mapper.xml文件,targetPackage指定mapper.xml文件的包名,
targetProject指定生成的mapper.xml放在当前工程src -->
<sqlMapGenerator targetPackage="com.powernode.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成MyBatis的Mapper接口类文件,targetPackage指定Mapper接口类的包名,
targetProject指定生成的Mapper接口放当前工程src目录 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.powernode.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 数据库表名及对应的Java模型类名 -->
<table tableName="category" domainObjectName="Category"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="movie" domainObjectName="Movie"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="user" domainObjectName="User"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
</context>
</generatorConfiguration>
也可以安装插件 Easy Code
)
(3)在 pom.xml 文件中添加 mysql 反向工程依赖
<!--mybatis代码自动生成插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!--配置文件的位置-->
<configurationFile>GeneratorMapper.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
(4)在maven窗口中找到Generator插件启动生成器
(5)如果插件生成的Mapper.xml文件不在resources目录下,则需要手动设置编译目录,否则读取不到映射文件
(idea默认要求mapper文件只能在resources目录)
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
5 Spring Boot 前端使用 JSP
5.1 在 pom.xml 文件中配置以下依赖项
SpringBoot 虽然在 starter-web 中嵌入了Tomcat容器,但排除了JSP相关的依赖。对于服务器页面模板基数,SpringBoot 更推崇自己旗下的 Thymeleaf 模板技术,因此没有内置JSP模板依赖,需要使用JSP的开发者,需要添加以下依赖。
<!--如果只是使用 JSP 页面,可以只添加该依赖-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--如果使用 JSTL 必须添加该依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!--如果要使用 servlet 必须添加该以下两个依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
5.2 在 pom.xml 的 build 标签中要配置编译信息。
SpringBoot 要求 jsp 文件必须编译到指定的 META-INF/resources 目 录下才能访问,否则访问不到。在IDEA环境中,我们需要添加以下配置,让IDEA在编译时把 src/main/webapp 下的 jsp 文件输出到 META-INF/resources 目录下。
<!--
SpringBoot 要求 jsp 文件必须编译到指定的 META-INF/resources 目录下才能访问,否则访问不到。其它官方已经建议使用模版技术(后面会课程会单独讲解模版技术)
-->
<resources>
<resource>
<!--源文件位置-->
<directory>src/main/webapp</directory>
<!--指定编译到 META-INF/resources,该目录不能随便写-->
<targetPath>META-INF/resources</targetPath>
<!--指定要把哪些文件编译进去, **表示 webapp 目录及子目录, *.*表示所有文件-->
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
5.3 配置 Spring MVC 的视图 展示为 jsp
#SpringBoot 核心配置文件
#指定内嵌 Tomcat 端口号
server.port=8090
#配置 SpringMVC 视图解析器
#其中: / 表示目录为 src/main/webapp
spring.mvc.view.prefix=/WEB-INF/pages/
spring.mvc.view.suffix=.jsp
application.yml 格式的配置文件
spring:
mvc:
view:
prefix: /WEB-INF/pages/
suffix: .jsp
5.4 在 src/main 下创建一个 webapp 目录
然后在该目录 下新建 index.jsp 页面 如果在 webapp 目录下右键,如果没有创建 jsp 的选项,可以在 Project Structure 中指定 webapp 为 Web Resource Directory
5.5 添加静态资源
客户端资源的默认路径为”resources/static“,可以在其中添加静态资源
6 添加拦截器
为了统一为应用程序增加一些功能,比如权限控制,统计请求处理时间等功能,可使用基于AOP的拦截器实现。
6.1 创建拦截器
与传统 SpringMVC 项目相同,我们可以通过实现 HandlerInterceptor 接口来编写 Web 拦截器功能
@Component
public class TimerInterceptor implements HandlerInterceptor {
long start=0;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
start=System.currentTimeMillis();
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
long end=System.currentTimeMillis();
long result=end-start;
System.out.println("请求资源="+request.getRequestURI()+",使用了="+result+"毫秒");
}
}
6.2 创建拦截器(springboot)
在SpringBoot项目中,我们可以通过实现 WebMvcConfigurer 接口,对SpringMVC进行进阶的配置,例如配置拦截器。
以下代码基于 WebMvcConfigurer 创建配置类,注册拦截器,配置拦截器的应用路径和排除路径。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private TimerInterceptor timerInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
//使用拦截器的路径表达式,数组
String[] includePaths={
"/*" //应用于所有路径
};
//排除拦截器的路径表达式,数组
String[] excludePaths={
"/user/login",
"/error"
};
registry.addInterceptor(timerInterceptor)
.addPathPatterns(includePaths)
.excludePathPatterns(excludePaths);
}
}
6.3编写测试代码,测试拦截器是否生效
@GetMapping("/admin/home")
@ResponseBody
public String adminHome(){
return "admin-home";
}
@GetMapping("/user/login")
@ResponseBody
public String userLogin(){
return "login-success";
}
7. 在Spring Boot 实现 RESTful 控制器
7.1 常用请求方法和SpringMVC对应的注解
(1)HTTP 的请求方式(Request Method)
HTTP的请求方式不仅仅有 GET 和POST,常用的就至少有5种,在规范的前后端分离开发中,开发应该遵循HTTP协议,根据功能提交不同方法的HTTP请求。
GET(SELECT): 从服务器取出资源(一项或多项)。
POST(CREATE): 在服务器新建一个资源。
PUT(UPDATE): 在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE): 在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE): 从服务器删除资源。
(2)Spring MVC 中对应的控制器注解
注解 | 作用 |
---|---|
@RequestMapping | 支持任意请求方法的请求,可以通过 method 属性指定请求方法 |
@GetMapping | 支持Get请求,用于查询数据 |
@PostMapping | 只支持Post请求,用于增加数据 |
@PutMapping | 只支持Put请求,用于修改数据 |
@PatchMapping | 支持此Patch请求,用于修改部分数据(只修改改变的部分属性) |
@DeleteMapping | 只支持Delete请求,删除数据 |
(3)各种请求方式的测试
浏览器对于同步请求,请求方法往往只能支持“GET”和“POST”,对于其它请求方法(Request Method),我们可以使用 Postman 测试。
7.2 认识 RESTful
REST(英文: Representational State Transfer,简称 REST)一种互联网软件架构设计的风格,但它并不是标准,它只是提出了一组客户端和服务器交互时的架构理念和设计原则,基于这种理念和原则设计的接口可以更简洁,更有层次, REST这个词,是Roy ThomasFielding 在他 2000 年的博士论文中提出的。
详细的RESTful规范,可以参考:
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
究竟什么是RESTful?
简而言之,就是充分利用HTTP请求规范去区分和处理不同的请求。在HTTP协议中,可以用于标识请求的除了URL(请求路径)外,还有Method(请求方法)和请求头信息等等,我们应该充分利用HTTP请求的这些属性去描述后端的服务接口。
特别在前后端分离的项目中,后端使用的是JavaEE服务,前端是静态的HTML和JavaScript,用好RESTful规范,后端的接口才能更容易被前端调用。
下面举一些示例。
GET /categories: 列出所有分类
POST /categories: 新增一个分类
GET /categories/ID: 获取某个指定的分类信息信息
PUT /categories/ID: 更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /categories/ID: 更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /categoreis/ID: 删除某个动物园
7.3 RESTful 的优点
➢ 轻量,直接基于 http,不再需要任何别的诸如消息协议 get/post/put/delete 为 CRUD 操作
➢ 面向资源,一目了然,具有自解释性。
➢ 数据描述简单,一般以 xml, json 做数据交换。
➢ 无状态,在调用一个接口(访问、操作资源)的时候,可以不 用考虑上下文,不用考虑当前状态,极大的降低了复杂度。
➢ 简单、低耦合
7.4 RESTful 原则
➢ 增 post 请求、删 delete 请求、改 put 请求、查 get 请求
➢ 请求路径不要出现动词
例如:查询订单接口 /boot/order/1021/1(推荐) /boot/queryOrder/1021/1(不推荐)
➢ 分页、排序等操作,不需要使用斜杠传参数
例如:订单列表接口 /boot/orders?page=1&sort=desc 一般传的参数不是数据库表的字段,可以不采用斜杠
7.5 Spring MVC 对 RESTfulf 风格的支持
SpringMVC 提供了以下几个注解,支持 RESTful 风格的后端服务的开发
- @RestController 返回 JSON 视图的控制器,即 @Controller+@ResponseBody
- @PostMapping 接收和处理 Post 方式的请求
- @DeleteMapping 接收 delete 方式的请求
- @PutMapping 接收 put 方式的请求
- @GetMapping 接收 get 方式的请求
- @ResponseBody 指定控制器的返回值作为响应身体输出(返回 JSON 格式数据)
- @RequestParam (单个字符接收)
- @PathVariable (路径传参)获取 url 中的参数,该注解是实现 RESTFul 最主要的一个注解(路径上的占位符的值)
- @RequestBody (实体类接收)指定控制器的参数接收 JSON 格式数据并需要反序列化(把JSON格式的请求参数反序列化为对象)
- @ResponseStatus 用于指定响应状态码(如:200、404、500等…)
附:预备知识:使用Java配置Spring
@Repository(dao层)、@Service(业务层) 和 @Controller(控制层)。
在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,
但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。
1 为什么使用Java配置
传统的Spring项目,需要使用XML配置,虽然自定义的组件可以通过注解简化配置,但依然有大量的非自定义组件需要通过XML配置。XML不如Java代码的灵活性(没有良好的语法支持,错误提示和面向对象特性),从Spring 3.x开始,Spring支持并建议使用基于Java代码的配置,全面替代基于XML的配置。Java配置也是后续学习的Spring Boot的推荐配置手段,实际开发中,我们可以结合XML、Java和注解三种配置方式,灵活使用。
1.1 使用“Java配置”实现依赖注入
(1)@Configuration 注解
在 Java 配置中,我们使用@Configuration标记某Java类为配置类,配置类的作用类似于传统的XML配置文件(如:applicationContext.xml)。
@Configuration
public class BeansConfig {
}
(2)@ComponentScan 注解
在配置类中,我们通过 @ComponentScan 注解来指定Spring启动时扫描组件的包范围,它的作用等效于XML配置时的<context:component-scan />。
@Configuration
@ComponentScan(basePackages="com.bjpowernode.service")
public class BeansConfig {
}
在com.bjpowernode.dao包中创建CategoryDao接口及实现类:
@Repository
public class CategoryDaoImpl implements CategoryDao {
@Override
public List<String> selectAll() {
return new ArrayList(){{add("喜剧");add("战争");add("动漫");}};
}
}
创建Spring容器,这时使用AnnotationConfigApplicationContext类替代传统的ClassPathXmlApplicationContext加载Spring配置类。
ApplicationContext ctx = new AnnotationConfigApplicationContext(BeansConfig.class);
CategoryDao dao = ctx.getBean(CategoryDao.class);
dao.findAll().forEach(x->System.out.println(x));
(3)使用 AnnotationConfigApplicationContext 创建Spring容器
使用XML进行配置时,我们可以通过 ClassPathXmlApplicationContext 来创建Spring容器;
使用Java配置时,我们可以通过通过 AnnotationConfigApplicationContext 来创建Spring容器。
ApplicationContext ctx = new AnnotationConfigApplicationContext(DiConfig.class);
CategoryDao target = ctx.getBean(CategoryDao.class);
(4)@Bean 注解
@Bean用于标记一个方法,Spring会调用这个方法并把方法返回结果作为Bean对象管理起来,它的作用等效于XML配置时的<beans:bean />。@Bean可以通过name属性指定Spring中Bean的对象名,如果不指定,默认Bean的对象名就是工厂方法的方法名。
如果想用一段Java代码来配置一个Bean并交给Spring托管,我们可以用编写一个工厂方法并且给它打上@Bean注解。@Bean注解适用于那些需要被Spring管理的,由外部框架提供而的对象,因为这些类我们无法使用注解进行托管标记。
以下示例为 Spring 配置“数据源”对象和“JdbcTemplate”,实现真实的JDBC数据查询:
@Configuration
@ComponentScan(basePackages = {"com.bjpowernode.dao","com.bjpowernode.service"})
public class BeansConfig {
@Bean
public DataSource dataSource() {
BasicDataSource ds = new org.apache.commons.dbcp.BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/MyCinema");
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("1234");
return ds;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate db = new JdbcTemplate();
db.setDataSource(dataSource);
return db;
}
}
让后就可以使用依赖注入为DAO提供JdbcTemplate:
@Repository
public class CategoryDaoImpl implements CategoryDao {
@Autowired
private JdbcTemplate db;
@Override
public List<Category> findAll() {
return db.query("select * from Category",
BeanPropertyRowMapper.newInstance(Category.class));
}
}
1.2 使用Java配置实现面向切面
@EnableAspectJAutoProxy注解
用于启用AOP注解配置
@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.bjpowernode.service","com.bjpowernode.aspect"})
public class AopConfig {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
CategoryBiz target = ctx.getBean(CategoryBiz.class);
target.findAll();
}
}
1.3 小结
Java配置往往是和注解配置联合一起使用的,一般情况下:
(1)自己编写的Bean可以通过注解进行配置和依赖注入(例如:@Repository、@Service、@Controller、@Autowired),只要通过Java配置类中的@ComponentScan注解指定Spring启动时的包扫描范围即可;
(2)第三方框架的Bean由于已经打包了,无法注解,则需要通过@Bean注解标记的工厂方法来创建和配置。
2 使用纯 Java 配置来构建简单的SSM项目
2.1 使用Spring的Java配置整合MyBatis
(1)创建Spring配置类,配置模型Bean
import java.io.IOException;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
@ComponentScan(basePackages = "mycinema")
@Configuration
public class BeansConfig {
//数据源
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mycinema");
dataSource.setUsername("root");
dataSource.setPassword("1234");
return dataSource;
}
//MyBatis的Session工厂
@Bean(name = "sqlSessionFactoryBean")
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
//如果有基于XML的Mapper配置,则需要该项
// PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
bean.setTypeAliasesPackage("mycinema.entity");
return bean;
}
//MyBatis的Mapper扫描器
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer bean = new MapperScannerConfigurer();
bean.setBasePackage("mycinema.mapper");
bean.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
return bean;
}
}
(2)编写实体类(Category)和Mapper(CategoryMapper)
public interface CategoryMapper {
@Select("select * from Category")
List<Category> findAll();
}
(3)添加单元测试
@ContextConfiguration(classes = BeansConfig.class) //注意这里使用 Java配置类 加载Spring上下文
@RunWith(SpringJUnit4ClassRunner.class)
public class CategoryMapperTest {
@Autowired
private CategoryMapper target;
@Test
public void testFindAll() {
target.findAll().forEach(x->System.out.println(x));
}
}
2.2 配置声明式事务管理
(1)在Java配置类中添加事务管理器
配置类中添加*** @EnableTransactionManagement 注解***,打开事务管理开关;然后再为Spring添加一个事务管理器Bean。
@ComponentScan(basePackages = "mycinema")
@EnableTransactionManagement
@Configuration
public class BeansConfig {
//事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager tx = new DataSourceTransactionManager();
tx.setDataSource(dataSource);
return tx;
}
...
}
(2)使用注解配置事务管理
@Service
public class CategoryBizImpl implements CategoryBiz {
@Autowired
private CategoryMapper categoryDb;
@Transactional
public void deleteAll(int[] ids) {
for(int id : ids) {
categoryDb.delete(id);
}
}
......
}
2.3 配置MVC框架和Web应用
(1)使用 WebMvcConfigurerAdapter 配置SpringMVC
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan("mycinema.web")
public class MvcConfig extends WebMvcConfigurerAdapter {
//配置ViewResolver
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
//配置静态资源路径
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//添加对外暴露的的访问路径,用于静态资源
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
//配置快速视图映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//免写controller方法快速实现 “/login -> ~/views/login.jsp” 映射
registry.addViewController("/login").setViewName("login");
registry.addViewController("/register").setViewName("register");
}
//配置文件上传处理
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
}
(2)使用 WebApplicationInitializer 配置Web应用(替代传统的web.xml)
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;
@Configuration
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(BeansConfig.class); // 加载业务Beans配置
ctx.register(MvcConfig.class); // 加载MVC Beans配置
ctx.setServletContext(servletContext);
// 配置编码过滤器
Filter characterEncodingFilter = new CharacterEncodingFilter("UTF-8");
servletContext.addFilter("characterEncodingFilter", characterEncodingFilter)
.addMappingForUrlPatterns(null, false, "/*");
// 配置MVC核心Servlet
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
3 附录:所需的依赖包pom.xml
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>