0


天狗实战(二)SpringBoot API开发详解 --SpringMVC注解+封装结果+支持跨域+打包(下)

CSDN成就一亿技术人


本文目录


前言

在上文,我们基于Maven,已经把三层架构项目搭建起来了,重点掌握的是如何规范的创建Maven项目、如何统一管理依赖版本。还没掌握的同学,可以先看看上文:天狗实战SpringBoot+Vue(二)项目结构搭建(上)

本文,将基于上文搭建的项目结构,开始创建SpringBoot项目,并进行API开发,最终输出给前端两个API:分别基于GETPOST 请求。

实现层面:会结合实战 解读SpringMVC常用注解的使用,并实现 API结果统一封装支持跨域请求,以及 多Jar如何打包

规范层面:会结合实战 把 三层架构 都串起来,包括各层的命名规范对象职责

这些都是实际项目必须掌握的,所以跟上节奏,Let’s Go!

对应思维导图的红框处:

在这里插入图片描述


专栏介绍

本文对应前端博文:基于Vue+Less+axios封装+ElementUI搭建项目底层支撑实战

因为可能还有很多同学还不清楚上下文,所以简单介绍一下这个专栏要做的事:

天罡老哥和狗哥(博客主页)有意

从0到1

带大家搭建一个

SpringBoot+SpringCloud+Vue

的前后端分离项目!
打造一个短小精悍、技术主流、架构规范的前后端分离实战项目!我负责后端,狗哥负责前端!
目的就是让大家通过项目实战,学到一些真东西,将所学理论落地,助力有心强大的你更快的成长!开启你的工作之旅,让开发游刃有余!

详细的后端规划后端大纲思维导图在开篇已经给出,你可以到开篇查收:基于SpringBoot+SpringCloud+Vue前后端分离项目实战 --开篇。


一、创建SpringBoot项目

因为我们已经创建Maven项目:tg-book-web,所以基于此,创建SpringBoot项目,简单来说只需要

3小步

① 修改pom.xml:加依赖 spring-boot-starter-web

② 创建启动类:加注解 @SpringBootApplication

③ 创建控制器类:加注解 @RestController

1.1 添加springboot依赖

1). web层增加依赖

tg-book-webpom.xml 增加依赖如下:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
    其它dependency。。。
</dependencies>

在这里插入图片描述

2). 父项目统一管理依赖

上面的

spring-boot-starter-web

没有定义版本号

version

,还记得上文我们说的

父项目统一管理依赖

吗?

没错,就是父pom.xml

dependencyManagement

节点,用来统一管理依赖版本。

所以,我们需要在

dependencyManagement

节点下增加依赖

spring-boot-dependencies

,这里包括SpringBoot为我们定义好的所有依赖。

<properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><!-- spring boot 版本--><spring-boot.version>2.3.12.RELEASE</spring-boot.version></properties><dependencyManagement><dependencies><!-- spring boot 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency>
        其它dependency。。。
    </dependencies></dependencyManagement>

在这里插入图片描述

另外,这里有一个小知识点:统一管理依赖有两种方式,一种是上面这样,另外还可以把parent定义为

spring-boot-starter-parent

,实际内部也是依赖

spring-boot-dependencies

。综合考虑Maven是单继承,所以建议使用上面

spring-boot-dependencies

的方式。

3).Maven刷新依赖

温馨提示

:添加完依赖,别忘了刷新Maven,上文说过了,我再重复一遍!

  • 父项目右键-》Maven-》Reload Project
  • 父项目右键-》Run Maven-》Reimport

我最喜欢的是在最右侧的Maven选项卡,选中父项目,点上面的刷新图标,如下图:(或者右键Reload Project)

IDEA刷新Maven依赖

1.2 创建启动类

创建启动类:

org.tg.book.ApplicationRunner

,如下图

java包右键菜单->New->Java Class

在这里插入图片描述

输入类的全名,包括包名,如下图:

在这里插入图片描述

启动类里面只有一个入口main方法,用于项目的启动。

main方法里调用

SpringApplication.run

方法,并配上

@SpringBootApplication

注解,这样就齐活了,里面做了啥先不用深挖!

packageorg.tg.book;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassApplicationRunner{publicstaticvoidmain(String[] args){SpringApplication.run(ApplicationRunner.class, args);}}

1.3 创建控制器类

创建控制器类:

org.tg.book.web.controller.BookAdminController

,用于向前端提供API,定义如下:

packageorg.tg.book.web.controller;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/admin")publicclassBookAdminController{@GetMapping("/hello")publicStringhello(@RequestParam("name")String name){return"hello:"+ name;}}

到这,我们就已经定义好了一个GET请求的API,请求路径为:/admin/hello?name=xxx

是不是很容易?除了注解,写的都是入门级的代码,一个API就定义好了,所以用SpringBoot开发API可以说是零门槛,OK,我们先运行起来享受一下吧!

对于这几个注解,先别着急,注解会在下面的【3.1 Spring MVC常用注解】里详解!

1.4 Run 或 Debug

我们直接使用IDEA就可以运行,不需要部署Tomcat!

运行(Run):

ApplicationRunner

类的右键菜单

Run 'ApplicationRunner....main()'

调试(Debug):

ApplicationRunner

类的右键菜单 ``Debug ‘ApplicationRunner…main()’`

控制台Console输出如下就代表启动成功了,默认端口是8080

Tomcat started on port(s): 8080 (http) with context path ''
Started ApplicationRunner in xxx seconds

我们用浏览器测试请求:

http://localhost:8080/admin/hello?name=tian

返回结果正如期望,调用成功!

hello:tian

二、开发图书管理API

上面的/hello API,只是开胃小菜,我们来两个正式点的API:

① GET 请求:根据id获取图书

路径:/admin/book;入参:图书id;返回:图书对象

② POST 请求:新增/修改图书

路径:/admin/book;入参:图书对象;返回:图书id

说明:

包结构对应如下图,先上代码,再逐块讲解!

在这里插入图片描述

2.1 web层

BookAdminController

  • getBook方法:Get请求
  • saveBook方法:Post请求
  • web层会调用service层,所以依赖注入了BookService
  • 注解会在下面的【3.1 Spring MVC常用注解】里详解!
packageorg.tg.book.web.controller;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importorg.tg.book.common.dto.TgResult;importorg.tg.book.service.BookService;importorg.tg.book.service.bo.BookBO;importorg.tg.book.web.vo.BookVO;@RestController@RequestMapping("/admin")publicclassBookAdminController{@AutowiredprivateBookService bookService;@GetMapping("/book")publicTgResult<BookBO>getBook(@RequestParam("id")Integer id){returnTgResult.ok(bookService.getBook(id));}@PostMapping("/book")publicTgResult<Integer>saveBook(@RequestBodyBookVO bookVO){returnTgResult.ok(bookService.saveBook(bookVO.toBookBO()));}}

BookVO

web层对象,即前端传入的对象!关于各层对象,会在【3.3各层对象说明】详解

packageorg.tg.book.web.vo;importcom.fasterxml.jackson.annotation.JsonFormat;importlombok.Data;importorg.springframework.beans.BeanUtils;importorg.tg.book.service.bo.BookBO;importjava.io.Serializable;importjava.util.Date;@DatapublicclassBookVOimplementsSerializable{privateInteger id;privateString bookName;privateString bookNo;privateString bookAuthor;privateInteger bookType;privateString bookDesc;privateString publisher;@JsonFormat(pattern ="yyyy-MM-dd")privateDate publishDate;publicBookBOtoBookBO(){BookBO bookBO =newBookBO();BeanUtils.copyProperties(this, bookBO);return bookBO;}}

2.2 service层

BookService

定义service接口:

packageorg.tg.book.service;importorg.tg.book.service.bo.BookBO;publicinterfaceBookService{BookBOgetBook(Integer id);IntegersaveBook(BookBO bookBO);}

BookServiceImpl

实现service方法:service层会调用dal层,所以依赖注入了BookMapper

packageorg.tg.book.service.impl;importorg.springframework.beans.BeanUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.tg.book.dal.mapper.BookMapper;importorg.tg.book.dal.po.Book;importorg.tg.book.service.BookService;importorg.tg.book.service.bo.BookBO;@ServicepublicclassBookServiceImplimplementsBookService{@AutowiredprivateBookMapper bookMapper;@OverridepublicBookBOgetBook(Integer id){Book book = bookMapper.selectByPrimaryKey(id);if(book ==null){returnnull;}BookBO bookBO =newBookBO();BeanUtils.copyProperties(book, bookBO);return bookBO;}@OverridepublicIntegersaveBook(BookBO bookBO){if(bookBO ==null){returnnull;}Book book =newBook();BeanUtils.copyProperties(bookBO, book);if(bookBO.getId()==null){return bookMapper.insert(book);}else{return bookMapper.updateByPrimaryKey(book);}}}

BookBO

service层对象

packageorg.tg.book.service.bo;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;importjava.io.Serializable;importjava.util.Date;@Data@NoArgsConstructor@AllArgsConstructor@BuilderpublicclassBookBOimplementsSerializable{privateInteger id;privateString bookName;privateString bookNo;privateString bookAuthor;privateInteger bookType;privateString bookDesc;privateString publisher;privateDate publishDate;}

2.3 dal层

BookMapper

定义数据访问接口:

packageorg.tg.book.dal.mapper;importorg.tg.book.dal.po.Book;publicinterfaceBookMapper{BookselectByPrimaryKey(Integer id);Integerinsert(Book book);IntegerupdateByPrimaryKey(Book book);}

BookMapperImpl

数据访问实现类,这里没有保存到数据库,因为还没有讲,所以直接保存在内存List中,当我们集成Mybatis时会改造这块代码。

packageorg.tg.book.dal.mapper.impl;importorg.springframework.beans.BeanUtils;importorg.springframework.stereotype.Repository;importorg.tg.book.dal.mapper.BookMapper;importorg.tg.book.dal.po.Book;importjava.util.ArrayList;importjava.util.List;importjava.util.Optional;@RepositorypublicclassBookMapperImplimplementsBookMapper{privatefinalList<Book> bookList =newArrayList<>();@OverridepublicBookselectByPrimaryKey(Integer id){return bookList.stream().filter(p -> p.getId().equals(id)).findFirst().orElse(null);}@OverridepublicIntegerinsert(Book book){
        book.setId(bookList.size()+1);
        bookList.add(book);return book.getId();}@OverridepublicIntegerupdateByPrimaryKey(Book book){Optional<Book> first = bookList.stream().filter(p -> p.getId().equals(book.getId())).findFirst();if(first.isPresent()){BeanUtils.copyProperties(book, first.get());return book.getId();}returnnull;}}

Book

持久化对象,与数据库表字段对应

packageorg.tg.book.dal.po;importlombok.Data;importjava.util.Date;@DatapublicclassBook{privateInteger id;privateString bookName;privateString bookNo;privateString bookAuthor;privateInteger bookType;privateString bookDesc;privateString publisher;privateDate publishDate;}

2.4 Postman测试

最终实现了两个API,可以先对号入座!

在这里插入图片描述
在这里插入图片描述


三、庖丁解牛API实现

上面只贴了代码,相信大家肯定会有疑问,那下面我们就来一一化解!

3.1 Spring MVC 常用注解

接下来说说如下图中这几个注解的作用。

在这里插入图片描述

  • 1) @RestController

表示用于

处理web请求的控制器

。我们开发前后端分离的Restful风格API,使用@RestController就对了。

关于@RestController经常拿来与@Controller做区别和联系,所以简单说明一下:

@Controller

也表示用于处理web请求的控制器,返回的是视图,配合视图解析器才能返回到指定页面,当需要直接返回数据时需要配合加@ResponseBody注解。

@ResponseBody

表示返回的是数据对象,而不经过视图解析器。

@RestController 等同于@Controller + @ResponseBody,这样我们开发Restful API就方便了,因为我们就是直接返回数据对象,像普通文本、JSON、XML等等对象,所以开发Restful风格的前后端分离项目通常都会用@RestController。

  • 2) @RequestMapping

用于处理

请求映射

,就是定义请求路径,可以作用于类和方法上。

value:请求的映射地址

method:请求方式,包括 GET, POST, PUT, DELETE 等等

produces:响应类型,像"application/json;charset=UTF-8"代表返回json,"application/xml;charset=UTF-8"代表返回xml,等等

另外,从@RequestMapping派生了4个常用注解:,分别对应常见的4种请求方法,只可以作用于方法上,因为更方便,所以通常 代替@RequestMapping 作用于方法上。

  • 3) @GetMapping和@PostMapping

@GetMapping、@PostMapping、@PutMapping、@DeleteMapping 是同类注解,都是从

@RequestMapping

派生的,分别对应4类请求方法,所以用法和@RequestMapping 相同,区别是只可以作用于方法上。

所以,上面的API,类路径使用的是@RequestMapping,方法路径使用的@GetMapping和@PostMapping

  • 4) @RequestParam

用于获取

传入参数

的值,就是在请求url上

?

后拼接的参数,通常用于GET、DELETE请求。

value:参数的名称

required:定义该传入参数是否必须,默认为true

  • 5) @RequestBody

用于获取

请求体

的内容,就是前端通过

body json

传过来的对象,通常用于POST、PUT请求。


3.2 API结果统一封装

接下来说说如下图中

TgResult

在这里插入图片描述

接口通常都是前后端协商定义的,通常都会定义统一返回JSON格式数据,这样可以返回标准的结构化数据,另外还可以传达更多的信息。

我这里定义

success、code、message、data

四个字段,说明:

  • success:是否成功的标识;
  • code:返回给前端的状态码;
  • message: code对应的消息描述;
  • data: 该接口定义的对象数据,因为API结果对象有多种多样,所以会用到泛型类

我们将这种通用的对象类,定义在

tg-book-common

中,命名为

org.tg.book.common.dto.TgResult

,就是上面API返回的

TgResult

.

packageorg.tg.book.common.dto;importlombok.Data;importjava.io.Serializable;@DatapublicclassTgResult<T>implementsSerializable{privateboolean success;privateString code;privateString message;privateT data;publicstatic<T>TgResult<T>ok(T data){returnok("200","成功", data);}publicstatic<T>TgResult<T>ok(String code,String message,T data){returnbuild(true, code, message, data);}publicstatic<T>TgResult<T>fail(String code,String message){returnfail(code, message,null);}publicstatic<T>TgResult<T>fail(String code,String message,T data){returnbuild(false, code, message, data);}privatestatic<T>TgResult<T>build(boolean success,String code,String message,T data){TgResult<T> result =newTgResult<>();
        result.setSuccess(success);
        result.setCode(code);
        result.setMessage(message);
        result.setData(data);return result;}}

只要你会定义泛型类和泛型方法,这个代码你就能看懂,只是重载定义了

ok

fail

两个方法便于外部调用。

调用时直接

TgResult.ok

TgResult.fail

即可!

3.3 各层对象说明

命名规范:

在这里插入图片描述

项目做的越大,各层的

高内聚

就显的格外重要,职责越单一,代码耦合性就越低!

所以,对于三层(web层、service层、dal层),规范使用各层自定义的对象,对象在各层传输时进行对象转换后使用!

在这里插入图片描述

  • VO:值对象,在web层使用,对应接口的参数,例如:BookVO
  • BO:业务对象,在service层使用,对应web层传入的参数 和 返回给web层的结果 ,例如:BookBO
  • PO:持久化对象,在dal层使用,对应数据库的表字段,例如:Book
  • DTO:数据传输对象,这里顺带提一下DTO,当我们与外部接口调用时使用。

3.4 各层包结构规范

对于上面代码的各层包结构,都是有讲究的,你发现了吗?

在这里插入图片描述

各层的包的前缀都是以项目Maven的groupId+artifactId为根,我们定义的groupId为

org.tg

,所以:

tg-book-web

org.tg.book.web

tg-book-service

org.tg.book.service

tg-book-dal

org.tg.book.dal

3.5 Spring IOC

接下来我们说说IOC(Inversion of Control):控制反转

在这里插入图片描述

像上面这个bookService,没有new就使用,按正常编码是会报错的,

那为什么没报错呢?

是因为它使用了

@Autowired

注解,Spring会自动给它new,还有属性赋值等都会由Spring管理,这样我们在调用的地方就不会报错了。

说的更直白点:bookService不由使用它的BookAdminController控制,这样做的好处:如果想替换BookService的实现,不用更改BookAdminController的代码,而是由外部控制,从而达到解耦合的目的!

而相应的在BookService类上也需要加@Service注解,如下图:

在这里插入图片描述

常见的组件注解如下:

  • @Service: 通常放在service层的服务类上
  • @Repository: 通常放在dal层的数据访问类上
  • @Controller: 通常放在web层控制器的类上
  • @Component: 代表通用的组件,从它派生了上面3个注解,用于各个实际的场景.

在这里插入图片描述

被加上这些注解的对象,在Spring中称之为Bean

3.6 约定优于配置

我们使用SpringBoot,有必要了解它采用的约定优于配置的思想。

约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式

白话说就是:约定好的就不用配置了!

比如,当我们导入一个

spring-boot-starter-web

后,SpringBoot就知道你要开发API,所以就会自动帮我们引入

Spring MVC

的相关依赖和内置的

Tomcat

容器,所以,你就很方便,它很懂你对不对?当然,你不想按约定的来的话,也可以通过配置来修改。

SpringBoot约定以

starter

的形式减少依赖,于是相继推出了很多常用的 starter,就像本文我们用到的

spring-boot-starter-web

,所以你看到,我们只添加了一个依赖,就可以实现API的定义。


四、支持跨域

下面咱们说说跨域

默认我们通过SpringBoot提供的API,由于浏览器的

同源策略

:前端和后端

不同源

就会报跨域的

CORS

错误。

支持跨域有很多方式,因为很多实现会涉及到很多知识,所以这里不扩展说明,我先使用最简单的方式处理!

BookAdminController

上增加

@CrossOrigin

注解,像这样:

在这里插入图片描述

这样,BookAdminController下的所有API就都支持跨域请求了,是不是又简单又容易理解?


五、打包

再说说打包

如何打包多jar包的springboot项目?

可以使用Maven build插件:

spring-boot-maven-plugin

,通过它可以打成一个jar包。

在启动类所在的项目

tg-book-web

pom.xml

中,添加

build

节点,配置如下:

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.12.RELEASE</version><configuration><executable>true</executable></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins><finalName>tg-book</finalName></build>

这里我们最终会打成一个jar,即通过finalName指定的

tg-book.jar

具体操作:

① 对

父tg-book

,Maven先

clean

,再

install

② 对启动类所在的项目

tg-book-web

,Maven执行

package

最终生成的 tg-book.jar 位置在

\tg-book\tg-book-web\target\tg-book.jar


最后

通过本文,我们最终输出给前端两个API:分别基于GETPOST 请求。
但还有很多遗留点,像日志、统一异常处理、参数校验、权限认证等等,不要担心没有相关内容,这篇已经很长,我会按照思维导图在后面的博文都讲到,当然,如果你有你的需求,也可以联系我来增加需求!
最后,我希望大家能跟着本文去实现这两个API,并可以举一反三,按照本文的设计实现更多的API,这样,这个项目的后端是不是可以不仅是我一个人?
如果觉得写的不错,订阅起来吧,后面还有更多干货输出… ,让我们一起拉开有趣的程序人生~~~
另外,别忘了关注天哥:天罡gg ,发布新文不容易错过: https://blog.csdn.net/scm_2008

有没有觉得干货太多?
能不能跟上节奏?
请投票告诉我!


本文转载自: https://blog.csdn.net/scm_2008/article/details/129674701
版权归原作者 天罡gg 所有, 如有侵权,请联系我们删除。

“天狗实战(二)SpringBoot API开发详解 --SpringMVC注解+封装结果+支持跨域+打包(下)”的评论:

还没有评论