乐趣区

关于node.js:企业开发中Maven的基本使用

简述
java 开发中能够应用 maven 来治理依赖,引入依赖,构建最终 jar 文件,当然其中也可能须要解决依赖抵触问题。

治理依赖:通过 <dependencyManagement>, 申明依赖版本,进行依赖的版本控制的。
引入依赖:通过 <dependency>, 进行依赖的理论引入。
构建 jar 包:在须要打包的模块中增加 <build> 并退出定制插件 plugin 进行 jar 生成。
依赖抵触:通过工具或者命令行排查抵触的依赖后,应用 exclusion 来排出抵触的依赖。

注:

依赖抵触能够应用 idea 的 mavenhelper 插件来查看,简略直观,也能够命令行应用 mvn dependency:tree -Dverbose > tree.txt,在文件中检索 conflict 关键字。

maven 应用前,须要设置好 setting.xml 配置文件,如镜像仓库。

maven 的打包命令 mvn clean package -Dmaven.test.skip=true
指定配置文件打包 mvn clean package -s setting.xml -Dmaven.test.skip=true
maven 的仲裁机制:门路最近者优先,门路雷同第一声明者优先(门路间隔是从打包模块的 pom 开始算,第一申明是 pom 中申明的前后程序)

maven 罕用标签的应用
的根本应用
次要解说的内容:依赖罕用的两种援用形式,依赖的排除形式,依赖的作用域,<option> 标签。

仓库引入

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
复制代码

本地引入

注:须要在 <build> 中 <plugin> 中需增加 <includeSystemScope>true</includeSystemScope>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>kingbase8-hibernatedialect</artifactId>
<version>5.2.17.Finaldialect</version>
<scope>system</scope>
<systemPath>${project.basedir}/../../../lib/hibernate-5.2.17.Finaldialect.jar</systemPath>
</dependency>
复制代码

依赖排除

<dependency>

<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>

<!– 须要排除的依赖项 –>

<exclusions>
<!-- 排除的依赖无需申明版本号 -->
    <exclusion>
        <groupId>com.github.jsqlparser</groupId>
        <artifactId>jsqlparser</artifactId>
    </exclusion>
</exclusions>

</dependency>
复制代码

依赖的作用域标签 <scope>, 其默认值为 compile。

<!– Lombok –>
<dependency>

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>

</dependency>
复制代码

拓展:<scope>import</scope> 只能在 <dependencyManagement> 模块中应用,用于解决 maven 的单继承问题。

<option> 标签,固定值为 true

<option> 示意这个依赖是须要抉择是否引入的,如果须要引入则须要显示申明。
举例:下方代码块是 B 模块的 pom 文件,A 我的项目将 B 我的项目作为依赖后,这些带 <option> 的依赖并不会被引入,不会打进 jar 包,如果须要引入则显示的增加申明。
<dependencies>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.10</version>
    <optional>true</optional>
<dependency>
<dependency>
    <groupId>postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>8.4-701.jdbc3</version>
    <optional>true</optional>
<dependency>

</dependencies>
复制代码

拓展常识:<classifier> 标签,提供一个新的维度定义 jar, 个别为不同 jdk 版本生成 jar 文件

maven-resp (maven 仓库)
└── org

└── apache
    └── maven
        ├── maven-artifacr-3.8.1-calss1.jar(jdk8 生成一个 jar)└── maven-artifacr-3.8.1-calss2.jar  (jdk21 生成一个 jar)

在 pom 文件中辨别环境的中增加 <classifier>calss1</classifier> 或 <classifier>calss1</classifier> 则不同环境生成不同 jar 文件
样例:
<properties>

  <classifier>default</classifier>

</properties>
<profiles>
<profile>

<id>jdk8</id>
<properties>
  <classifier>calss1</classifier>
</properties>

</profile>
<profile>

<id>jdk21</id>
<properties>
  <classifier>calss2</classifier>
</properties>

</profile>
</profiles>
<bulid>
<plugins>

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <version>3.2.0</version>
  <executions>
    <execution>
      <id>default-jar</id>
      <goals>
        <goal>jar<goal>
      </goals>
      <configuration>
        <classifier>${classifier}</classifier>
      </configuration> 
    </execution> 
  </executions> 
</plugin> 

</plugins>
</build>
复制代码
我的项目 A, 依赖 B 我的项目,并且须要 mysql 驱动
<dependencies>
<dependency>

    <groupId>project-b</groupId>
    <artifactId>project-b</artifactId>
    <version>b-version</version>
<dependency>
<!-- 因为 B 模块中 mysql 驱动是可选的,所以 A 模块须要 mysql 驱动得显示引入 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.10</version>
<dependency>

</dependencies>
复制代码
的根本应用

      <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
            <!--1. 在原始 Maven 打包造成的 jar 包根底上,进行从新打包,新造成的 jar 包岂但蕴含利用类文件和配置文件,而且还会蕴含利用所依赖的 jar 包以及 Springboot 启动相干类(loader 等),以此来满足 Springboot 独立利用的个性;2. 将原始 Maven 打包的 jar 重命名为 XXX.jar.original 作为原始文件;-->
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
      <configuration>
        <!-- 将本地 jar 包打入构建生成的 jar 文件中 -->
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>

复制代码
微服务的目录构造
以当初宽泛风行的 springboot 多个微服务来作为介绍模板,一个微服务能够作为一个单体我的项目对待。
├── common (公共模块)
│ └── pom.xml
├── services(微服务模块)
│ ├── common(微服务之间的公共模块)
│ │ └── pom.xml
│ ├── service1(某微服务 1 的聚合模块)
│ │ ├──api(提供调用接口的模块)
│ | | └── pom.xml
│ | ├──specific-service(实现具体性能的模块)
│ | | └── pom.xml
│ │ └── pom.xml
│ ├── service2(某微服务 2 的聚合模块)
│ │ ├──api(提供调用接口的模块)
│ | | └── pom.xml
│ | ├──specific-service(实现具体性能的模块)
│ | | └── pom.xml
│ │ └── pom.xml
│ └── pom.xml
├── settings.xml (maven 的配置文件)
└── pom.xml
复制代码
构造划分:聚合模块,依赖模块,实现模块
聚合模块
作用:聚合用于疾速构建 maven 工程,一次性构建多个我的项目 / 模块。
罕用标签

<packaging>:这是聚合模块必须要有的标签内容为 pom,示意这是一个聚合模块(没有代码),用来治理多个模块。
<module>: 标签用于指定治理的那些模块, 不记录模块治理的模块(浅援用)。
<dependencyManagement>:申明依赖版本,子模块引入依赖无需写入版本号,对立治理依赖版本。
<pluginManagement>, 申明插件,治理插件配置,子项目间接继承,无需反复编写配置规定。
<properties> 申明变量,用于对变量的治理,如版本号,通过 ${}取值。

依赖模块和实现模块
如 api 和 common 只提供工具和性能反对的模块称其依赖模块,而实现模块则是一个用于打包部署运行的具体微服务模块。
依赖模块罕用标签

<dependency>:引入所需依赖
packaging: 须要把代码打包,个别为 jar

微服务模块罕用标签

<dependency>:引入所需依赖
packaging: 须要把代码打包,个别为 jar(放在 web 容器则为 war)
<build>: 对于一个 springboot 我的项目,具体微服务模块须要通过 build 指定一个构建形式,如指定 springboot-maven-plugin 来进行构建。

JVM 加载类
JVM 会依据程序加载 class 文件,如果全限定名反复,前面的 class 将会被疏忽。
如果存在同类名的 class 能够思考:

移除反复的 class
写类加载器来加载特定的 class
扭转 classpath 里的程序

springboot 生成 jar 构造目录
阿里的一些开发标准
【强制】禁止在子项目的 pom 依赖中呈现雷同的 GroupId,雷同的 ArtifactId,然而不同的 Version。
阐明:在本地调试时会应用各子项目指定的版本号,然而合并成一个 war,只能有一个版本号呈现在最初的 lib 目录 中。已经呈现过线下调试是正确的,公布到线上却出故障的先例。
【举荐】所有 pom 文件中的依赖申明放在语句块中,所有版本仲裁放在 语句块中。
阐明:里只是申明版本,并不实现引入,因而子项目须要显式的申明依赖,version 和 scope 都读取自父 pom。而所有申明在主 pom 的里的依赖都会主动引入,并默 认被所有的子项目继承。
【强制】二方库的新增或降级,放弃除性能点之外的其它 jar 包仲裁后果不变。如果有扭转,必须明确评 估和验证。
阐明:在降级时,进行 dependency:resolve 前后信息比对,如果仲裁后果齐全不统一,那么通过 dependency:tree 命 令,找出差别点,进行排除 jar 包。
【强制】依赖于一个二方库群时,必须定义一个对立的版本变量,防止版本号不统一。
阐明:依赖 springframework-core,-context,-beans,它们都是同一个版本,能够定义一个变量来保留版本:${spring.version},定义依赖的时候,援用该版本。
开发中遇见的问题
某些包没有打入生成 jar 中

在 idea 开发实现后,生成镜像上云中发现微服务 j 启动 ar 报错,找不某个类,查看 jar 包发现某个依赖未打入 jar 包,jar 为 spring-boot-configuration-processor, 检索引入中央,发现其引入作用域 <scope>option<scope>, 将其正文后,查看 idea 侧边 maven 也的确通过 common 引入,打包后仍然未打入 jar 包,将此依赖间接引入微服务打包后依赖引入胜利,但各个微服务都有引入,须要都复制一份有些麻烦,后将其从父工程根 pom 引入,打包后依赖引入胜利。只有正文掉作用域也不失效,其起因未知。
另一次就是 pom 引入本地 jar 包,但打包未增加 <includeSystemScope>true</includeSystemScope> 导致打包未打入 jar。

jar 包抵触

mybatis-plus-boot-starter 中引入了 jsqlparser 依赖,然而 mavenhelper 未检测到,idea 每次编译都会产生一个低版本的 jsqlparser, 因为开始不分明低版本的来处,导致节约了很多精力。
jsqlparser 与 pagehelper 版本不匹配导致,办法不存在报错,最终通过查问到一个匹配版本解决。

某些二次封装的包与以前包全限定类名统一并且接口内容不统一产生谬误

如对一些罕用依赖进行了二次封装,然而接口办法有差异,导致编译时呈现谬误。

莫名其妙的循环依赖问题:增加了依赖或者改了一点无关的代码,循环依赖报错就会呈现,按情理低版本 springboot 能够通过三级缓存来解决循环依赖,然而并不失效。

抱着疑难,我找到了程序员导师:Google 来求助,最终兜兜转转找到了 github 里 spring-framework 的一个 issue,提的就是这个问题:
github.com/spring-proj…
能够看到这个 issue 从 2016 年首次被提出,到 2019 年 reopen,实际上始终都没有找到过起因。issue 里好几个人遇到了和我一样的问题:一样的代码,在不同的环境上编译,进去的 jar 包有的能运行,有的却报错。
spring 的保护人员可能是感觉循环依赖不该当在程序中呈现,甚至目前 springboot2.6 版本曾经齐全不容许循环依赖了,所以对这个 issue 也就没有能源去解决。

退出移动版