天狗实战springbootapi开发详解--springmvc注解+封装结果+支持跨域+打包(下)(代码片段)

天罡gg 天罡gg     2023-04-10     380

关键词:


本文目录


前言

在上文,我们基于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.xmldependencyManagement节点,用来统一管理依赖版本。

所以,我们需要在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)

1.2 创建启动类

创建启动类:org.tg.book.ApplicationRunner,如下图 java包右键菜单->New->Java Class

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

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

main方法里调用SpringApplication.run方法,并配上@SpringBootApplication注解,这样就齐活了,里面做了啥先不用深挖!

package org.tg.book;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApplicationRunner 
    public static void main(String[] args) 
        SpringApplication.run(ApplicationRunner.class, args);
    

1.3 创建控制器类

创建控制器类:org.tg.book.web.controller.BookAdminController,用于向前端提供API,定义如下:

package org.tg.book.web.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class BookAdminController 

    @GetMapping("/hello")
    public String hello(@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常用注解】里详解!
package org.tg.book.web.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.tg.book.common.dto.TgResult;
import org.tg.book.service.BookService;
import org.tg.book.service.bo.BookBO;
import org.tg.book.web.vo.BookVO;

@RestController
@RequestMapping("/admin")
public class BookAdminController 

    @Autowired
    private BookService bookService;

    @GetMapping("/book")
    public TgResult<BookBO> getBook(@RequestParam("id") Integer id) 
        return TgResult.ok(bookService.getBook(id));
    

    @PostMapping("/book")
    public TgResult<Integer> saveBook(@RequestBody BookVO bookVO) 
        return TgResult.ok(bookService.saveBook(bookVO.toBookBO()));
    

BookVO

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

package org.tg.book.web.vo;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import org.tg.book.service.bo.BookBO;

import java.io.Serializable;
import java.util.Date;

@Data
public class BookVO implements Serializable 
    private Integer id;
    private String bookName;
    private String bookNo;
    private String bookAuthor;
    private Integer bookType;
    private String bookDesc;
    private String publisher;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date publishDate;

    public BookBO toBookBO() 
        BookBO bookBO = new BookBO();
        BeanUtils.copyProperties(this, bookBO);
        return bookBO;
    

2.2 service层

BookService

定义service接口:

package org.tg.book.service;

import org.tg.book.service.bo.BookBO;

public interface BookService 

    BookBO getBook(Integer id);

    Integer saveBook(BookBO bookBO);

BookServiceImpl

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

package org.tg.book.service.impl;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.tg.book.dal.mapper.BookMapper;
import org.tg.book.dal.po.Book;
import org.tg.book.service.BookService;
import org.tg.book.service.bo.BookBO;

@Service
public class BookServiceImpl implements BookService 

    @Autowired
    private BookMapper bookMapper;

    @Override
    public BookBO getBook(Integer id) 
        Book book = bookMapper.selectByPrimaryKey(id);
        if (book == null) 
            return null;
        
        BookBO bookBO = new BookBO();
        BeanUtils.copyProperties(book, bookBO);
        return bookBO;
    

    @Override
    public Integer saveBook(BookBO bookBO) 
        if (bookBO == null) 
            return null;
        
        Book book = new Book();
        BeanUtils.copyProperties(bookBO, book);
        if (bookBO.getId() == null) 
            return bookMapper.insert(book);
         else 
            return bookMapper.updateByPrimaryKey(book);
        
    

BookBO

service层对象

package org.tg.book.service.bo;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BookBO implements Serializable 
    private Integer id;
    private String bookName;
    private String bookNo;
    private String bookAuthor;
    private Integer bookType;
    private String bookDesc;
    private String publisher;
    private Date publishDate;

2.3 dal层

BookMapper

定义数据访问接口:

package org.tg.book.dal.mapper;

import org.tg.book.dal.po.Book;

public interface BookMapper 

    Book selectByPrimaryKey(Integer id);

    Integer insert(Book book);

    Integer updateByPrimaryKey(Book book);

BookMapperImpl

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

package org.tg.book.dal.mapper.impl;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;
import org.tg.book.dal.mapper.BookMapper;
import org.tg.book.dal.po.Book;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Repository
public class BookMapperImpl implements BookMapper 

    private final List<Book> bookList = new ArrayList<>();

    @Override
    public Book selectByPrimaryKey(Integer id) 
        return bookList.stream().filter(p -> p.getId().equals(id)).findFirst().orElse(null);
    

    @Override
    public Integer insert(Book book) 
        book.setId(bookList.size() + 1);
        bookList.add(book);
        return book.getId();
    

    @Override
    public Integer updateByPrimaryKey(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();
        
        return null;
    

Book

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

package org.tg.book.dal.po;

import lombok.Data;

import java.util.Date;

@Data
public class Book 
    private Integer id;
    private String bookName;
    private String bookNo;
    private String bookAuthor;
    private Integer bookType;
    private String bookDesc;
    private String publisher;
    private Date 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&#x

新书《nginx实战:基于lua语言的配置开发与架构详解》开始发售

新书《Nginx实战:基于Lua语言的配置、开发与架构详解》开始发售https://item.jd.com/12487157.html#none  《Nginx实战:基于Lua语言的配置、开发与架构详解》主要讲解了Nginx在反向代理和应用开发中的作用,阅读本书可以了解Nginx在互... 查看详情

nodejs开发实战详解

博客论坛或微博适合吗不适合PHP在这个方面已经很成熟解决的是长连接和多请求引发的成本问题实时在线game实时在线聊天室 实时消息推送 SNS实时交流实时监控系统 股票系统运行状态等《程序员如何说服老板采用Nodej... 查看详情

新书上市:androidapp开发实战从规划到上线全程详解

京东:https://item.jd.com/28649240718.html当当:http://product.dangdang.com/25282490.html 亚马逊: 查看详情

新书上市:androidapp开发实战从规划到上线全程详解

京东:https://item.jd.com/28649240718.html当当:http://product.dangdang.com/25282490.html 亚马逊: 查看详情

as开发实战第二章学习笔记——其他

第二章学习笔记(1.19-1.22)像素Android支持的像素单位主要有px(像素)、in(英寸)、mm(毫米)、pt(磅,1/72英寸)、dp(与设备无关的显示单位)、dip(就是dp)、sp(用于设置字体大小),常用的是px、dp、sp三种。dp与物理设备无... 查看详情

node.js开发实战详解

...;讲解Node.js应用开发。“Web开发典藏大系”之《Node.js开发实战详解》3月下旬正式上市,敬请关注。链接:http://www.tup.com.cn/book/showbook.asp?cpbh=056313-01 查看详情

android安卓书籍推荐《android驱动开发与移植实战详解》下载

...很高的智能手机操作系统。  《Android驱动开发与移植实战详解》分为18章,依次讲解了Android系统的基本知识,Linux内核的基本知识,分析了Android系统的源码,深入分析HAL层的基本知识,GoldFish下的驱动、MSM内核和驱动、OMAP内 查看详情

javacv开发详解之rtsp推流实战:rtsp转推到rtsp(代码片段)

javacv实战专栏目录:JavaCV实战专栏文章目录(JavaCV速查手册)前言在上一篇《JavaCV开发详解之rtp推流实战:rtsp转推到rtp》我们主要进行rtp推流,本章将进行rtsp推流。还是那句话,Nocodenobb!废话少谈,直接上代码。参考资料ffmpeg... 查看详情

最新flutter完整开发实战详解,安卓程序员快存下吧,很难找全的~

...验的人才。但是光有技术有什么用呢?我们需要的是实战。所以我给大家分享一份《Flutter完整开发实战详解 查看详情

flutter完整开发实战详解自定义布局,移动开发框架2019(代码片段)

///计算返回第一个child的基线,常用于child的位置顺序有关doubledefaultComputeDistanceToFirstActualBaseline(TextBaselinebaseline)///计算返回所有child中最小的基线,常用于child的位置顺序无关doubledefaultComputeDistanceToHighestActualBaseline(TextBasel... 查看详情

鸿蒙开发|呼吸训练实战项目

文章目录鸿蒙开发|呼吸训练实战项目(二)实现训练页面与主页面之间相互跳转运行效果实现思路代码详解验证应用和每个页面的生命周期时间运行效果在主界面中显示logo和两个选择器实现思路代码详解鸿蒙开发|呼吸... 查看详情

快速入门|flutter完整开发实战详解谷歌架构师独家分享

前言这几年在大前端的开发领域,选择跨端方案的公司和部门越来越多,一方面是跨平台的前端框架越来越成熟,另一方面也是因原生开发者正逐年减少。所以,在当下掌握一门跨平台的技术栈还是很有必要的... 查看详情

聚焦天狗

以下所有照片版权均归周晗所有,转载请附。侵权必究。Copyright?2018Douglas 查看详情

androidframeworkinputmanager模拟触摸事件inject详解实战开发-视频课程笔记(代码片段)

我相信触摸事件注入,即模拟触摸事件绝对是最受我们android开发者喜欢的,想想你程序可以自由的模拟出你的点击事件了,你就完全可以不用在手动了,可以帮你干各种各样的事。但是同学们可能对模拟触摸事件... 查看详情

javacv开发详解之rtp推流实战:rtsp转推到rtp(代码片段)

javacv实战专栏目录:JavaCV实战专栏文章目录(JavaCV速查手册)前言在《JavaCV实战教程》前面的文章中,我们主要进行rtmp推流和录制mp4、flv和hls,博主原本觉得已经照顾到大部分场景了,直到最近群里有小伙伴们问怎么推rtp,这... 查看详情

androidflutter完整开发实战详解,一文搞懂flutter框架

...效学习,这里我就来给大家分享一份《Flutter完整开发实战详解》资料,有需要完整版的同学可以下滑至文末【免费获取】哦~一、Dart语言和Flutter基础⼆、快速开发实战篇1、基础控件2、数据模块3、其他功能三、打包与填... 查看详情

最新flutter完整开发实战详解,安卓程序员快存下吧,很难找全的~

...验的人才。但是光有技术有什么用呢?我们需要的是实战。所以我给大家分享一份《Flutter完整开发实战详解》资料,帮助大家更好的去开发Flutter,需要完整版的可以点击文末卡片【免费获取】哦~一、Dart语言和Flutter... 查看详情

rsyslog详解实战和避坑

目标是要把线上环境的debug日志及集中化收集起来,一方面是方便开发调试;一方面是避免直接到线上环境查看,存在安全隐患。常用可选方案:rsyslog发送端+rsyslog接收端:直接存在接收端的本地硬盘rsyslog发送端+logstash接收端+&l... 查看详情