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
analyzerparameter.那为什么
Lombok的scope是provided, 而spring-core的scope是compile呢? 这个我猜是因为运行时, 也要用到spring-core,Lombok运行时是用不到的, 所以,spring-core是编译时需要, 运行时也需要, 那就只能是compile了为什么
Lombok可以在编译的时候生成代码呢?
这个是用到java自己的一些特性, 只有实现AbstractProcessor, 就可以在编译时, 运行一些代码来操作编译时存在的注解了. 这也说明了编译时注解的作用