maven使用
如何使用Maven
Maven
Maven的作用
Maven
最主要的两个作用
- 项目构建
- 项目的依赖管理
Maven, Make, Ant的对比
这三个都可以用来构建java
代码, 它们之间的对比如下
编程范式 | 依赖管理 | 跨平台 | |
---|---|---|---|
Make |
命令式 | 不支持 | 不支持 |
Ant |
命令式 | 不支持 | 支持 |
Maven |
声明式 | 支持 | 支持 |
Make
是命令式的, 需要自己写明先编译什么, 再编译什么, 这样比较麻烦. 同时Make
调用的是操作系统的命令, 例如cp
, mv
等等, 在Linux
和Windows
是不通用的.
Ant
用java
实现了操作系统的某些指令, 让Ant
可以跨平台使用
创建Maven项目
只需要使用下面的命令即可
1 | mvn archetype:generate "-DgroupId=com.caicai.maven" "-DartifactId=maven-test" "-DarchetypeArtifactId=maven-archetype-quickstart" "-DinteractiveMode=false" |
这个命令会形成一个这样的目录结构
也可以自己手动创建创建目录和pom.xml
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
所以说这个命令其实并没有做什么特殊操作, 就创建了一些目录, pom.xml
和简单的java
文件(这个不是必须的). 只要有这样的目录结构和pom.xml
, 就可以把一个项目称为maven
项目了
如何打包可运行的jar包
我们首先需要了解一下jar
命令, jar
命令其实就是一个打包压缩命令, 像zip
一样. 只不过会多生成一个META-INF/MANIFEST.MF
, 用于描述class
. jar
打包就是class
文件, 其实也可以自己用zip
命令打包class, 然后创建MANIFEST
文件
1 | jar -c[m]f [manifest.mf文件] "jar包的名称" 要打包的目录 |
不可运行的jar
的MANIFEST.MF
可运行的jar
的MANIFEST.MF
运行jar包
1 | java -jar jar包 |
Maven打包成可执行的jar包
上面我们编写的可执行java
程序没有依赖别的jar
包, 用的都是java
自己的类. 如果要使用别的jar
包, 需要自己把jar
包复制出来, 然后在MANIFEST.MF
中指定类路径. 这是不使用maven
的手动方式, 如果依赖的jar
包比较多, 自己就需要一个一个复制, 比较麻烦
Maven
可以配置构建插件, 自动复制. 如果不配置构建插件, 依赖的jar
包, 不会出现在打包的jar
文件中
配置插件, 会生成两个jar
, 一个是带original
, 一个是不带的. 带的不包含自己依赖的jar
包, 一般是提供给别人用的, 别人的maven
会自动下载这些依赖. 不带的, 放入了解压后的jar
包, jar
包依赖的jar
包也会被放入, 是可以运行的
1 | <build> |
Maven指定java版本
为什么Maven
需要指定java
版本呢?
我们已经安装了jdk
, 也指定了jdk
中javac
的path
, 这里就已经确定了java
的版本, 为什么还有指定java
的版本呢? 其中的原因就是高版本的java
可以编译出低版本的class
文件, 而Maven
默认使用的是jdk1.5
, 这就意味着, 写的代码不能使用jdk1.5
之后的新的特性, 例如jdk1.8
的lambda`表达式
利用高版本jdk
编译低版本class
的方式如下
1 | javac.exe -bootclasspath rt.jar -source 1.8 -target 1.8 HelloWorld.java |
Maven指定java版本
1 | <properties> |
Maven中scope=provided
provided
的两种应用场景
servlet-api
: 编译时需要, 但运行时也需要, 但你不应该提供, 应该用别人已经提供好的, 否则就会产生冲突lombok
: 自己编译时需要, 别人用你的代码时编译时, 不需要. 生命周期在自己的代码编译成class后, 就已经结束了, 代码中的import
已经被移除掉了
和optional=true的区别
大概意思是说,在A项目依赖B项目提供了一些特性,但又不想让这些特性默认提供,而是作为可以选择的附加功能,默认不提供,需要声明后(主动添加B项目的依赖)才生效,这时用optional;而对于provided,文档侧重提到了
运行环境
概念,强调只在编译时存在,而运行时不存在的依赖,也就是说,provided的主要用途不是为了考虑依赖是否传递,而是要看项目运行时是不是不应该有这个依赖(是不是需要jvm或者运行容器提供)。经常拿scope=provided来举例的经典场景之一,就是
servlet-api
这个依赖了,在代码coding阶段需要使用到它的一些api,而在实际运行时,它的作用要由具体的运行容器来实现,因此编译时可以有它,而打成war包放到tomcat环境下运行时,war包里面不应该有这个servlet-api.jar
,否则就会报错了。在实际的spring-boot项目中,由于大部分使用了内置的
undertow
或者tomcat
容器,已经不需要特别声明这个jar的provided属性了。事实上,日常中更经常需要被用到的,应该主要就是这个optinal了,比如你要提供一组基础jar包,供项目组中的其他同事在他们的项目中引入依赖使用时,如果你提供的某些依赖了其他jar包来做的功能并不一定会被使用到,便可以用到这个optinal了。特别是用到诸如@ConditionalOnClass
这种检测项目中是否存在某个class的判断条件时,更是用optional的好时机。而
provided
的使用场景,除了servlet-api
,lombok
也很适合:A项目使用lombok做了一些代码生成,完成开发需要deploy到私服仓库之前,记得要将lombok的依赖加上<scope>provided</scope>
,因为它的作用周期已经在A项目打包完成时结束了,对于依赖A项目的其他项目,不需要用到lombok这个玩意儿,它们需要的是A项目提供的功能,而不是附带的帮助自己生成代码的额外功能;也不应该用optional
,因为没什么好选择的,它并不是A项目提供的可选功能之一。
其实最好不要使用optional
, 创建一个新的maven
项目提供另一种功能更好, 对于使用者更加友好
注意事项
- 如果使用了
maven
来管理项目依赖, 就不要再自己手动添加jar
包了. 如果不进行配置,maven
看不见你加的jar
包的,maven
应该只能看到pom
里面的依赖 - 从
Maven
仓库下载的jar
包, 只会包含自身的class
, 不会包含它依赖的jar
包, 还会在同级目录的``pom.xml里面, 会写明自己的依赖, 这样可以让
Maven不用每次都下载全量文件, 可以利用之前下载好的
jar`包 - 一个
maven
项目中每个jar
包只能出现一个版本, 毕竟java
只会找类路径, 根本没有版本的概念 Maven
跳过测试mvn clean package -Dmaven.test.skip=true
问题
如何将本地的
jar
包加入maven
的本地仓库呢?做不到, 除非自己用源码, 然后再
maven install
. 只有一个jar
包是缺少很多东西的为什么
spring-core
会被maven
的dependency:analyze
分析为Unused declared dependencies found
这个用
lombok
可以解释, 字节码中并没有Lombok
, 只是源代码中有, 而maven
分析的只是字节码, 所以Maven
会认为没有使用Lombok
, 下面是Maven
官网的一段描述, 可以印证我这个猜想By default, maven-dependency-analyzer is used to perform the analysis, with limitations due to the fact that it works at bytecode level, but any analyzer can be plugged in through
analyzer
parameter.那为什么
Lombok
的scope
是provided
, 而spring-core
的scope
是compile
呢? 这个我猜是因为运行时, 也要用到spring-core
,Lombok
运行时是用不到的, 所以,spring-core
是编译时需要, 运行时也需要, 那就只能是compile
了为什么
Lombok
可以在编译的时候生成代码呢?
这个是用到java
自己的一些特性, 只有实现AbstractProcessor
, 就可以在编译时, 运行一些代码来操作编译时存在的注解了. 这也说明了编译时注解的作用