你的第一个Spring Boot应用

Posted 疯狂Java图书

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你的第一个Spring Boot应用相关的知识,希望对你有一定的参考价值。

创建可执行的JAR

由于Spring Boot应用内嵌了Web服务器(TomcatJettyUndertow),所以无须将SpringBoot应用部署到其他Web服务器中,Spring Boot应用完全可以独立运行。
在发布Spring Boot应用时,只需要将该应用打包成一个可执行的JAR包,以后就可直接通过该JAR包来运行Spring Boot应用了。
为了将Spring Boot应用打包成JAR包,首先需要保证在pom.xml文件中添加了SpringBoot Maven插件,也就是其中包含如下配置:
<build> <plugins> <!--定义Spring BootMaven插件,可用于运行SpringBoot应用 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>
此外,如果在pom.xml文件中定义了<packaging.../>元素,请确保该元素的值是jar,即该元素的内容如下:  
<packaging>jar</packaging> 
省略 <packaging.../> 元素是可以的,当在 pom.xml 文件中添加了 Spring Boot Maven 插件后, <packaging.../> 元素的内容默认就是 jar ,因此可以省略该元素。
接下来只要执行如下两条Maven命令即可生成可执行的JAR包:
mvncleanmvn package 
上面第一条命令指定执行到 Maven 生命周期的 clean 阶段,用于清除所有在构建过程中生成的文件。
上面第二条命令指定执行到Maven生命周期的package阶段,用于生成可执行的JAR包。
至于如何执行“mvn1 clean”和“mvn package”命令,同样可通过上面介绍过的任意一种方法:
  • 使用命令行窗口。

  • 使用IntelliJ IDEATerminal窗口。

  • 使用Run Anything

  • 使用Maven面板。

  • 使用运行配置。


通过Maven面板的Lifecycle节点可以查看该项目构建的生命周期,该生命周期就包含了cleanpackage等阶段,双击该阶段即可执行到Maven生命周期的对应阶段。
如果成功执行完构建生命周期的package阶段,则可看到如下输出:
[INFO] ---maven-jar-plugin:3.2.0:jar (default-jar) @ firstboot ---[INFO] Buildingjar: G:\publish\codes\01\1.2\firstboot\target\firstboot-0.0.1-SNAPSHOT.jar[INFO][INFO] ---spring-boot-maven-plugin:2.4.2:repackage(repackage) @ firstboot ---[INFO] Replacing main artifact with repackaged archive[INFO]------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------
上面的输出表明:Spring Boot应用的可执行的JAR包构建完成,该JAR包的文件名为firstboot-0.0.1-SNAPSHOT.jar,该文件名中的firstbootartifactId0.0.1-SNAPSHOTversion,都是由pom.xml文件指定的。
打包完成后,即可在项目的target目录下看到一个firstboot-0.0.1-SNAPSHOT.jar包,这就是可执行的JAR包。接下来只要执行如下Java命令即可启动Spring Boot应用:  
java -jar firstboot-0.0.1-SNAPSHOT.jar 
在打包过程中很容易出现如下错误(尤其是在 Windows 平台开发 Spring Boot 应用时):
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources) on project firstboot:Input length = 1 -> [Help 1][ERROR][ERROR] To see the full stack trace of the errors,re-run Maven with the -e switch.[ERROR] Re-run Maven using the -X switch to enablefull debug logging.
导致出现该错误的原因是: Spring Boot 应用需要在 src/main/resources/ 目录下添加配置文件 application.properties (或 application. yaml ), Windows 平台默认以 GBK 字符集保存这两个文件,但 Maven 打包时默认以 UTF-8 字符集读取配置文件。解决该错误非常简单,只要将 application.properties (或 application. yaml )以 UTF-8 字符集重新保存一次即可。
提示:
使用Notepad++打开需要转码的文件,然后通过Notepad++的“Encoding”主菜单即可完成转码。
由于Maven生命周期的缘故,执行“mvn package”命令会从默认生命周期的第一阶段一直执行到package阶段,而Maven的默认生命周期包含compile(编译项目)→test(单元测试)→ package(项目打包)→ install(安装到本地仓库)→ deploy(部署到远程)这几个核心阶段,这就意味着Maven在打包之前会先执行compiletest两个阶段。
如果Spring Boot应用包含了单元测试的测试用例(1.3节会介绍单元测试),那么“mvn package”命令会先执行compile阶段(编译项目),再执行test阶段(单元测试),最后才执行package(项目打包)阶段。如果在单元测试阶段某个测试失败,则会显示类似于如下的测试失败信息:  
[ERROR] Tests run: N, Failures: M, Errors: L,Skipped: 0  
上面信息显示运行了N个测试方法,其中失败了M个,有L个出现错误。一旦在构建过程中出现上述错误,就意味着Maven无法通过test阶段,从而根本无法执行到package阶段,这样打包也会失败。解决这种错误的方法也很简单:检查并修改测试出错的单元测试,使之能通过单元测试。
如果时间仓促,来不及修改所有的单元测试,可先将那些测试出错的单元测试注释掉,这样能暂时让项目通过单元测试,打包成功。但这只是权宜之计,在项目最终发布之前,还是要保证项目的所有单元测试都能测试通过。

开发业务组件

前面控制器的处理方法直接返回了字符串作为响应,这在实际项目中肯定是不行的,实际项目中的控制器要调用业务组件来处理用户响应,因此本例会开发一个业务组件来处理用户请求。
为了和前一个简单的示例区分开,此处将前一个项目复制一份,重命名为“firstboot2”。
本例的业务组件要能实现添加图书、删除图书、列出全部图书这三个功能。下面是本例业务组件的接口代码。
程序清单:
public interface BookService{ List<Book>getAllBooks(); Integer addBook(Book book); void deleteBook(Integer id);}
Service 组件的实现类则调用 DAO 组件的方法来实现上述方法。下面是 BookService 组件的实现类代码。
程序清单:
@Service@Transactional(propagation =Propagation.REQUIRED, timeout = 5)public class BookServiceImplimplements BookService{ // 依赖注入容器中的BookDao组件 @Autowired private BookDao bookDao; @Override publicList<Book> getAllBooks(){ return(List<Book>) bookDao.findAll(); } @Override publicInteger addBook(Book book){ bookDao.save(book); returnbook.getId(); } @Override publicvoid deleteBook(Integer id){ bookDao.deleteById(id); }}
上面第1行代码使用了@Service注解修饰该实现类,且该实现类位于org.crazyit. firstboot.service.impl包下,也就是位于FirstbootApplication类所在包的子包下,因此Spring Boot会自动扫描该实现类,并将它配置成容器中的Bean
上面第2行代码使用了@Transactional注解修饰该Service组件,该注解指定事务传播规则为“REQUIRED”,事务超时时长为5秒,Spring将会为该Service组件生成事务代理,从而为该Service组件中的每个方法都添加事务。为目标组件生成事务代理是Spring AOP的功能,但生成事务代码所需要的事务管理器,同样由Spring Boot的自动配置提供。
上面第3行代码使用了@Autowired注解修饰BookDao实例变量,这也是Spring的基本用法,Spring将会把容器中唯一的、类型为BookDaoBean注入该实例变量
接下来要对前面的控制器类进行一些修改,让SpringBookService组件注入控制器,控制器调用BookService的方法来处理用户请求。
下面是修改后的BookController类的代码。
程序清单:
@Controllerpublic class BookController{ @GetMapping("/") publicString index(Model model) { model.addAttribute("tip", "欢迎访问第一个Spring Boot应用"); return"hello"; } @GetMapping("/rest") @ResponseBody public ResponseEntity restIndex() { returnnew ResponseEntity<>("欢迎访问第一个Spring Boot应用", null, HttpStatus.OK); } @Autowired privateBookService bookService;  @PostMapping("/addBook") publicString addBook(Book book, Model model) { bookService.addBook(book); return"redirect:listBooks"; } @PostMapping("/rest/books") @ResponseBody public ResponseEntity<Map<String, String>> restAddBook(@RequestBodyBook book) { bookService.addBook(book); returnnew ResponseEntity<>(Map.of("tip","添加成功"), null, HttpStatus.OK); }  @GetMapping("/listBooks") publicString list(Model model) { model.addAttribute("books", bookService.getAllBooks()); return"list"; } @GetMapping("/rest/books") @ResponseBody public ResponseEntity<List<Book>> restList() { returnnew ResponseEntity<>(bookService.getAllBooks(), null, HttpStatus.OK); }  @GetMapping("/deleteBook") publicString delete(Integer id) { bookService.deleteBook(id); return"redirect:listBooks"; } @DeleteMapping("/rest/books/{id}") @ResponseBody public ResponseEntity<Map<String,String>> restDelete(@PathVariable Integer id) { bookService.deleteBook(id); returnnew ResponseEntity<>(Map.of("tip","删除成功"), null, HttpStatus.OK); }}
上面的BookController类增加了一个BookService实例变量,且使用了@Autowired注解修饰,因此Spring就会将容器中唯一的、类型为BookServiceBean注入该实例变量。
接下来该BookController定义了6个处理方法,这些处理方法使用了@GetMapping@PostMapping@DeleteMapping等注解修饰,映射这些处理方法能处理来自不同URL地址的请求。这些注解都是@RequestMapping的简化版,用于指定被修饰的方法仅能处理GETPOSTDELETE请求。这些注解都属于Spring MVC的基本注解,并不属于Spring Boot
上面这些方法定义了两个版本:带界面响应的版本和RESTful版本。使用@ResponseBody修饰的方法就是用于生成RESTful响应的方法。
对于RESTful响应的处理方法,SpringBoot应用无须提供视图页面,在前后端分离的架构中,前端应用会负责提供用户界面、处理用户交互。Spring Boot应用只要暴露RESTful接口即可。
对于要生成界面的处理方法,程序还需要为它们提供视图页面。首先在前面的hello.html页面中增加一个表单,该表单供用户填写图书信息。修改后的hello.html页面代码如下。
程序清单
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <metacharset="UTF-8"/> <title>第一个Spring Boot应用</title> <!-- 引用WarJar中的静态资源--> <link rel="stylesheet"th:href="@{/webjars/bootstrap/4.5.3/css/ bootstrap.min.css}"/> <scripttype="text/javascript" th:src="@{/webjars/jquery/3.5.1/jquery.js}"> </script></head><body><div> <!-- 使用th:text将表达式的值绑定到标准HTML元素 --> <divclass="alert alert-primary"th:text="${tip}"></div> <h2>添加图书</h2> <formmethod="post" th:action="@{/addBook}"> <divclass="form-group row"> <labelfor="title" class="col-sm-3 col-form-label">图书名:</label> <divclass="col-sm-9"> <input type="text" id="title"name="title" class="form-control"placeholder="输入图书名"> </div> </div> <divclass="form-group row"> <labelfor="author" class="col-sm-3 col-form-label">作者:</label> <divclass="col-sm-9"> <input type="text" id="author"name="author" class="form-control"placeholder="输入作者"> </div> </div> <divclass="form-group row"> <labelfor="price" class="col-sm-3 col-form-label">价格:</label> <div> <input type="number" step="0.1"id="price" name="price" class="form-control"placeholder="输入价格"> </div> </div> <divclass="form-group row"> <divclass="col-sm-6 text-right"> <button type="submit" class="btnbtn-primary">添加</button> </div> <divclass="col-sm-6"> <button type="reset" class="btnbtn-danger">重设</button> </div> </div> </form></div></body></html> 
上面页面中添加了一个Bootstrap样式的表单,该表单的界面看起来会比较美观,该表单的提交地址是addBook,与前面BookController中处理方法定义的处理地址对应。
还需要一个list.html页面用于显示所有图书,该页面代码如下。
程序清单:
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><head> <metacharset="UTF-8"/> <title>所有图书</title> <!-- 引用WarJar中的静态资源--> <link rel="stylesheet"th:href="@{/webjars/bootstrap/4.5.3/css/bootstrap.min.css}"/> <scripttype="text/javascript" th:src="@{/webjars/jquery/3.5.1/jquery.js}"> </script></head><body><div> <h2>全部图书</h2> <tableclass="table table-hover"> <tr> <th>书名</th> <th>作者</th> <th>价格</th> <th>操作</th> </tr> <trth:each="book : ${books}"> <tdth:text="${book.title}">书名</td> <tdth:text="${book.author}">作者</td> <tdth:text="${book.price}">0</td> <td><ath:href="@{/deleteBook?id=}+ ${book.id}">删除</a></td> </tr> </table> <divclass="text-right"><a class="btnbtn-primary" th:href="@{/}">添加图书</a></div></div></body></html>
该页面代码中的第21行代码使用th:each标签对指定集合进行迭代,这也是Thymeleaf的功能。Thymeleaf与传统视图技术的最大区别在于:传统视图技术总是使用额外的标签来控制页面数据的显示;而Thymeleaf则使用额外的th:*属性来控制页面数据的显示。这样做的好处在于:浏览器能自动忽略HTML标签中不认识的属性(th:*属性),因此,即使在不执行动态解析的情况下,也可直接使用浏览器查看Thymeleaf页面效果。

开发DAO组件

前面BookService中用到了BookDao组件和Book类,这些都是与持久化相关的类,本例直接使用SpringBoot Data JPA来访问数据库,为此首先要为项目添加如下依赖:
  • Spring Boot Data JPA依赖。

  • mysql数据库驱动依赖。

pom.xml文件的<dependencies.../>元素中添加如下两个子元素来添加依赖。
程序清单:
<!-- Spring Boot Data JPA依赖 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- MySQL数据库驱动依赖 --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency> 
添加了上面的依赖之后,重新加载项目的依赖库,然后在项目的src\main\application\目录下添加一个application.properties文件—这个文件是Spring Boot项目的配置文件,当整合不同的项目时,该配置文件支持大量不同的属性,不同的属性也由不同的处理类负责读取(后面深入介绍Spring Boot整合时,会详细介绍该文件中不同属性的作用)。
此处只使用application.properties文件配置数据库的连接信息,该文件的内容如下。
程序清单
# 数据库URL地址spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC# 连接数据库的用户名spring.datasource.username=root# 连接数据库的密码spring.datasource.password=32147# 指定显示SQL语句spring.jpa.show-sql=true# 指定根据实体自动建表spring.jpa.generate-ddl=true
上面配置文件指定了连接数据库的基本信息:URL地址、用户名和密码,并指定了JPA能根据实体类自动建表,还会显示它所执行的SQL语句。
上面的数据库连接信息指定连接MySQLspringboot数据库,因此需要创建一个springboot数据库,仅创建数据库即可,应用启动时会自动建表。
为项目创建一个Book实体类,该实体类的代码如下。
程序清单
@Entity@Table(name = "book_inf")public class Book{ @Id @Column(name= "book_id") @GeneratedValue(strategy = GenerationType.IDENTITY) privateInteger id; privateString title; privateString author; privatedouble price; // 省略getter、setter方法 ...}
此处再次体现了Spring Boot的自动配置。上面的配置文件仅仅指定了连接数据库的基本信息,Spring Boot将会自动在容器中配置一个DataSourceBean;上面的配置文件仅仅指定了两个JPA属性,Spring Boot将会自动在容器中配置一个EntityManagerFactoryBean。这一切都是“静悄悄”地自动发生的,当然,这正是SpringBoot的职责所在。
为项目创建DAO组件:BookDao,该DAO组件的接口代码如下。
程序清单
public interface BookDaoextends CrudRepository<Book, Integer>{}
BookDao接口完全是一个空接口,它仅仅继承了CrudRepository,但它实际上已经拥有了大量方法。
这得益于Spring Data的优秀设计,继承了CrudRepository接口的BookDao不需要提供实现类,Spring Data会自动为它动态生成实现类,并将该实现类的实例部署在Spring容器中。不仅如此,Spring Data还可为BookDao动态增加很多查询方法,本书第5章会深入介绍Spring Boot整合Spring Data后的强大功能,此处暂不深入。
Spring Boot应用的主程序无须任何变化,依然只需要调用SpringApplicationrun()方法即可。该应用既提供了用户界面供浏览器访问,又提供了RESTful接口供前端应用或移动APP调用。
启动该Spring Boot应用,使用浏览器访问该应用,可以看到如图21所示的页面。

21   表单页面
在图21所示的页面中填写图书信息,然后单击“添加”按钮,即可看到如图22所示的图书列表页面。

你的第一个Spring Boot应用(2)

22   列出全部图书
单击图22所示页面中的“删除”链接,即可删除该图书。
正如在前面所看到的,该应用还提供了RESTful接口,可使用Postman来测试RESTful接口。
使用Postman向“http://localhost:8080/rest/books”发送GET请求,可以看到如图23所示的结果。

你的第一个Spring Boot应用(2)

23   发送 GET 请求测试 RESTful 接口
使用Postman向“http://localhost:8080/rest/books”发送POST请求,可以看到如图24所示的结果。

你的第一个Spring Boot应用(2)

24   发送 POST 请求测试 RESTful 接口
使用Postman向“http://localhost:8080/rest/books/1”发送DELETE请求可删除ID1的图书,此时可以看到如图25所示的结果。

你的第一个Spring Boot应用(2)

25   发送 DELETE 请求测试 RESTful 接口
以上内容摘自李刚老师的作品《 疯狂Spring Boot终极讲义 》,电商平台预订中,即刻扫码,了解图书详情,6折优惠,先领劵再结账哦


李刚老师亲自录制的 Spring Boot技术视频 ,预售秒杀中,敬请关注!
关注“ 疯狂Java图书 ”公众号,获得更多你想要的Java开发技术干货

 
   
   
 

如果希望阅读Spring Boot的后续干货好文
欢迎 在看留言分享至朋友圈 三连
点击“ 阅读原文”了解图书详情

以上是关于你的第一个Spring Boot应用的主要内容,如果未能解决你的问题,请参考以下文章

一张图,理顺 Spring Boot应用在启动阶段执行代码的几种方式

一张图帮你记忆,Spring Boot 应用在启动阶段执行代码的几种方式

一张图,理顺 Spring Boot应用在启动阶段执行代码的几种方式

一张图,理顺 Spring Boot应用在启动阶段执行代码的几种方式

年轻人的第一个 Spring Boot 应用,太爽了!

让你的spring-boot应用日志随心所欲--spring boot日志深入分析