Java开发 - 问君能有几多愁,Spring Boot瞅一瞅。

news2024/11/28 14:47:31

前言

首先在这里恭祝大家新年快乐,兔年大吉。本来是想在年前发布这篇博文的,奈何过年期间走街串巷,实在无心学术,所以不得不放在近日写下这篇Spring Boot的博文。在还没开始写之前,我已经预见到,这恐怕将是我从业以来写过最长的博文了。前一篇Java开发 - Mybatis框架初体验2.7w的字数我觉得已经是最长了,但在整理Spring Boot的知识点时我才知道,是我小瞧了它,所以在这里先给大家做个预告,有个心理准备。为什么不拆分成几段来写呢?技术这东西,最好是一蹴而就,一次学完,好有个整体的框架感。敢拆分成三五篇,要是好几天才能看完,前面的东西也就忘了,来来回回看又浪费时间,不如花几个小时,一次学完来的痛快。再根据目录去针对性的学习不熟悉的内容,此为最佳,这也是博主平时学习的一个方式,个人觉得很高效。废话不多说,接下来,就跟着博主一起来学习Spring Boot吧。

Spring Boot是什么

什么是Spring Boot

对于这个问题,不用多说,先上百度词条:Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

Spring Boot的特点

SpringBoot基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。

SpringBoot所具备的特征有:

(1)可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;

(2)内嵌Tomcat或Jetty等Servlet容器;

(3)提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;

(4)尽可能自动配置Spring容器;

(5)提供准备好的特性,如指标、健康检查和外部化配置;

(6)绝对没有代码生成,不需要XML配置。

Spring Boot工程

创建Spring Boot工程

创建Spring Boot项目时,我们选择Spring Initializr,注意Group和Artifact,这两个值直接决定了Package name,有些特殊字符在这里体现不一,比如‘-’将会直接消失。

我们看到Java处也圈了起来,这个学Java的同学都知道,高版本可能会存在一些意想不到的问题,我们还是以项目的稳定性为主,所以选择低版本使用。

下一步之后会看到此界面:

Spring Boot的版本我们先不管,在项目内进行统一修改,因为官方更新的会频率比较高,所以我们一般不会选择太高的版本,下面的依赖,我们在学习的时候先不勾选,在项目中统一手动添加。

在此图中,pom.xml文件内修改Spring Boot的版本为2.5.9,这个版本博主目前使用中没遇到问题。修改完后记得右上角的刷新按钮刷新一下,这个在SSM框架中我们已经比较熟悉了。

前面我们看到Spring Boot项目创建过程中是可以直接勾选依赖的,这就很方便了,这在我们先前的SSM框架的项目中是没有的,在大家熟悉之后,可以根据自己的需要在这里直接勾选需要的依赖。

Spring Boot工程结构

Spring Boot创建时我们看到有Maven字样,所以其本质上也是一个Maven工程,所以和我们的SSM框架的项目区别并不大。展开文件目录,发现还是有些不一样的。

 可以看到src/main/java和src/test/java下默认都已存在指定的package,且此package是这个工程中的执行组件扫描的根包,所以,后续创建的所有的类和包都需要位于此包中。

在src/main/java下的此包中,默认已经创建了启动类,启动类默认添加了@SpringBootApplication注解,此注解的元注解中包含@SpringBootConfiguration,而@SpringBootConfiguration的元注解中包含@Configuration,所以,启动类本身也是配置类!将允许将@Bean方法写在此类中,或者某些与配置相关的注解也可以添加在此类上!但为了项目整体的整洁和统一管理,我们并不会将大量的代码塞入启动类,这都是后话了。

在src/test/java下的此包中,默认存在测试类,其类名是在启动类的基础上命名的,我们就认为这是给启动类创建的测试类,在后续创建测试类时也要遵守这一规则,事不大,但有助于帮助我们区分类名。测试类一般不会是public权限,这也是创建时默认的,所以其内的方法也遵从这一原则。此测试类上添加了@SpringBootTest注解,其元注解中包含@ExtendWith(SpringExtension.class),与使用spring-test时的@SpringJUnitTest注解中的元注解相同,所以,@SpringBootTest注解也会使得当前测试类在执行测试方法之前是加载了Spring环境的,在实际编写测试时,可以通过自动装配得到任何已存在于Spring容器中的对象,在各测试方法中只需要关注被测试的目标即可。这一点就很好,帮我们整合了Spring框架,免去了手动添加的麻烦,甚至还有可能导致错误。

spring-boot-starterspring-boot-starter-test是Spring Boot的基础依赖项和测试依赖项。

src\main\resources文件夹下默认就已经存在application.properties文件,在SSM中我们用于编写配置,在Spring Boot项目中,则会自动读取此文件,这主要是依赖了@PropertySource注解,相较于SSM框架要手动获取方便了很多。

还有一个比较重要的东西就是:创建项目时勾选依赖项选中了Web项,在src\main\resources下默认就已经创建了statictemplates文件夹,如果没有勾选Web则没有这2个文件夹,则需要自行创建。但在我们目前的市场下,前后端分离是主流,所以这个可有可无,可能有些地方会使用这个功能来创建一些错误页面,比如我们常见的404。

其实到这里为止,Spring Boot项目可以说已经给大家介绍完了,但Spring Boot的应用还远没有结束,接下来就是Spring Boot中常用的一些依赖和配置讲解。前面的比较简单,后面的大家就要打起精神认真看了。

Spring Boot怎么连接数据库

连接数据库我们在SSM框架中学过,是通过Mybatis框架来实现了,所以在这里,我们要引入Mybatis框架,是不是很神奇?SSM框架和Spring Boot不分家。

要引入Mybatis框架,我们需要添加两个依赖项:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

这里有两个问题需要注意:

  • Spring Boot下依赖项的版本不需要显示指定,这是因为父项目将对这些依赖项的版本进行统一管理,若有必要,可通过上面的方式<version>来指定版本号
  • 如果你注意到<scope>中的runtime,那我正好说一下,它的意思是此依赖项是在项目运行过程中需要使用的,在编译期并不参与编译。

到这里 ,有些同学比较着急,会去运行项目,但此时,项目还是不能够跑起来的,虽然数据库依赖已经添加完毕,但是数据库的信息还没有配置,将无法连接到数据库。这些配置信息我们先前都是写在application.properties中的,不要急,我们继续往下看。

数据库配置信息和原来相比很相似,但却有明显的区别,Spring Boot对属性名有严格的要求,符合要求,则自动进行读取,并创建数据源对象,否则无法读取,我们下面来看看怎么写吧:

# 连接数据库的URL
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
# 登录数据库的用户名
spring.datasource.username=root
# 登录数据库的密码
spring.datasource.password=123456

此处密码,大家要写成自己的数据库密码,否则将无法连接数据库。这里还有个坑,虽然配置写完了,但是无法验证配置的正确与否,因为Spring Boot只是加载配置,并不会实际连接数据库,所以,即使配置错误,我们也无从得知。

为了验证配置的正确性,这里我们将采用测试类来进行测试,我们就在系统默认的测试类中进行测试,我们在Mybatis中创建的数据库为mybatis,所以这里就以此数据库为例,查看完整代码:

package com.codingfire.springboot;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;

@SpringBootTest
class SpringbootApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void testGetConnection() throws Exception {
        System.out.println(dataSource.getConnection());
    }
}

运行测试方法后,查看输出如下: 

很好,控制台已经输出了dataSource连接对象,说明数据库连接成功,否则,连接数据库失败。这里测试时,要格外注意几点:

  • 数据库一定是已经存在的数据库
  • 数据库密码一定不要写错,要写成自己的数据库密码,不要写成博主的
  • 数据库服务一定要启动

 以上缺一不可,否则将无法测试成功。

Profile配置文件

在移动端我们对这种配置叫做多target环境,是为了区分测试环境,开发环境,生产环境三个环境,因为不同的环境所对应的域名是不一样的,为了方便测试,而不是每次都手动修改,这种配置就出现了。

在Java中也存在这样的配置问题,比如数据库密码,在Spring Boot下,对Profile配置也是支持的。我们可以通过在resources下创建多个配置文件的方式来进行配置,创建的配置文件大概命名如下:

application-test.properties 测试还款

application-dev.properties 开发环境

application-prod.properties 生产环境

创建完成后,Spring Boot并不会主动使用这些文件,还需要我们来手动在application.properties文件中指定需要运行的环境配置,通过如下方式:

# 激活Profile配置
spring.profiles.active=dev

此时 application.properties文件中只有这一句配置即可,原来的url等信息都需要放在dev所对应的配置文件中。接着我们重新运行上面的测试代码,发现运行成功,依然可以获取到配置中所包含的数据库信息。

如需使用test配置,则改为test,抑或是prod。这在实际开发中还是很有用的,早年间大家对这种配置使用的还不多,现在如果谁项目中还不是这么使用的,那一定会遭到嘲笑了,话不多说,我们进入下一个内容。

YAML配置

yaml配置听起来费解,其实就是将后缀名.properties更换为.yml,这种方式目前使用的也比较多。YAML配置原来并不是Spring系列框架内置的配置语法,如果在项目中需要使用这种语法进行配置,解析这类文件需要添加相关依赖,而Spring Boot默认已添加。

.properties配置中使用的是以.隔开的名字,如:

spring.profiles.active=dev

各部分用.连接,在YAML配置下,则有很大的区别,以换行+缩紧2个空格的形式,换行前使用冒号表示值类型,属性名和值之间使用冒号+一个空格的形式进行连接,两者缺一不可,否则都会导致报错。

YAML配置还有个有点,在.properties文件中,我们会看到重复的前缀,在YAML中则不会出现重复的前缀,否则会报错。

说了这么多,可能代价并不明白实际要怎么写,下面带大家一起来写一下,然后再结合文字看一遍,就懂了,这种方式我们应该都在其他地方见过,并不会十分陌生。

properties文件中我们这么写:

spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root

yml文件中我们这么写:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root

怎么知道自己写的对不对呢?其实很好分辨,如果写的不对,颜色就不会变,看下图:

用户名和密码颜色不一致,那是因为密码没有在冒号后面加空格,这点格外注意。

还有个小快捷方式,缩进两个空格我们可以使用tab键,IDE会将其转换为两个字符。虽然YAML配置和properties写起来不太一样,但并不影响项目本身,在Spring Boot眼里,他们最终的展现方式并无任何不同。

数据库连接池

在Mybatis中,我们使用的数据库连接池如下:

<!--数据库连接池依赖-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.8.0</version>
</dependency>

在这里,我们要说的不是这个,而是阿里巴巴团队研发的Durid,在Spring Boot项目中,如果需要明确指定使用此连接池,需要在项目中添加此连接池依赖: 

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
</dependency>

添加玩依赖之后还不行,我们还需要在指定spring.datasource.type属性,在yml文件中表示如下:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSourc

常用的配合框架

开发中,我们经常会使用一些配合的工具框架来提高我们的开发效率,在Mybatis中也提到过一些,比如简化setter&getter方法的框架,检查请求参数格式的框架,现在,他们来了。

Lombok

POJO在前文中已经给大家做过说明,它包含了实体类,VO,DTO等,说白了,他们都是数据模型,所以他们也都有相同的数据格式,比如:

  • setter&getter方法
  • 属性私有化
  • 实现Serializable接口
  • 重写hashCode方法,equals方法,一般我们还会重toString方法

由于这些操作非常固定,却占用了较大篇幅的代码,以至于要修改,删除或新增一个属性时比较麻烦,所以这时候Lombok就应运而生了。

Lombok框架可以极大的简化这些操作,可以通过添加注解的方式在编译期生成Setters & Getters、equals()hashCode()toString(),甚至生成构造方法等,所以,使用了此框架,开发人员只需要声明属性并添加注解即可,即使如此,Serializable接口也是需要我们自己实现的。

要添加Lombok框架有两种方式,一种是在创建项目时直接勾选:

另一种是通过手动在pom文件中添加依赖的方式,添加格式如下:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

完成后,在各POJO类上则不需要再额外添加Setters & Getters、equals()hashCode()toString()这些方法,只需在类上添加@Data注解即可。这里有个小问题,为了避免IntelliJ IDEA判断失误而提示了警告和错误,推荐安装Lombok插件,此插件是否安装,都不影响代码的编写和运行 ,安装可查看下图:

我们以用户信息的VO类为例:

package com.codingfire.springboot.pojo.vo;

import lombok.Data;

import java.io.Serializable;

@Data
public class UserInfoVO implements Serializable {
    private String username;
    private String password;
}

 这就是一个使用Lombok的类,大家可参考使用。

Slf4j

在移动端开发的时候,我们输出数据常用NSLog(),Java里面输出用System.out.println(),但在实际开发中,这两种系统级输出我们都是不用的,这是因为,无论是在开发环境,还是测试环境,还是生产环境中,这些输出语句都将输出相关信息,而删除或添加这些输出语句的操作成本比较高,操作可行性低。虽然这么说,但如果你要是用,还是可以使用的,只是一般我们不建议,甚至有些公司直接禁止这么用。

所以,大家应该猜到Slf4j的作用是什么了,没错,这是一个日志输出框架,但此框架并不需要单独额外添加,当添加了Lombok框架后,Slf4j框架就可以使用了,需要使用时,在类上添加@Slf4j注解,然后,在类的任意未知,均可使用名为log的变量,且调用其方法来输出日志。

既然控制了日志的输出,那么日志一定是有优先级的,没错,Slf4j提供了这一能力,从低到高有仙界分别为:

  • trace:跟踪信息
  • debug:调试信息
  • info:一般信息,通常不涉及关键流程和敏感数据
  • warn:警告信息,通常代码可以运行,但不够完美,或不规范
  • error:错误信息

根据开发环境,我们也可以在配置文件中配置不同环境日志的级别,这样就可以控制日志的输出,设置方法如下:

logging.level.cn.codingfire.springboot.xxxxx: info

xxx部分可具体到对应的类名或者包名。

设置完成后,仅显示设置级别高于其级别的日志,此例中设置显示级别为info,则只显示infowarnerror的日志信息debug和trace级别的日志则不会显示!

关键数据和敏感数据我们通常推荐使用trace或debug级别这样可以方便在开发时直观的看到输出的日志信息,帮助开发者快速定位问题,减少项目交付过程中出现的问题。交付后,这些信息也还是需要的,我们推荐info级别,这样,一些关键性数据的日志还是可以输出的。

忘记说了,日志的默认显示级别为info,即使不设置,info及以上优先级的日志也会自动输出。

在开发实践中,属性名称通常配置为logging.level.项目根包,例如:

logging.level.cn.codingfire.springboot: trace

各级别对应的方法如下:

public void trace(String format, Object... arguments);
public void debug(String format, Object... arguments);
public void info(String format, Object... arguments);
public void warn(String format, Object... arguments);
public void error(String format, Object... arguments);

以上方法第1个参数是将要输出的字符串的模式(模版),在此字符串中,如果需要包含某个变量值,则使用{}表示,如果有多个变量值,均是如此,然后,再通过第2个参数(是可变参数)依次表示各{}对应的值,例如:

log.debug("用户名:{},用户昵称:{}", username, nickname);

它的好处是可以避免频繁的拼接字符串,日志框架也会将第一个参数缓存,以提高后续每一次的执行效率。

但是怎么添加日志可能很多同学不清楚,这里做简要说明:

  • 程序可能会抛出异常的时候
  • 程序执行一些增删改查的时候
  • 一些关键的核心数据改变的时候,做前后对比
  • 判断代码执行到的位置的时候

在Java中还有其他的一些日志框架,如log4j、logback等,但由于其实现功能并不统一,所以才有了Slf4j的这种标准,Slf4j提供了对主流日志框架的兼容,在Spring Boot工程中,spring-boot-starter就已经依赖了spring-boot-starter-logging,而在此依赖下,我们通常可以看到Slf4j,还有一些具体的日志框架,以及Slf4j对具体日志框架的兼容,这也是Spring Boot框架很神奇的一点,包罗万象!

Validation

Validation和Slf4j一样,作用于POJO类,不同的是,Slf4j是简化代码,而Validation的作用是请求时检查属性参数是否符合我们的要求。比如为null,字符串长度是否在规定的长度,其他的格式问题。当检查到这种问题时,可直接响应错误,而不需要直接响应到Service。

需要注意的是,这里指检查格式问题,而不验证参数的正确与否,涉及到这方面的一般都是要和数据库中的数据做比较的,这部分都交由Service来完成,在此基础延伸出了不同的业务分层,这些我们稍后会讲。

要添加Validation依赖,需要在pom文件中引入其依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

我们以刚刚创建的UserInfoVO类为例,来讲讲怎么添加@Validation注解,在此之前,我们要先知道Validation框架有哪些注解:

  • @NotEmpty:只能添加在String类型上,不许为空字符串,例如""即视为空字符串
  • @NotBlank:只能添加在String类型上,不允许为空白,例如普通的空格可视为空白,使用TAB键输入的内容也是空白,换行产生的空白区域也是空白
  • @Size:限制长短
  • @Min:限制最小值
  • @Max:限制最大值
  • @Range:可以配置minmax属性,同时限制最小值和最大值
  • @Pattern:只能添加在String类型上,自行指定正则表达式进行验证
  • 其它

以上注解允许叠加使用,其还有一个属性message,用于指定验证失败的提示信息。这里着重说明几个注解:

  • 对于必须提交的属性,会添加@NotNull
  • 对于数值类型的,需要考虑是否添加@Range(则不需要使用@Min@Max),比如密码长度
  • 对于字符串类型,都添加@Pattern注解进行验证,比如手机号,身份证等

使用此注解分两步进行:

第一步,在模型类上添加限制注解:

package com.codingfire.springboot.pojo.vo;

import lombok.Data;
import org.hibernate.validator.constraints.Range;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

@Data
public class UserInfoVO implements Serializable {
    @NotNull(message = "用户名不能为空")
    private String username;
    @NotNull
    @Range(min = 12,max = 30,message = "密码长度不符合要求,请重新输入")
    private String password;
}

第二步,在控制器中,对需要检查格式的参数添加@Valid@Validated注解(这2个注解没有区别),比如:

    @RequestMapping("/add")
    public JsonResult<Void> addNew(@Validated UserInfoVO userInfoVO) {
        adminService.addNew(userInfoVO);
        return JsonResult.ok();
    }

如果是明确的某个参数,则只检查此参数,如果添加注解的是模型对象类,则检查此类中所有属性的格式。

记得曾经刚学Java时看后台的代码,明明没有看到后台的返回,但是我提交的参数就是无法通过,问题就是在这里了,注解在移动端是不存在这种用法的,但确实很好用,极大的简化了开发流程,节省了代码,若是不知道这些东西,直接看Java代码,真是一件让人头疼的事情呢。 

跨域问题

跨域问题专用来解决部署在不同服务器上的项目,比如我们现在的项目都是前后端分离的,很多时候会分别独立部署在不同的服务器上,这时候要想成功访问,就需要先解决跨域问题。

我们基于前面学过Spring MVC框架来解决这个问题,需要先有一个Spring MVC的配置类,这在前面是有使用过的,这个配置类还需要实现WebMvcConfigurer接口,重写addCorsMappings方法,以允许指定条件的跨域访问,我们来看看这个类里写了什么:

package com.codingfire.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

这里博主采用了通配符配置,所以任何类型的请求都可以进入,若是出于安全考虑,可增加前缀来进行使用。最早我记得在客户端里也做过类似的处理针对的也是这几个参数,不过已经记不太清了,大家使用时可按照实际需求再做处理。

这里要说明下,此类需要添加一个依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.0.2</version>
</dependency> 

版本可按照项目需要进行调整。这是Spring MVC的一个依赖,不加此依赖,将会报错。 

参数的请求格式

不学Java不知道,一学吓一跳,原来参数的请求格式有两种,真实大跌眼镜,我后面才知道当年为什么和后端的同事在调试接口的时候怎么也成功不了,我说是json,他说是json字符串,在客户端的角度,json字符串是json格式转义成字符串格式,一般都是带很多斜杠的‘/’,但在后端来说,这就是字符串,但我看,却不是json的,不过要说是json格式,好像也有道理,个人理解不同,表达就会不同,导致出现一些问题,耽误了时间。所以前后端都懂是多么的重要。

下面我们来看看这两种格式的差别:

第一种,以&拼接的字符串格式:

//username=codingfire&password=123456
let data = 'username=' + this.userInfo.username + '&password=' + this.userInfo.password;

第二种,我认为是标准的json格式,也是我做移动端多年常用的一种格式:

let data = {
    'username': this.userInfo.username, // 'codingfire'
    'password': this.userInfo.password, // '123456'
};

 使用哪种方法要看服务端的设计:

如果在处理请求的方法中,在参数前添加@RequestBody,则允许使用第2种做法(JSON数据),将不允许使用第1种做法(使用&拼接),否则,两者皆可。所以会发现,第1种做法多出现在前端,第2种做法多出现在客户端,这可能是受限于两种语言的特点,因为还有可能要配合设置header和cookie等参数的原因。

业务分层

在写业务的时候,我们都会对业务进行分层,以解耦整体的功能,达到可能复用的目的,一般分层来说,在客户端,我们常见的就是MVC,MVP,MVVM,具体是什么就不多做解释了,在Java这边并不完全适用,在Java这边,我们经常会大致分围持久层,业务逻辑层,控制器层,可能有一些公司会分的更详细,但大致这三层就够我们用了,下面来说说这几层分别都写些什么,因为他们直接决定了我们项目的结构。

持久层

持久层,我们有时候也叫数据访问层,作用是数据的持久化。为什么要做数据持久化呢?通常认为:正在执行或处理的数据,这些数据都是在内存中的。而内存(RAM)的特征包含“一旦断电,数据将全部丢失”,为了让数据永久保存下来,通常会将数据存储到能够永久存储数据的介质中,通常是计算机的硬盘,硬盘上的数据都是以文件的形式存在的,所以,当需要永久保存数据时,可以将数据存储到文本文件中,或存储到XML文件中,或存储到数据库中,这些保存的做法就是数据持久化,而文本文件、XML文件都不利于实现增删改查中的所有数据访问操作,而数据库是实现增删改查这4种操作都比较便利的一种方式。所以,一般在讨论数据持久化时,默认指的都是使用数据库存储数据。

我们划分这些层就是为了解决不同的问题,持久层解决的就是数据持久化的问题。作用于代码,我们可以认为,持久层就是编写数据库相关的文件或代码。

我们前面使用的是Mybatis框架来处理数据库相关的东西,所以使用Mybatis技术实现持久层编码,有几个注意事项:

  • 使用@MapperScan指定接口所在的Base Package,配置SQL语句所在的XML文件的位置
  • 在接口中添加必要的抽象方法,在XML文件配置抽象方法对应的映射SQL

关于具体的操作,此处不再详细写明,忘记的可以前往Mybatis框架初体验在此细品,这里的目的是让大家了解代码的分层和项目的结构。

业务逻辑层

这里要明确一点,业务逻辑层一定是被Controller直接调用的层,因为我们默认Controller不能直接调用持久层,这将会导致一些可怕的后果。

业务逻辑层的目的是为了保证数据的完整性和安全性,让代码按照我们设定的方式执行并产生一些列的变化。

业务逻辑层的代码由接口和实现类组件沟通,其中,接口是必须存在的。所以很多时候推荐采用基于接口的编程方式,有些功能会使用基于接口的代理模式,例如Spring JDBC框架在处理事务时。但接口本身不处理编程逻辑,而是由接口的实现类完成业务逻辑的处理。如果整个过程发生问题,则通过抛出异常的方式来解决。

关于异常,通常我们会自定义异常,并统一处理,自定义异常通常是RuntimeException的子类,这是因为可以不用显示的抛出或捕获,因为业务逻辑层的异常永远是抛出的,而控制器层会调用业务逻辑层,在控制器层的Controller中其实也是永远抛出异常的,这些异常会通过Spring MVC统一处理异常的机制进行处理,关于异常的整个过程都是固定流程,所以,没有必要显式抛出或捕获。而前面说过的Spring JDBC框架在处理事务时,默认只对RuntimeException的子孙类进行识别并处理。这就导致我们自定义异常通常是RuntimeException的子类,可以避免漏下异常。

实际编码中,我们需要先规划异常,比如创建一个统一的异常类,并在类中捕获我们提前规定好的要抛出的异常做统一处理,免去了在不同的类中单独处理异常的情况,此方式我记得在前文中也有说明,大家可回过头去再细看。

控制器层

控制器层很好理解,我们认为控制器里除了接口,不会再有其他的东西了,控制器一般以controller结尾,代表是某某控制器。

Spring MVC是用于处理控制器层开发的,在使用Spring Boot时,在pom.xml中添加spring-boot-starter-web即可整合Spring MVC框架及相关的常用依赖项(包含jackson-databind),可以将已存在的spring-boot-starter直接改为spring-boot-starter-web,因为在spring-boot-starter-web中已经包含了spring-boot-starter

使用控制器层,我们需要先在跟包下创建controller子包,并在此包下创建具体的controller,此类应该添加@RestController@RequestMapping(value = "/xxxxxx", produces = "application/json; charset=utf-8")注解,看如下代码格式:

package com.codingfire.springboot.controller;

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

@RestController
@RequestMapping(value = "/admins", produces = "application/json; charset=utf-8")
public class AdminController {
    
}

服务器响应数据类型一般都是确定的,这也需要我们自定义数据的格式,但必须是json的,此前学习的Spring MVC框架中我们已经学习过相关的部分,通过自定义数据返回类型的方式返回数据:

package cn.codingfire.springmvc.controller;

import cn.codingfire.springmvc.vo.UserVO;
import cn.codingfire.springmvc.web.JsonResult;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping(value = "/user", produces = "application/json; charset=utf-8")
@ResponseBody
public class UserController {
    @GetMapping("/codingFireInfo.page")
    public JsonResult<UserVO> codingFireInfo() {
        UserVO userVO = new UserVO();
        userVO.setUsername("codingfire");
        userVO.setPassword("123456");

        JsonResult jsonResult = new JsonResult();
        jsonResult.setState(200);
        jsonResult.setMessage("请求成功");
        jsonResult.setData(userVO);
        return jsonResult;
    }


}

记不清的可以前往查看这篇博客:Spring MVC初体验

项目结构

前面已经介绍了项目分层后各层的作用,那么结合这些,我们就知道了项目整体的结构,其实可以参考Spring MVC那篇的项目结构,看下图:

只是实际开发中vo包我们会单独放在pojo包下,大格局基本不变,只是会调整相应的位置和层级。这个要根据各公司情况去处理,毕竟还有为服务的存在,项目的结构可能还会再变,但万变不离其宗,我们只要知道项目该怎么分层,各层所负责的功能,你基本上就掌握了项目的结构,这也是成为架构师的第一步,或者说,连第一步都算不上,只是个开端。 

结语

这篇Spring Boot知识点的博客到这里就算是写完了,我本来预期要写2w+字的,最终还是收了一下,一些代码相关的东西考虑还是放在后面的实战中结合起来说明,但紧写慢写也写了1.5w的字,从过年前开始写,横跨20多天,这篇博客才出来跟大家见面,实在是过年期间有太多的诱惑,容我捂脸笑一会儿。当写到这段话的时候,我内心os:终于写完了。真的,以后再也不想写这么长的博客了,我写着困难,列为看着也愁。但是不写这么长不手把手教,又失去了教程的意义,唉!两难啊。

到这篇为止,前期的知识点部分就先到此为止了,后面是继续知识点结合代码合适增加实战内容我需要再考虑下,希望前面的这几部分入门的东西大家喜欢,觉得不错就点个赞再走吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/335662.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中国社科院与美国杜兰大学金融管理硕士,让我们相遇在春暖花开时

在芸芸众生中&#xff0c;能拥有志同道合的朋友是一件多么幸运的事。人们常说&#xff1a;你是谁&#xff0c;就会遇见谁。走过半生才知道&#xff0c;看似命中注定的遇见谁、发生的事&#xff0c;其实都取决于自己。只有自己足够优秀&#xff0c;才能遇到更优秀的别人。在这个…

IT人的晋升之路——关于人际交往能力的培养

对于咱们的程序员来说&#xff0c;工作往往不是最难的&#xff0c;更难的是人际交往和关系的维护处理。很多时候我们都宁愿加班&#xff0c;也不愿意是社交&#xff0c;认识新的朋友&#xff0c;拓展自己的圈子。对外的感觉就好像我们丧失了人际交往能力&#xff0c;是个呆子&a…

【chatGPT】持续火热一路狂飙,简单了解下TA的功能和示例代码吧

&#x1f389;&#x1f389; 最近chatGPT持续火爆&#xff0c;一路狂飙&#xff0c;对应如何注册和使用的优质文章非常多。 所以&#xff0c;此篇文章除了整理chatGPT文章外&#xff0c;主要是讲解如何获取API Key进行接口的调用&#x1f389;&#x1f389; 目录1、chatGPT解读…

蓝牙单点技术实现路径介绍

本文主要介绍蓝牙设备与手机一对一相连的 蓝牙单点 技术。 准备工作 系统要求&#xff1a;蓝牙使用需要安卓 4.3 以及以上版本&#xff0c;智能生活 App SDK 从安卓 4.4 开始支持。Manifest 权限&#xff1a; <uses-permission android:name"android.permission.ACCE…

Fluent Python 笔记 第 3 章 字典和集合

3.1 泛映射类型 只有可散列 的数据类型才能用作这些映射里的键 字典构造方法&#xff1a; >>> a dict(one1, two2, three3) >>> b {one: 1, two: 2, three: 3} >>> c dict(zip([one, two, three], [1, 2, 3])) >>> d dict([(two, 2…

5. Spring 事务

文章目录1. Spring 事务简介2. Spring 事务角色3. Spring 事务属性3.1 事务配置3.2 案例&#xff1a;转账业务追加日志3.3 事务传播行为1. Spring 事务简介 Spring 事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功、同失败。 数据层有事务我们可以理解&am…

多传感器融合定位十三-基于图优化的建图方法其二

多传感器融合定位十二-基于图优化的建图方法其二3.4 预积分方差计算3.4.1 核心思路3.4.2 连续时间下的微分方程3.4.3 离散时间下的传递方程3.5 预积分更新4. 典型方案介绍4.1 LIO-SAM介绍5. 融合编码器的优化方案5.1 整体思路介绍5.2 预积分模型设计Reference: 深蓝学院-多传感…

Vue3 - 自定义指令封装

Vue3 - 自定义指令封装一. 自定义指令封装1.1 全局/局部注册自定义聚焦指令1.2 自定义指令相关参数1.3 自定义指令参数传递二. 总结一. 自定义指令封装 vue中有很多内置的指令&#xff0c;我们一般在开发中也经常用到&#xff0c;比如v-if&#xff0c;v-for等等。那么本篇文章…

Vue极简使用

Vue安装Vue模板语法安装Vue 安装nodejs 这里我安装的是14.5.4版本 https://nodejs.org/download/release/v14.15.4/解压后配置一下环境变量就行 安装cnpm镜像 (这个安装的版本可能过高&#xff0c;后面安装Vue可能出问题) npm install -g cnpm --registryhttps://registry…

二十二、Gtk4-ListView

GTK 4添加了新的列表对象GtkListView、GtkGridView和GtkColumnView。这个新特性在Gtk API参考—列表小构件概述中有描述。 GTK 4还有其他实现列表的方法。它们是GtkListBox和GtkTreeView&#xff0c;它们是从GTK 3接管的。在Gtk开发博客中有一篇关于Matthias Clasen所写的列表…

vscode执行Python输出exited with code=9009 in 0.655 seconds

vscode执行Python输出exited with code9009 in 0.655 seconds 想用vscode写个脚本&#xff0c;用自己电脑配置了下vscode的python环境&#xff0c;结果点击右上角三角图标运行时却只会输出exited with code9009 in 0.655 seconds 这就不太理解了&#xff0c;我在公司时是能正…

linux性能分析 性能之巅学习笔记和内容摘录

本文只是在阅读《性能之巅》的过程中&#xff0c;对一些觉得有用的地方进行的总结和摘录&#xff0c;并附加一些方便理解的材料&#xff0c;完整内容还请阅读Gregg的大作 概念和方法 性能分析领域一词的全栈代表了整个操作系统的软硬件在内的所有事物 软件生命周期和性能规划…

LabWindows CVI 2017开发笔记--串口API

参考资料&#xff1a;https://download.csdn.net/download/Stark_/87424565?spm1001.2014.3001.5501 转载请注明出处&#xff1a;https://blog.csdn.net/Stark_/article/details/128966962?spm1001.2014.3001.5501 打开串口OpenComConfig OpenComConfig 打开一个串行并进行…

HTML-CSS-js教程

HTML 双标签<html> </html> 单标签<img> html5的DOCTYPE声明 <!DOCTYPE html>html的基本骨架 <!DOCTYPE html> <html> </html>head标签 用于定义文档的头部。文档的头部包含了各种属性和信息&#xff0c;包括文档的标题&#…

【成为架构师课程系列】架构设计中的核心思维方法

架构设计中的核心思维方法 目录 前言 #一、抽象思维 #二、分层思维 #三、分治思维 #四、演化思维 #五、如何培养架构设计思维

leaflet 加载WKT数据(示例代码050)

第050个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中加载WKT文件,将图形显示在地图上。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,请加载其他来练习 文章目录 示例效果配置方式示例源代码(共67行…

中国特色地流程管理系统,天翎让流程审批更简单

编者按&#xff1a;本文分析了国内企业在采购流程管理系统常遇到的一些难点&#xff0c;并从适应中国式流程管理模式的特点出发&#xff0c;介绍了符合中国特色的流程审批管理系统——天翎流程管理系统。关键词&#xff1a;可视化开发&#xff0c;拖拽建模&#xff0c;审批控制…

威联通ContainerStation部署Oracle11g

文章目录前言部署过程详解使用docker-compose文件创建容器临时开启NAS的SSH远程访问通过SSH客户端远程连接NAS进入容器创建用户拷贝容器中的数据库相关文件至宿主机在ContainerStation中修改docker-compose文件总结前言 ContainerStation本质上是对Docker可视化的一款软件&…

聊聊分布式锁——Redis和Redisson的方式

一、什么是分布式锁 分布式~~锁&#xff0c;要这么念&#xff0c;首先得是『分布式』&#xff0c;然后才是『锁』 分布式&#xff1a;这里的分布式指的是分布式系统&#xff0c;涉及到好多技术和理论&#xff0c;包括CAP 理论、分布式存储、分布式事务、分布式锁... 分布式系统…

Android开发

前言&#xff1a;因为这学期选了手机APP开发这门课&#xff0c;所以还是写个博客记录一下学习过程&#xff0c;包括安卓开发和ios开发。用到的资料包括课程PPT&#xff0c;和我在网上找的一些视频和资料。 1.Andriod入门 XML&#xff1a;描绘应用界面 &#xff08;决定APP长什…