为什么呢
很多时候,我在日常工作中试图解决一些基本或复杂的Maven / Java EE项目结构问题。 为了提供解决方案,我经常最终会尝试项目结构,测试我在不同应用程序服务器上的部署并细化我的配置。
Maven对于新来者来说可能会有陡峭的学习曲线,如果您将现代Java EE应用程序的“ 有时 ”复杂的配置要求添加进来,事情会变得更加令人沮丧。 在我的职业生涯中,我还看到很多初级Java开发人员 ,当他们加入一个大团队或一个项目时,大多数情况下,项目结构已经由高级成员细化并配置。 他们认为这是可行的,并且没有花费时间来了解接线和配置。 我过去自己犯过这个错误。 他们被分配了简单的编码任务,并且深入研究了需求,但不幸的是,他们忘记了研究应用程序的结构,而他们的高级同事也常常由于时间限制而忘记在此特定领域进行培训。 当人们在没有经验的情况下开始尝试弄乱应用程序的结构而试图使其正常工作时,可能会导致事故。 Maven及其约定旨在为建立通用的结构和约定提供有关项目结构的帮助,但是您再次需要了解工具的约定,然后掌握您的“ 配置 ”。
您经常会听到有人说“ 我在这里添加了这个库,它起作用了 ”,如果您回复“在那定义 ”,那么您可能会得到一些有趣的答案。 有时偶然或偶然的情况下它会起作用,但是在复杂的多模块应用程序中,大多数情况下“ 它只是起作用 ”被轻描淡写,问题很快就会开始出现。
本系列文章主要针对Maven和Java EE新手 ,但如果您是高级开发人员,可以随时分享或用作演示。 我将在演示的基础上“攻击”我在日常工作中发现的一些实际问题,并尝试在提供基本解释或相关资源链接的同时提供解决方案。 请欢迎为可以以更简洁的方式执行/完成的操作添加评论,更正或参考。 学习Maven并创建“复杂”但易于维护的应用程序的最佳方法是从头开始创建空的pom文件。
我要传达给初级开发人员阅读我的文章的主要信息是, “ 研究”您的应用程序结构,询问底层的构建工具是您工作的一部分,并且您永远不要以为其他方法总是会照顾您它 。 这也是挑战更艰巨的任务并提高您作为Java开发人员的技能的一步。
使用的核心技术
- 基于Java EE 7的应用程序
- 将包装为EAR
- 将具有多个组件(wars,jars,ejb jars)
- 将针对Java 7进行编译
- 将使用Maven 3打包
我的演示耳应用
我的应用程序将是EAR ,对于这个特定的职位,该耳朵将包括2个顶级模块war和ejb-jar。 也将有一个jar,其中将包含将成为我的数据库域模型(JPA实体)的类。 我将扩展结构,在以后的帖子中添加更多资源。 一个非常抽象的图像,仅用于提供一个想法,说明将“包含在我们的耳朵中”。 未来的war模块将包含servlet或jsf组件,services模块将包含一组常见的无状态Sesson Bean(或消息驱动的Bean)。 域项目将具有用JPA 2构造正确注释的普通Java类。
使用Maven构成我们应用程序的基本结构
为了让您耳目一新,我们需要使用Maven来定义模块和应用程序的各个部分,而Maven仍然是我们的构建/打包/配置工具。 这是最重要的步骤之一,如果您从一开始就掌握了这一步骤,那么其余的将只是简单的技术或配置细节。 我要提出的并不是最终的解决方案,而是与标准非常接近的标准,这在大多数情况下是启动新应用程序的“ 路要走 ”,因此在这里没有有趣的细节,让我们遵循标准并开始构建新的应用程序。具体的基础。
因此,让我们现在忘记上面的图片,让我们考虑一下Maven,可以定义什么模块和多少模块,如何互连它们以及定义依赖项。 注意,我建议的工作方式是标准的,但不是最终的解决方案,这意味着您可以通过将应用程序包装成耳朵,定义更少的模块和依赖项来获得相同的结果。 假设我想涵盖高度复杂的结构,那么我将始终遵循标准来定义通用结构。
我假设您已经涵盖了Maven的一些基本知识,并且至少熟悉该术语。 如果没有在这里看看。
请记住,Maven即将按照正确定义的结构将文件放置在正确的位置,并定义maven插件,maven插件是用于完成特定工作的某种工具,包括编译,打包,复制文件等。 Maven,所以再次需要将插件定义到正确的位置并使用适当的配置。 您不是在编写make或ant脚本,而只是“插入”插件并要求maven以明确定义的顺序执行它们。
作为我的好前同事(最近写了一封电子邮件), 打破生活和编码惯例是件好事,但绝不能使用Maven 。 他是对的!
如果不确定如何安装Maven,请在此处查看Windows或Mac
我的Maven项目结构–摘要
我们正在使用Maven进行构建,因此我们需要考虑Maven pom和模块。 为了制作出所需的耳朵包装(见上文),我们需要5 poms
- pom – 充当父母
- 包含/定义最终耳朵的pom –负责配置最终程序包。
- 一个包含/定义Web应用程序代码的pom,即我们的.war
- 一个pom,它将包含/定义ejb-module的代码,ejb-module是我们将打包EJB的模块
- 一个pom,它将包含将成为我们的JPA(数据库实体)的类
如您所见,每个模块都有自己的pom,并且有父级,由于项目很小,假设他们不需要它,那么很多人都不会在其结构中添加其中之一。过了一会儿,当添加更多模块时,您最终将遭受重创。 因此,请在此处记下“ parent pom非常好拥有并配置 ”。 这是pom,您可以在其中定义所有依赖项版本(即库)并配置maven插件,以便所有子pom都继承通用配置。
我的Maven项目结构-父pom
正如我已经详细说明的那样,我们将从头开始,因此我将创建一个名为“ sample-parent ”的新文件夹,并在该文件夹中添加一个名为“ 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>gr.javapapo</groupId>
<artifactId>sample-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
</project>
是的,不要激动,只需在定义“ pom ”的包装元素上注明。父级称为parent,因为它“定义”并管理子级模块,这是在模块定义部分完成的。 我们原来的pom变成了这样。 这意味着我们必须在sample-parent下创建相关文件夹,然后将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>gr.javapapo</groupId>
<artifactId>sample-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>sample-ear</module>
<module>sample-web</module>
<module>sample-services</module>
<module>sample-domain</module>
</modules>
</project>
让我们继续添加更多配置...
这是重要的部分,因为我们为
- 我们将要使用和配置的Maven插件
- 任何库–使用的依赖项以及其他模块的引用
- 其他通用属性,例如我们将要编译的Java运行时的版本
- 源文件或其他资产的默认编码。
<properties>
<!-- encoding-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--java version -->
<java-version>1.7</java-version>
<!-- plugin versions -->
<ejb-plugin-version>2.3</ejb-plugin-version>
<war-plugin-version>2.4</war-plugin-version>
<ear-plugin-version>2.9</ear-plugin-version>
<compiler-plugin-version>3.1</compiler-plugin-version>
<!-- dependency versions -->
<javaee-api-version>7.0</javaee-api-version>
<!-- EJB spec version -->
<ejb-spec-version>3.2</ejb-spec-version>
</properties>
让我们在属性部分之后添加另一个重要的部分,即dependencyManagement 。在这里,我们将定义可以在我们的应用程序模块中潜在使用的依赖关系及其版本。 在本节中,我们实际上关心版本,依赖项的包含或排除取决于子pom(这意味着它们不会自动添加)以及它们的范围。 因此, DependencyManagement部分是要控制的部分,版本集中在一处。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>${javaee-api-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
父pom中的另一个最后但重要的部分与dependencyManagemt相似,称为pluginManagement ,是我们将在其中定义,将在我们的应用程序配置和打包期间参考和使用的所有maven插件的版本和通用配置的部分。在下面的示例中,我定义了最基本的编译器插件之一,但是我当然需要更多!
<!-- Plugin management -->
<build>
<pluginManagement>
<plugins>
<!-- compiler plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin-version}</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
让我们添加并配置一些稍后将要使用的插件。在“插件管理”部分中添加它们。 我们定义了将要编译并打包我们的ejb的ejb插件以及将要打包我们的war的war插件。
<!-- ejb plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>${ejb-plugin-version}</version>
<configuration>
<ejbVersion>${ejb-spec-version}</ejbVersion>
</configuration>
</plugin>
<!-- war plugin -skinny wars mode! -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${war-plugin-version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
<webResources>
<resource>
<filtering>true</filtering>
<directory>src/main/webapp</directory>
<includes>
<include>**/web.xml</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
那是现在
您可以在此处下载我们的最小样本(标记post1,bitbucket)。 当时,我们似乎还没有完成任何工作,但是最终定义一个干净而具体的父pom将成为我们在即将发布的帖子中将要进行的其余工作的基础。
学习要点
- Maven标准布局
- 父pom
- DependencyManagement和pluginManagement的重要性