使用Maven构建Java项目
如今很多Java项目应该都采用Maven进行构建, 其依赖管理, 配置式工作原理使得项目构建也变得比较灵活统一。
基础, Maven基于XML进行配置, 其配置文件叫pom.xml, 如
<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> <artifactId>module-name</artifactId> <packaging>jar</packaging> <version>pacakge version</version> <name>module-name</name> <url>http://maven.apache.org</url> <!-- jar包依赖管理--> <dependencies> <dependency> ... </dependency> </dependencies> <!-- 插件配置 --> <build> <plugins> ... </plugins> </build></project> 多模块开发
通常我们的项目都会有多个模块, 合理地拆分模块, 也会使得我们的项目更清晰, 更容易维护, 甚至使得模块间的依赖关系更简单, 如

这里有4个模块, 我们会将api(如user-api)及其实现(如user)分开, 这样做对以后要替换或扩展实现,或者暴露给外部, 甚至作服务化时都是有好处的,web模块依赖了user和webc模块, 那么我们会有一个root pom.xml作为整个项目的配置中心
<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.xxx.yyy</groupId> <artifactId>project-all</artifactId> <version>project-version</version> <!-- 子模块 --> <modules> <module>user-api</module> <module>user</module> <module>web</module> <module>webc</module> </modules> <packaging>pom</packaging> <!-- root模块packaging为pom类型 --> <name>project-all</name> <url>http://maven.apache.org</url> <!-- 项目属性, 如依赖版本, 一些插件属性配置 --> <properties> ... </properties> <!-- 项目依赖包管理, 子项目只需依赖对应dependency的groupId, artifactId --> <dependencyManagement> <dependencies> ... </dependencies> </dependencyManagement> <!-- 项目插件管理 --> <build> <pluginManagement> ... </pluginManagement> </build> <!-- 远程仓库 --> <repositories> <repository> ... </repository> </repositories> <!-- 其他, 如发布仓库等 --></project> 而对应的某个子模块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"> <parent> <artifactId>project-all</artifactId> <groupId>com.xxx.yyy</groupId> <version>project-version</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>module1</artifactId> <packaging>war</packaging> <!-- module为web项目 --> <name>module1</name> <url>http://maven.apache.org</url> <!-- 来自parent模块里的dependencyManagement中的依赖包配置 --> <dependencies> ... </dependencies> <build> <!-- 来自parent模块里的pluginManagement中的插件配置 --> <plugins> <plugin> ... </plugin> </plugins> <!-- 环境配置, 这将实现切换环境的特性 --> <profiles> ... </profiles> <!-- 其他配置 --> ... </build></project> 不同环境切换
项目开发中, 我们可能会有很多不同的环境配置, 如DB源, 服务器host等, 我们不可能每次发布都去手动更改配置, 这既繁琐又容易出错, 那基于Maven我们怎么优雅地来切换不同环境呢?如上面所说, Maven可通过profile机制和filtering机制实现环境切换, 如:
<build> <finalName>web</finalName> <!-- war包名 --> <resources> <resource> <!-- 资源目录 --> <directory>src/main/resources</directory> <!-- 开启filtering机制 --> <filtering>true</filtering> </resource> </resources></build><profiles> <!-- 默认开发环境 --> <profile> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <filters> <!-- 使用dev.properties --> <filter>src/main/filter/dev.properties</filter> </filters> </build> </profile> <!-- 测试环境 --> <profile> <id>test</id> <build> <filters> <!-- 使用test.properties --> <filter>src/main/filter/test.properties</filter> </filters> </build> </profile></profiles> 项目结构如下

对应的配置文件
# dev.propertiesenv=devprops.one=123props.two=456 # test.properties:env=testprops.one=123abcprops.two=456abc # app.properties:env=${env}props.one=${props.one}props.two=${props.two} 这样我们就可以不同环境使用不同的profile进行编译打包了:
mvn clean package -Pdevmvn clean package -Ptest 通过这种方式我们就可以灵活的切换不同的环境, 甚至可以在不同的环境切换不同的实现等。
若properties文件中有中文值出现,则会出现乱码,猜测maven-resource-plugin读取properties文件时采用的是二进制流的方式,查阅后发现该插件的PropertiesUtil.loadPropertyFile()方法中确实使用FileInputStream读取properties。要解决中文乱码,可以将中文值进行Unicode转码,或者使用Reader(如InputStreamReader)读取properties文件,则需修改插件源码。
使Spring感知到当前环境
既然我们已经能通过maven编译打包不同的环境配置了, 那要怎么告知Spring当前是什么环境呢?在Spring ApplicationContext对象中有一个环境配置属性environment
/** Environment used by this context; initialized by [email protected] #createEnvironment()} */private ConfigurableEnvironment environment;// 该环境配置对象通过属性spring.profiles.active来进行环境配置/*** Name of property to set to specify active profiles: [email protected]}. Value may be comma* delimited.* Note that certain shell environments such as Bash disallow the use of the period* character in variable names. Assuming that Spring's [email protected] SystemEnvironmentPropertySource}* is in use, this property may be specified as an environment variable as* [email protected] SPRING_PROFILES_ACTIVE}.* @see ConfigurableEnvironment#setActiveProfiles*/public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";<!--若对于Web应用, 我们当然可以在初始化Spring根容器时指定该属性值, 即在web.xml中指定:--><context-param> <param-name>spring.profiles.active</param-name> <param-value>${env}</param-value> <!-- 该值会在maven打包时替换掉 --></context-param><!-- Spring Root Context --><listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener> // 当然, 你也可以通过Java系统属性来设置System.setProperty("spring.profiles.active", "dev"); <!--于是你就可以在Spring的配置文件里, 在不同的环境中匹配不同的配置了--><beans profile="dev, test"> ...</beans><beans profile="prod"> ...</beans> 如今, 除了Maven外, 有了更加灵活强大的构建工具Gradle, Spring已经采用了其进行构建, 其通过Groovy动态语言及编程式构建项目, 不妨也可以实践实践。