当前位置:   article > 正文

SpringBoot_distributionmanagement配置

distributionmanagement配置

Maven高级

依赖管理

我们现在已经能把项目拆分成一个个独立的模块,当在其他项目中想要使用独立出来的这些模块,只需要在其pom.xml使用<dependency>标签来进行jar包的引入即可。

<dependency>其实就是依赖,关于依赖管理里面都涉及哪些内容,我们就一个个来学习下:

  • 依赖传递

  • 可选依赖

  • 排除依赖

我们先来说说什么是依赖:

依赖指当前项目运行所需的jar,一个项目可以设置多个依赖。

格式为:

<!--设置当前项目所依赖的所有jar-->
<dependencies>
    <!--设置具体的依赖-->
    <dependency>
        <!--依赖所属群组id-->
        <groupId>org.springframework</groupId>
        <!--依赖所属项目id-->
        <artifactId>spring-webmvc</artifactId>
        <!--依赖版本号-->
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>

2.1 依赖传递与冲突问题

依赖是具有传递性的:

说明:A代表自己的项目;B,C,D,E,F,G代表的是项目所依赖的jar包;D1和D2 E1和E2代表是相同jar包的不同版本

(1) A依赖了B和C,B和C有分别依赖了其他jar包,所以在A项目中就可以使用上面所有jar包,这就是所说的依赖传递

(2) 依赖传递有直接依赖和间接依赖

  • 相对于A来说,A直接依赖B和C,间接依赖了D1,E1,G,F,D2和E2

  • 相对于B来说,B直接依赖了D1和E1,间接依赖了G

  • 直接依赖和间接依赖是一个相对的概念

(3)因为有依赖传递的存在,就会导致jar包在依赖的过程中出现冲突问题,具体什么是冲突?Maven是如何解决冲突的?

解决:这里所说的依赖冲突是指项目依赖的某一个jar包,有多个不同的版本,因而造成类包版本冲突。

  • 特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的。

情况二: 路径优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高

  • A通过B间接依赖到E1

  • A通过C间接依赖到E2

  • A就会间接依赖到E1和E2,Maven会按照层级来选择,E1是2度,E2是3度,所以最终会选择E1

情况三: 声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的

  • A通过B间接依赖到D1

  • A通过C间接依赖到D2

  • D1和D2都是两度,这个时候就不能按照层级来选择,需要按照声明来,谁先声明用谁,也就是说B在C之前声明,这个时候使用的是D1,反之则为D2

可选依赖

可选依赖指对外隐藏当前所依赖的资源---不透明

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>maven_03_pojo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递-->
    <optional>true</optional>
</dependency>

排除依赖

  • 排除依赖指主动断开依赖的资源,被排除的资源无需指定版本---不需要

<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>maven_04_dao</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--排除依赖是隐藏当前资源对应的依赖关系-->
    <exclusions>
        <exclusion>
            <groupId>com.itheima</groupId>
            <artifactId>maven_03_pojo</artifactId>
        </exclusion>
    </exclusions>
</dependency>

这样操作后,BookServiceImpl中的Book类一样也会报错。

聚合和继承

聚合

  • 所谓聚合:将多个模块组织成一个整体,同时进行项目构建的过程称为聚合

  • 聚合工程:通常是一个不具有业务功能的"空"工程(有且仅有一个pom文件)

  • 作用:使用聚合工程可以将多个工程编组,通过对聚合工程进行构建,实现对所包含的模块进行同步构建

    • 当工程中某个模块发生更新(变更)时,必须保障工程中与已更新模块关联的模块同步更新,此时可以使用聚合工程来解决批量模块同步构建的问题。

步骤2:将项目的打包方式改为pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <groupId>com.itheima</groupId>
    <artifactId>maven_01_parent</artifactId>
    <version>1.0-RELEASE</version>
    <packaging>pom</packaging>
    
</project>

说明:项目的打包方式,我们接触到的有三种,分别是

  • jar:默认情况,说明该项目为java项目

  • war:说明该项目为web项目

  • pom:说明该项目为聚合或继承(后面会讲)项目

步骤3:pom.xml添加所要管理的项目

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <groupId>com.itheima</groupId>
    <artifactId>maven_01_parent</artifactId>
    <version>1.0-RELEASE</version>
    <packaging>pom</packaging>
    
    <!--设置管理的模块名称-->
    <modules>
        <module>../maven_02_ssm</module>
        <module>../maven_03_pojo</module>
        <module>../maven_04_dao</module>
    </modules>
</project>

步骤4:使用聚合统一管理项目

说明:聚合工程管理的项目在进行运行的时候,会按照项目与项目之间的依赖关系来自动决定执行的顺序和配置的顺序无关。

聚合的知识我们就讲解完了,最后总结一句话就是,聚合工程主要是用来管理项目

继承

  • 所谓继承:描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。

  • 作用:

    • 简化配置

    • 减少版本冲突

接下来,我们到程序中去看看继承该如何实现?

步骤1:创建一个空的Maven项目并将其打包方式设置为pom

因为这一步和前面maven创建聚合工程的方式是一摸一样,所以我们可以单独创建一个新的工程,也可以直接和聚合公用一个工程。实际开发中,聚合和继承一般也都放在同一个项目中,但是这两个的功能是不一样的。

步骤2:在子项目中设置其父工程

分别在maven_02_ssm,maven_03_pojo,maven_04_dao的pom.xml中添加其父项目为maven_01_parent

<!--配置当前工程继承自parent工程-->
<parent>
    <groupId>com.itheima</groupId>
    <artifactId>maven_01_parent</artifactId>
    <version>1.0-RELEASE</version>
    <!--设置父项目pom.xml位置路径-->
    <relativePath>../maven_01_parent/pom.xml</relativePath>
</parent>

步骤3:优化子项目共有依赖导入问题

  1. 将子项目共同使用的jar包都抽取出来,维护在父项目的pom.xml中

  2. 删除子项目中已经被抽取到父项目的pom.xml中的jar包,如在maven_02_ssm的pom.xml中将已经出现在父项目的jar包删除掉

  3. maven_04_dao项目的pom.xml中的所有依赖删除,然后添加上maven_01_parent的父项目坐标

步骤4:优化子项目依赖版本问题

  1. 在父工程mavne_01_parent的pom.xml来定义依赖管理

  2. 将maven_02_ssm的pom.xml中的junit依赖删除掉,刷新Maven

  3. 在maven_02_ssm的pom.xml添加junit的依赖

  4. 在maven_04_dao的pom.xml添加junit的依赖

小结

继承的实现步骤:

  • 创建Maven模块,设置打包类型为pom

    <packaging>pom</packaging>
  • 在父工程的pom文件中配置依赖关系(子工程将沿用父工程中的依赖关系),一般只抽取子项目中公有的jar包

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        ...
    </dependencies>
  • 在父工程中配置子工程中可选的依赖关系

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
        </dependencies>
        ...
    </dependencyManagement>
  • 在子工程中配置当前工程所继承的父工程

    <!--定义该工程的父工程-->
    <parent>
        <groupId>com.itheima</groupId>
        <artifactId>maven_01_parent</artifactId>
        <version>1.0-RELEASE</version>
        <!--填写父工程的pom文件,可以不写-->
        <relativePath>../maven_01_parent/pom.xml</relativePath>
    </parent>
  • 在子工程中配置使用父工程中可选依赖的坐标

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
    </dependencies>

    注意事项:

    1.子工程中使用父工程中的可选依赖时,仅需要提供群组id和项目id,无需提供版本,版本由父工程统一提供,避免版本冲突

    2.子工程中还可以定义父工程中没有定义的依赖关系,只不过不能被父工程进行版本统一管理。

聚合与继承的区别

3.3.1 聚合与继承的区别

两种之间的作用:

  • 聚合用于快速构建项目,对项目进行管理

  • 继承用于快速配置和管理子项目中所使用jar包的版本

聚合和继承的相同点:

  • 聚合与继承的pom.xml文件打包方式均为pom,可以将两种关系制作到同一个pom文件中

  • 聚合与继承均属于设计型模块,并无实际的模块内容

聚合和继承的不同点:

  • 聚合是在当前模块中配置关系,聚合可以感知到参与聚合的模块有哪些

  • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己

属性

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.version}</version>
</dependency>

配置文件加载属性

步骤1:父工程定义属性

<properties>
   <jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url>
</properties>

步骤2:jdbc.properties文件中引用属性

在jdbc.properties,将jdbc.url的值直接从获取Maven配置的属性

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=${jdbc.url}
jdbc.username=root
jdbc.password=root

步骤3:设置maven过滤文件范围

Maven在默认情况下是从当前项目的src\main\resources下读取文件进行打包。现在我们需要打包的资源文件是在maven_02_ssm下,需要我们通过配置来指定下具体的资源目录。

<build>
    <resources>
        <!--
			${project.basedir}: 当前项目所在目录,子项目继承了父项目,
			相当于所有的子项目都添加了资源目录的过滤
		-->
        <resource>
            <directory>${project.basedir}/src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

说明:**打包的过程中如果报如下错误:

原因就是Maven发现你的项目为web项目,就会去找web项目的入口web.xml[配置文件配置的方式],发现没有找到,就会报错。

解决方案1:在maven_02_ssm项目的src\main\webapp\WEB-INF\添加一个web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
</web-app>

解决方案2: 配置maven打包war时,忽略web.xml检查

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.3</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>
    </plugins>
</build>

上面我们所使用的都是Maven的自定义属性,除了${project.basedir},它属于Maven的内置系统属性

属性分类

版本后缀

  • SNAPSHOT(快照版本)

    • 项目开发过程中临时输出的版本,称为快照版本

    • 快照版本会随着开发的进展不断更新

  • RELEASE(发布版本)

    • 项目开发到进入阶段里程碑后,向团队外部发布较为稳定的版本,这种版本所对应的构件文件是稳定的

    • 即便进行功能的后续开发,也不会改变当前发布版本内容,这种版本称为发布版本

除了上面的工程版本,我们还经常能看到一些发布版本:

  • alpha版:内测版,bug多不稳定内部版本不断添加新功能

  • beta版:公测版,不稳定(比alpha稳定些),bug相对较多不断添加新功能

  • 纯数字版

多环境配置与应用

<profiles>
    <!--开发环境-->
    <profile>
        <id>env_dep</id>
        <properties>
            <jdbc.url>jdbc:mysql://127.1.1.1:3306/ssm_db</jdbc.url>
        </properties>
    </profile>
    <!--生产环境-->
    <profile>
        <id>env_pro</id>
        <properties>
            <jdbc.url>jdbc:mysql://127.2.2.2:3306/ssm_db</jdbc.url>
        </properties>
        <!--设定是否为默认启动环境-->
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <!--测试环境-->
    <profile>
        <id>env_test</id>
        <properties>
            <jdbc.url>jdbc:mysql://127.3.3.3:3306/ssm_db</jdbc.url>
        </properties>
    </profile>
</profiles>

私服

  • 私服简介

  • 私服仓库分类

  • 资源上传与下载

首先来说一说什么是私服?

6.1 私服简介

团队开发现状分析

(1)张三负责ssm_crm的开发,自己写了一个ssm_pojo模块,要想使用直接将ssm_pojo安装到本地仓库即可

(2)李四负责ssm_order的开发,需要用到张三所写的ssm_pojo模块,这个时候如何将张三写的ssm_pojo模块交给李四呢?

(3)如果直接拷贝,那么团队之间的jar包管理会非常混乱而且容器出错,这个时候我们就想能不能将写好的项目上传到中央仓库,谁想用就直接联网下载即可

(4)Maven的中央仓库不允许私人上传自己的jar包,那么我们就得换种思路,自己搭建一个类似于中央仓库的东西,把自己的内容上传上去,其他人就可以从上面下载jar包使用

(5)这个类似于中央仓库的东西就是我们接下来要学习的私服

所以到这就有两个概念,一个是私服,一个是中央仓库

私服:公司内部搭建的用于存储Maven资源的服务器

远程仓库:Maven开发团队维护的用于存储Maven资源的服务器

所以说:

  • 私服是一台独立的服务器,用于解决团队内部的资源共享与资源同步问题

搭建Maven私服的方式有很多,我们来介绍其中一种使用量比较大的实现方式:

  • Nexus

    • Sonatype公司的一款maven私服产品

    • 下载地址:Download

6.2 私服安装

步骤1:下载解压

资料\latest-win64.zip解压到一个空目录下。

步骤2:启动Nexus

使用cmd进入到解压目录下的nexus-3.30.1-01\bin,执行如下命令:

nexus.exe /run nexus

看到如下内容,说明启动成功。

步骤3:浏览器访问

访问地址为:

http://localhost:8081

步骤4:首次登录重置密码

输入用户名和密码进行登录,登录成功后,出现如下页面

点击下一步,需要重新输入新密码,为了和后面的保持一致,密码修改为admin

设置是否运行匿名访问

点击完成

至此私服就已经安装成功。如果要想修改一些基础配置信息,可以使用:

  • 修改基础配置信息

    • 安装路径下etc目录中nexus-default.properties文件保存有nexus基础配置信息,例如默认访问端口。

  • 修改服务器运行配置信息

    • 安装路径下bin目录中nexus.vmoptions文件保存有nexus服务器启动对应的配置信息,例如默认占用内存空间。

6.3 私服仓库分类

私服资源操作流程分析:

(1)在没有私服的情况下,我们自己创建的服务都是安装在Maven的本地仓库中

(2)私服中也有仓库,我们要把自己的资源上传到私服,最终也是放在私服的仓库中

(3)其他人要想使用你所上传的资源,就需要从私服的仓库中获取

(4)当我们要使用的资源不是自己写的,是远程中央仓库有的第三方jar包,这个时候就需要从远程中央仓库下载,每个开发者都去远程中央仓库下速度比较慢(中央仓库服务器在国外)

(5)私服就在准备一个仓库,用来专门存储从远程中央仓库下载的第三方jar包,第一次访问没有就会去远程中央仓库下载,下次再访问就直接走私服下载

(6)前面在介绍版本管理的时候提到过有SNAPSHOTRELEASE,如果把这两类的都放到同一个仓库,比较混乱,所以私服就把这两个种jar包放入不同的仓库

(7)上面我们已经介绍了有三种仓库,一种是存放SNAPSHOT的,一种是存放RELEASE还有一种是存放从远程仓库下载的第三方jar包,那么我们在获取资源的时候要从哪个仓库种获取呢?

(8)为了方便获取,我们将所有的仓库编成一个组,我们只需要访问仓库组去获取资源。

所有私服仓库总共分为三大类:

宿主仓库hosted

  • 保存无法从中央仓库获取的资源

    • 自主研发

    • 第三方非开源项目,比如Oracle,因为是付费产品,所以中央仓库没有

代理仓库proxy

  • 代理远程仓库,通过nexus访问其他公共仓库,例如中央仓库

仓库组group

  • 将若干个仓库组成一个群组,简化配置

  • 仓库组不能保存资源,属于设计型仓库

6.4 本地仓库访问私服配置

  • 我们通过IDEA将开发的模块上传到私服,中间是要经过本地Maven的

  • 本地Maven需要知道私服的访问地址以及私服访问的用户名和密码

  • 私服中的仓库很多,Maven最终要把资源上传到哪个仓库?

  • Maven下载的时候,又需要携带用户名和密码到私服上找对应的仓库组进行下载,然后再给IDEA

上面所说的这些内容,我们需要在本地Maven的配置文件settings.xml中进行配置。

步骤1:私服上配置仓库

说明:

第5,6步骤是创建itheima-snapshot仓库

第7,8步骤是创建itheima-release仓库

步骤2:配置本地Maven对私服的访问权限

 <servers>
      <server>
          <id>heima-releases</id>
          <username>admin</username>
          <password>321EWQasd#</password>
      </server>
      <server>
          <id>heima-snapshots</id>
          <username>admin</username>
          <password>321EWQasd#</password>
      </server>
      <server>
          <id>heima</id>
          <username>admin</username>
          <password>321EWQasd#</password>
      </server>
  </servers>

步骤3:配置私服的访问路径

<mirrors>     
        <mirror> 
                <id>heima</id>
                <mirrorOf>*</mirrorOf>
                <name>maven-nexus</name>
                <url>http://47.93.15.190:8081/repository/maven-public/</url>
        </mirror> 
</mirrors>

为了避免阿里云Maven私服地址的影响,建议先将之前配置的阿里云Maven私服镜像地址注释掉,等练习完后,在将其恢复。

至此本地仓库就能与私服进行交互了。

6.5 私服资源上传与下载

本地仓库与私服已经建立了连接,接下来我们就需要往私服上上传资源和下载资源,具体的实现步骤为:

步骤1:配置工程上传私服的具体位置

 <!--配置当前工程保存在私服中的具体位置-->
 <distributionManagement>
    <!--和maven/settings.xml中server中的id一致,表示使用该id对应的用户名和密码-->
        <repository>
            <id>heima-releases</id>
          <!--release版本上传仓库的具体地址-->
            <url>http://47.93.15.190:8081/repository/maven-releases/</url>
            <name>nexus私服中宿主仓库->存放/下载稳定版本的构件</name>
        </repository>
        <snapshotRepository>
          <!--和maven/settings.xml中server中的id一致,表示使用该id对应的用户名和密码-->
            <id>heima-snapshots</id>
           <!--snapshot版本上传仓库的具体地址-->
            <url>http://47.93.15.190:8081/repository/maven-snapshots/</url>
            <name>nexus私服中宿主仓库->存放/下载快照版本的构件</name>
        </snapshotRepository>
    </distributionManagement>

步骤2:发布资源到私服

或者执行Maven命令

mvn deploy

注意:

要发布的项目都需要配置distributionManagement标签,要么在自己的pom.xml中配置,要么在其父项目中配置,然后子项目中继承父项目即可。

发布成功,在私服中就能看到:

现在发布是在itheima-snapshot仓库中,如果想发布到itheima-release仓库中就需要将项目pom.xml中的version修改成RELEASE即可。

如果想删除已经上传的资源,可以在界面上进行删除操作:

如果私服中没有对应的jar,会去中央仓库下载,速度很慢。可以配置让私服去阿里云中下载依赖。

至此私服的搭建就已经完成,相对来说有点麻烦,但是步骤都比较固定,后期大家如果需要的话,就可以参考上面的步骤一步步完成搭建即可。

SpringBoot

简介

Spring优缺点

* 优点:
		Spring是一个轻量级的Java开源框架,通过IOC和AOP轻松实现程序开发。
* 缺点:
		1. 添加一个框架或技术时,需要添加相应的maven依赖
		2. 添加一个框架或技术时,引入的依赖可能出现依赖冲突
		3. 添加一个框架或技术时,需要添加大量的配置

SpringBoot概述

* SpringBoot对Spring的缺点进行的改善和优化,它基于`约定优于配置`的思想,提供了大量的默认配置和实现
* 使用SpringBoot之后,程序员只需按照SpringBoot规定的方式去进行程序代码的开发即可,而无需再去编写一堆复杂的配置
* SpringBoot的主要功能如下:
		版本锁定:SpringBoot在父工程中进行了大量常见依赖的版本锁定
		起步依赖:SpringBoot以功能化的方式将需要的依赖进行组装,并且允许程序员以start的方式进行引入
		默认配置:SpringBoot实现了大量依赖框架的默认配置项,程序员无须再进行自己配置
		内置Tomcat:SpringBoot内置了一个tomcat,使用它开发的程序无需再进行tomcat部署,可直接运行
* 总之:SpringBoot最主要作用就是帮我们快速的构建庞大的spring项目,并且尽可能的减少配置,让程序员去关注业务而非配置。

入门案例(重点)

需求:搭建一个SpingBoot项目,实现访问浏览器地址localhost:8080/index, 返回hello, springboot!这句话

代码开发

创建工程

注意: springboot工程要求必须去继承一个springboot提供的父工程,里面对大量的依赖版本进行了锁定

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   
   <groupId>com.ali</groupId>
   <artifactId>springboot-quickstart</artifactId>
   <version>1.0-SNAPSHOT</version>
   
   <properties>
      <maven.compiler.source>8</maven.compiler.source>
      <maven.compiler.target>8</maven.compiler.target>
   </properties>
   
   
   <!--1. 设置父工程: 里面进行版本的锁定-->
<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.2.2.RELEASE</version>
</parent>

<!--2. 引入依赖启动器: 里面是一堆需要的依赖的集合-->
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <version>1.18.24</version>
      </dependency>
   </dependencies>
</project>

编写controller

@RestController
public class IndexController {
    @RequestMapping("/index")
    public String index(){
        return "SpringBoot";
    }

}

编写启动类

package com.ali;

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

//位置:必须在com.ali下
//名称:必须是xxxxApplication
//内容:必须是main里面调用SpringApplication.run(当前类名.class,args),注解

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

案例解析

* 做完了入门案例,我们应该会有下面这样几个疑问:
	1. 我们的工程在引入`spring-boot-starter-web`依赖的时候,为什么没有指定版本
	2. `spring-boot-starter-web`是个啥,为什么引入了它之后,就不需要再引入我们原来做spring开发的那一堆包了
	3. 我们的程序没有部署到tomcat,为什么就可以被访问
	4. 为什么我们访问程序的时候要用8080端口,谁定义的

版本锁定

我们的项目继承了spring-boot-starter-parent父工程,它内部已经锁定了一些常见依赖的版本号,故而在我们自己开发的工程中无需再指定依赖的版本。

起步依赖

SpringBoot根据场景将各种依赖组装成了一个个的集合(starter),我们根据功能引入指定的starter即可。

内置tomcat

SpringBoot在中引入一个内置的tomcat,故而我们无需再将程序部署到位置的tomcat中即可运行。

默认配置

SpringBoot的约定大于配置,即SpringBoot的大量配置都有默认值,如果我们不去写配置就使用默认的。

比如说:tomcat默认端口 8080、redis 默认端口6379 、session 的过期默认时间为30m等等

配置文件(重点)

自定义配置

SpringBoot是基于约定的,很多配置都有默认值,但它也允许使用自己的配置替换默认配置,具体做法是在resources下创建文件:

application.yaml   或者   application.yml  或者  application.properties

注意:目前版本中, SpringBoot启动时会依次加载:yaml、yml、properties文件,如果多个配置文件同时存在,后加载的会覆盖前面加载的内容。其他版本不一定

YAML介绍

YAML 是专门用来写配置文件的语言,非常简洁和强大。

语法

# 大小写敏感
# 使用缩进表示层级关系
# 缩进的空格数目不重要,但是相同层级的元素必须左侧对齐 
# 参数值和冒号之间必须有空格
server:
  port: 8082
  servlet:
    context-path: /itheima

数据格式

# 纯量:单个的、不可再分的值(包括字符串字符串、布尔值、数值、Null、时间类型)
username: 'jack'

# 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
user:
  username: '张三'
  password: '123'
  
# 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
addressList:
  - '北京'
  - '上海'
  - '广州'

读取配置

* 方式一: Environment(了解)
	此对象是Spring框架提供的,用来表示整个应用运行时的环境,可以读取配置文件中的属性值并逐个注入到Bean对象的对应属性中

* 方式二: @Value (了解)
	此注解是Spring框架提供的,用来读取配置文件中的属性值并逐个注入到Bean对象的对应属性中

* 方式三: @ConfigurationProperties(掌握) 
	此注解是SpringBoot框架提供的,用来快速、方便地将配置文件中的自定义属性值批量注入到某个Bean对象的多个对应属性中

三种方式

package com.ali.controller;

import com.ali.config.UserConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {
    @Autowired
    private Environment environment;

    @Value("${user.username}")
    private String username;
    @Value("${user.password}")
    private String password;

    @Autowired
    private UserConfig userConfig;

    @RequestMapping("/index")
    public String index(){
        System.out.println("=========Environment=====");
        System.out.println(environment.getProperty("user.username"));
        System.out.println(environment.getProperty("user.password"));

        System.out.println("======Value=============");
        System.out.println(username);
        System.out.println(password);

        System.out.println("=========ConfigurationProperties=============");
        System.out.println(userConfig.getUsername());
        System.out.println(userConfig.getPassword());
        return "SpringBoot";
    }
}

ConfigurationProperties的配置类

package com.ali.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
@Component
@ConfigurationProperties("user")
@Data
public class UserConfig {
    private String username;
    private String password;
    private List<String> address;
}

添加下面依赖可以解决图中的红色提示

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

多环境配置

在实际开发中,项目的开发环境、测试环境、生产环境的配置信息是否会一致?如何快速切换?

使用---隔开

共享资源
---
spring:
  profiles:
    active: test #激活那个环境
---
spring:
  profiles: test
server:
  port: 8080

user:
  username: 张三test
---
spring:
  profiles: pre
server:
  port: 8081
user:
  username: 张三pre
---
spring:
  profiles: dev
server:
  port: 8082
user:
  username: 张三dev

打包依赖

<!--在pom.xml的project节点中添加下面配置,然后执行maven clean package-->
<build>
    <finalName>springboot</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

命令格式传递参数

java –jar springboot.jar --spring.profiles.active=test     # 指定使用test环境 
java –jar springboot.jar --spring.profiles.active=test --server.port=9090  # 指定使用test环境,端口号为9090

整合其他框架(练习)

日志输出设置

# 日志级别设置格式是   
# 包名: 日志级别(常用的级别有4个: debug info warn error)
logging:
  level:
    org.springframework: info
    com.itheima: info

① 打印日志的时候,设置级别

@RestController
@Slf4j
public class IndexController {
    @Autowired
    private Environment environment;

    @Value("${user.username}")
    private String username;
    @Value("${user.password}")
    private String password;

    @Autowired
    private UserConfig userConfig;
    
    @GetMapping("/config")
    public String config(){
        log.info("=========Environment=====");
        log.info(environment.getProperty("user.username"));
        log.info(environment.getProperty("user.password"));

        log.info("======Value=============");
        log.info(username);
        log.info(password);

        log.info("=========ConfigurationProperties=============");
        log.info(userConfig.getUsername());
        log.info(userConfig.getPassword());
        return "Config";
    }
}

控制显示日志的级别**

# 设置                      输出
# debug                     debug  info  warn  error
# info                      info  warn  error
# warn                      warn  error
# error                     error

配置类

package com.ali.config;

import com.ali.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private MyInterceptor myInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).
                addPathPatterns("/**").
                excludePathPatterns("/html/hello.html");
    }
}

单元测试

① 引入依赖

在pom.xml加入单元测试的starter

<!--引入junit依赖启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

② 编写测试类

注意测试类的位置: 必须在启动类所在包之下

ackage com.ali.test;

import com.ali.config.UserConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


//包必须在主类同级或者子包下
//要用    @SpringBootTest 指定这是单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class MyTest {
    @Autowired
    private UserConfig userConfig;
    @Test
    public void test1(){
        System.out.println(userConfig.getUsername());
    }
}

指定静态资源

现在项目是一个普通java工程,没有webapp,那么静态资源应该放哪里呢? 其实SpringBoot已经定义了几个位置用于存放静态资源

public class ResourceProperties {
//定义了静态资源路径				以下四种方式都可以,可以使用第三种
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = 
	new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
}

我们习惯会把静态资源放在classpath:/static/ 目录下。

拦截器配置

在SpringBoot中,拦截器的配置是使用java注解实现的,主要分为下面两步骤

①. 定义拦截器

自定义一个类实现HandlerInterceptor接口,重写自己需要的方法

package com.ali.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入控制器之前");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("离开控制器之后");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("页面渲染环境之后");
    }
}

注册拦截器**

自定义一个配置类,实现WebMvcConfigurer接口,并通过addInterceptors方法将自己的拦截器注册到SpringBoot

package com.ali.config;

import com.ali.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private MyInterceptor myInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).
                addPathPatterns("/**").
                excludePathPatterns("/html/hello.html");
    }
}

整合MyBatis

数据准备

使用下面的sql创建数据库和表

CREATE DATABASE `springboot`;
USE `springboot`;

DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `username` varchar(20) NOT NULL COMMENT '用户名',
  `password` varchar(20) NOT NULL COMMENT '密码',
  `nick_name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;


insert  into `tb_user`(`id`,`username`,`password`,`nick_name`,`age`,`email`) values 
(1,'zhangsan','123456','张三',18,'test1@itcast.cn'),
(2,'lisi','123456','李四',20,'test2@itcast.cn'),
(3,'wangwu','123456','王五',28,'test3@itcast.cn'),
(4,'zhaoliu','123456','赵六',21,'test4@itcast.cn'),
(5,'sunqi','123456','孙七',24,'test5@itcast.cn');

添加依赖

在pom.xml中加入下面依赖

<!--调整mysql的版本-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
</dependency>
<!--加入mybatis的启动器,这是mybatis公司提供的-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

创建实体类

package com.itheima.domain;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String username;
    private String password;
    private String nickName;
    private Integer age;
    private String email;
}

创建UserMapper接口

创建dao层的接口,推荐使用类似于xxxMapper的名字

创建UserMapper映射

在resources下创建mappers目录,然后在此目录下创建UserMapper.xml文件,注意:这里用做到包的对应

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.UserMapper">
    <select id="findAll" resultType="com.itheima.domain.User">
        select * from tb_user
    </select>
</mapper>

添加配置文件

在配置文件application.yaml中添加数据源和mybatis的配置

# 数据源配置(springboot内置连接池对象HiKariCP)
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///springboot
    username: root
    password: root

# mybatis简单配置
mybatis:
  mapper-locations: classpath:mappers/** # 指定mapper映射文件
  configuration:
    map-underscore-to-camel-case: true # 开启驼峰式映射

修改启动类

修改启动类,在类中使用@MapperScan注解指定mapper接口所在的包

测试

修改UserController代码为从数据库查询,然后重启程序进行测试。

切换数据源

Springboot 2.2.0版本之后,内置了HiKariCP连接池,那如何将其切换换成druid连接池呢?

① 修改pom.xml,添加druid的依赖

<!--druid依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.15</version>
</dependency>

② 修改application.yaml,添加spring.datasource.type配置项

# 连接池配置(springboot内置连接池对象HiKariCP)
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///springboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource

自动装配

源码分析

SpringBootApplication注解分析

@Target(ElementType.TYPE)				//标注目标放在类上或者接口上
@Retention(RetentionPolicy.RUNTIME)		//保留策略:保留到运行时
@Documented								//生成JavaDoc文档
@Inherited								//运行被继承
@SpringBootConfiguration				//相当于@Configuration,标明时配置类
@EnableAutoConfiguration				//自动配置, 借助@Import的支持,将所有符合自动配置条件的bean定义加载到IoC容器
@ComponentScan()						//注解扫描	 扫描的是当前类所在包及其子包中类中的注解
public @interface SpringBootApplication {}

@EnableAutoConfiguration注解分析

@AutoConfigurationPackage			//获取注解扫描的包路径	包里面是@Import(AutoConfigurationPackages.Registrar.class)
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

Registrar类分析

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   //加载启动类所在的包下的主类与子类的所有组件注册到spring容器,这就是springboot默认扫描启动类所在的包下的主类与子类的所有组件。
   //此处的new PackageImport(metadata).getPackageName()会解析出包名com.ali
      register(registry, new PackageImport(metadata).getPackageName());
   }
}

AutoConfigurationImportSelector类分析

public class AutoConfigurationImportSelector...{
    ...

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry
                		= this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

    protected AutoConfigurationImportSelector.AutoConfigurationEntry 
        getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        //从所有的jar包中读取META-INF/spring.factories文件信息。
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories....");
        return configurations;
    }
}

下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容,其中有一个key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值定义了需要自动配置的bean,通过读取这个配置获取一组@Configuration类。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
......省略了很多

DispatcherServletAutoConfiguration

@AutoConfigureOrder(-2147483648)
@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({DispatcherServlet.class})  //只有JVM中有DispatcherServlet这个类的字节码时,当前类才有效
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
public class DispatcherServletAutoConfiguration {
    public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
    public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

    @Configuration(
        proxyBeanMethods = false
    )
    @Conditional({DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition.class})
    @ConditionalOnClass({ServletRegistration.class})
    @EnableConfigurationProperties({HttpProperties.class, WebMvcProperties.class})
    protected static class DispatcherServletConfiguration {
        protected DispatcherServletConfiguration() {
        }

        //创建DispatcherServlet的对象并放入容器
        @Bean(
            name = {"dispatcherServlet"}
        )
        public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
            dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
            dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
            return dispatcherServlet;
        }

        @Bean
        @ConditionalOnBean({MultipartResolver.class})
        @ConditionalOnMissingBean(
            name = {"multipartResolver"}
        )
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
            return resolver;
        }
    }
}

条件装配

* 并不是读取META-INF/spring.factories所有的Bean都会被初始化,在配置类中使用@Condition来加载满足条件的Bean
- ConditionalOnClass:      判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnProperty:   判断配置文件中是否有对应属性和值才初始化Bean 
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean

源码图示

Tomcat

  1. 执行run方法

  1. 调用刷新方法

  1. 执行刷新

  2. 执行创建之前调用工厂

  1. 创建Tomcat

6.启动Tomcat操作

MyBatisPlus

MyBatisPlus概述

MyBatis介绍

  • MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具

  • 设计理念:简化开发、提高效率。

  • 可以减少大量重复代码的编写,并且封装了很多常用方法。

MyBatisPlus特性

  • 无侵入:只做增强不做改变,不会对现有工程产生影响

  • 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作

  • 支持 Lambda:编写查询条件无需担心字段写错

  • 支持主键自动生成、内置分页插件、逻辑删除、自动填充、代码生成等一系列功能

  • 国人开发,官网:Redirect

入门案例

SpringBoot整合MP入门程序

步骤:

  1. 新建项目,选择Spring初始化,勾选mysql模块

  2. 引入MyBatis-Plus的stater以及Druid的stater,在配置文件中配置数据源(DataSource)

  3. 在数据库中创建表,之后在项目中创建与之对应的实体类。

  4. 编写mapper,定义数据接口,继承BaseMapper<T>

  5. 编写测试方法,测试类中注入dao接口,测试功能

引入MyBatis-Plus的stater以及Druid的stater,在配置文件中配置数据源(DataSource)

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
    username: root
    password: root

标准数据层开发

1. MyBatisPlus的CRUD操作

MyBatisPlus分页使用

yBatisPlus分页使用

①:设置分页拦截器作为Spring管理的bean

package com.itheima.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}

②:执行分页查询

//分页查询
@Test
void testSelectPage(){
    		//1 创建IPage分页对象,设置分页参数
    		IPage<User> page=new Page<>(1,3);
        IPage<User> page = new Page<>();
        // 页码
        page.setCurrent(1);
        // 条数
        page.setSize(2);
        IPage<User> users = dao.selectPage(page, null);
        log.info("记录 ===> {}", users.getRecords());
        log.info("分页总页数 ===> {}", users.getPages());
        log.info("总行数 ===> {}", users.getTotal());
}

开启MyBatisPlus日志

# 开启mp的日志(输出到控制台)
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

DQL编程控制

条件查询

// 跟据条件查询1
@Test
public void test1(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.ge("age",18);
    queryWrapper.le("age",23);
    List<User> users = userMapper.selectList(queryWrapper);
    log.info(users.toString());
}

// 跟据条件查询2
@Test
public void test2(){
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().ge(User::getAge,18).le(User::getAge,23);
    List<User> users = userMapper.selectList(queryWrapper);
    log.info(users.toString());
}
//跟据条件查询3
@Test
public void test3(){
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.le(User::getAge,23).ge(User::getAge,18);
    List<User> users = userMapper.selectList(queryWrapper);
    log.info(users.toString());
}
// 跟据条件查询(推荐)
@Test
public void test4(){
    List<User> users = userMapper.selectList(Wrappers.lambdaQuery(User.class).ge(User::getAge, 18).le(User::getAge, 23));
    //List<User> users = userMapper.selectList(Wrappers.lambdaQuery(new User()).ge(User::getAge, 18).le(User::getAge, 23));
    log.info(users.toString());
}

组合条件

并且关系(and)

//并且关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//并且关系:10到30岁之间
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
log.info(userList);

或者关系(or)

//或者关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者关系:小于10岁或者大于30岁
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List<User> userList = userDao.selectList(lqw);
log.info(userList);

查询投影-设置【查询字段、分组、分页】

查询结果包含模型类中部分属性

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 查询投影.
        wrapper.select(User::getId, User::getName, User::getAge);
        List<User> users = dao.selectList(wrapper);
        log.info(users.toString());

		//推荐
        List<User> users = userMapper.selectList(Wrappers.lambdaQuery(new User()).select(User::getAge, User::getId, User::getName));

查询结果包含模型类中未定义的属性

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("count(*) as count");
        List<Map<String, Object>> maps = dao.selectMaps(wrapper);
        log.info(maps.toString());


//推荐
//        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//        queryWrapper.select("count(*) as count");
//        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        List<Map<String, Object>> maps = userMapper.selectMaps(Wrappers.query(new User()).select("count(*) as count"));
        log.info(maps.toString());

3. 查询条件设定

查询条件

  • demo

        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        // 1.大于
        //wrapper.gt(User::getAge,18);
        // 2.介于...之间
        //wrapper.between(User::getAge, 18, 28);
        // 3.模糊匹配
        //wrapper.like(User::getAge,2);
        // 4.in
        //wrapper.in(User::getAge,18,20);
        //wrapper.in(User::getAge, Arrays.asList(18,20));
        // 5.group
        //wrapper.select(User::getAge);
        //wrapper.groupBy(User::getAge);
        // 6.order
        // 升序
        //wrapper.orderBy(true, true, User::getAge);
        // 降序
        wrapper.orderBy(true, false, User::getAge);
        List<User> users = dao.selectList(wrapper);
        log.info("user ===> {}", users.toString());

查询API

字段映射与表名映射(注解和全局配置)

  • 模型类属性上方,使用@TableField属性注解,通过value属性,设置当前属性对应的数据库表中的字段关系

  • 在模型类属性上方,使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。

  • 在模型类属性上方,使用@TableField注解,通过select属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。

  • 模型类上方,使用@TableName注解,通过value属性,设置当前类对应的数据库表名称。

@TableName("tbl_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    @TableField(value = "pwd",exist = false)
    private String password;
    private Integer age;
    private String tel;
    @TableLogic				
    private Integer deleted;
    @Version
    private Integer version;
}

DML编程控制

id生成策略控制(Insert)

雪花算法

雪花算法就是使用64位long类型的数据存储id,最高位一位存储0或者1,0代表整数,1代表负数,一般都是0,所以最高位不变,41位存储毫秒级时间戳,10位存储机器码(包括5位datacenterId和5位workerId),12存储序列号。这样最大2的10次方的机器,也就是1024台机器,最多每毫秒每台机器产生2的12次方也就是4096个id。

全局策略配置

# 开启mp的日志(输出到控制台)
mybatis-plus:
  global-config:
    db-config:
       # 逻辑删除数据数据删除后变成1
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
      # id生成策略
      id-type: auto
      # 匹配表明
      table-prefix: tbl_

乐观锁

1 乐观锁案例

①:数据库表中添加锁标记字段

实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
public class User {

	private Long id;
	
    @Version
    private Integer version;
}

③:配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装

package com.itheima.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();

        //2.添加乐观锁拦截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        return mpInterceptor;
    }
}

④:使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行

    @Test
    void testLock() {
        // 1. 接收前端传入的数据,使用这个数据
        // 2. 在数据库中取出与之对应的数据.
        // 3. 对比前端传入的数据和db中的数据
        // 4. 校验通过的话,更新数据.

        // 前端的数据
        User query = new User();
        query.setId(1L);
        query.setName("张三李四");
        query.setVersion(0);
        // db中的数据
        User user = dao.selectById(1L);
        if (!(query.getVersion().equals(user.getVersion()))) {
            log.info("该数据已经被他人修改,请刷新后再重试");
            return;
        }
        // 更新操作
        user.setName(query.getName());
        dao.updateById(user);
    } 

快速开发-代码生成器

工程搭建和基本代码编写

  • 第一步:创建SpringBoot工程,添加代码生成器相关依赖,其他依赖自行添加

<!--代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>

<!--velocity模板引擎-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>
  • 第二步:编写代码生成器类

package com.itheima;

import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;

public class Generator {
    public static void main(String[] args) {
        //1. 创建代码生成器对象,执行生成代码操作
        AutoGenerator autoGenerator = new AutoGenerator();

        //2. 数据源相关配置:读取数据库中的信息,根据数据库表结构生成代码
        DataSourceConfig dataSource = new DataSourceConfig();
        dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        autoGenerator.setDataSource(dataSource);

         //3. 执行生成操作
        autoGenerator.execute();
    }
}

3. 开发者自定义配置

  • 设置全局配置

//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");    //设置代码生成位置
globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
globalConfig.setAuthor("黑马程序员");    //设置作者
globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
autoGenerator.setGlobalConfig(globalConfig);
  • 设置包名相关配置

//设置包名相关配置
PackageConfig packageInfo = new PackageConfig();
packageInfo.setParent("com.aaa");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
packageInfo.setEntity("domain");    //设置实体类包名
packageInfo.setMapper("dao");   //设置数据层包名
autoGenerator.setPackageInfo(packageInfo);
  • 策略设置

//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_");  //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格
strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true);  //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);

说明:在资料中也提供了CodeGenerator代码生成器类,根据实际情况修改后可以直接使用。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/704130
推荐阅读
相关标签
  

闽ICP备14008679号