【问题标题】:Why are project layout resources kept separate from Java sources?为什么项目布局资源与 Java 源代码分开?
【发布时间】:2011-01-28 16:20:57
【问题描述】:

为什么 Maven 将资源保存在与 Java 源代码不同的“源文件夹”中?

根据我的经验,在 Java 中,资源文件大多被视为 Java 源文件,当“编译”时,只需要按原样与类一起复制,最终打包到 jar 中,并由 classloader 的方法getResource/getResourceAsStream,通过classpath

我个人认为将资源文件与 Java 源分开是一种无用的复杂性。

  1. 你怎么看?
  2. maven 是否有充分的理由将资源与来源分开?
  3. 不使用src/main/resourcessrc/test/resources并保持 src/main/javasrc/test/java 中的资源使用 maven?

【问题讨论】:

  • 这是一个主观的个人意见问题。你喜欢一种方式,别人喜欢另一种方式。什么选择是最好的这个问题没有很好的答案。
  • @Jesper,我明白你的意思,但是对于这种情况,我认为可以回答这个问题;答案是最好使用 maven 约定。在许多其他情况下,遵循约定是在环境上下文中做事的最佳方式,即使可能以其他非标准方式来做。例如,linux config文件最好放在/etc,word docs最好放在My Documents,README文件最好放在顶层项目目录等
  • ...(空间不足,所以在这里继续评论)...我认为有理由认为遵循约定是“最好的”,除非有某种理由可以提供更多价值来打破标准.在这种情况下,根据经验,我同意@Nishant 在下面的回答,我想不出任何足够强大的理由来证明打破 maven 惯例是合理的。
  • @Dave:我认为这里的问题主要不是关于是否最好遵循他们的约定,而是更多关于他们为什么选择它作为约定而不是像传统的“更简单”的东西单一源文件夹。
  • 我同意最好遵循约定。但是在 maven 引入将资源放在一边的约定之前,约定是将它们与源文件一起保存。所以,如果最好遵循现有的约定,如果它没有带来任何优势,为什么 maven 引入一个新的?我认为在您的项目中拥有 4 个或更多源文件夹并不会使工作更轻松。 @ColinD:完全正确。

标签: java maven layout resources


【解决方案1】:

还没有提到的一点是,您显然习惯于看到其中只有 java 源代码的项目。但是,如果您添加一些其他源文件类型,我认为该组织更有意义,例如:

  • src/main
    • 资源
    • java
    • 时髦

每个子目录都有一个特定的文件分类:

  • java -> 编译为 java 文件的东西
  • groovy -> 编译为 groovy 脚本的东西
  • resources -> 用于任何用途的未编译数据...(也可以过滤这些数据以添加编译时信息)

另外(正如我已经在一些 cmets 中指出的那样),我通常不会将资源目录设为平面。文件可以嵌套到类似包的结构或其他适当的子目录中(根据我的组织感)。

【讨论】:

  • 如果我有一些特定于 java 代码的资源,而一些资源是特定于 groovy 代码的呢?
  • 不知道,我个人从来没有觉得需要对资源进行分段(它们通常已经被分成类似包的结构)。
  • 我同意你的看法 :) 感谢您的出色回答。
【解决方案2】:

为了简单和更容易访问,我们还在 java 源路径中保留了一些资源。这使我们在 GUI 级别上开发时很方便,因为速度模板等接近控制器 Java 代码。但是你必须通过在你的 pom 文件中添加类似这样的东西来告诉 maven 包含 src/main/java 中的非 java 内容。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1.1</version>
                <configuration>
                <webResources>
                    <resource>
                        <directory>src/main/java</directory>
                        <targetPath>WEB-INF/classes</targetPath>
                      <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                        <include>**/*.htm</include>
                        <include>**/*.html</include>
                        <include>**/*.css</include>
                        <include>**/*.js</include>
                      </includes>
                    </resource>
                    <resource>
                        <directory>src/main/resources</directory>
                      <excludes>
                        <exclude>**/log4j.xml</exclude>
                      </excludes>
                    </resource>
                </webResources>
                </configuration>
            </plugin>

【讨论】:

    【解决方案3】:

    你怎么看? 我认为 Maven 背后的人应该练习一点“关注点分离”。使用 Maven 的第一个原因是依赖管理,但为什么搅拌机要告诉我如何剥水果呢?这对我来说毫无意义。

    maven 是否有充分的理由将资源与来源分开? 不会。在java项目中,包结构的重复,不是一次,而是三次,给项目增加了不必要的工作,增加了出错的风险,降低了代码的敏捷性。

    【讨论】:

    • 我非常同意你的看法。我使用 maven 已经很多年了,我从来没有觉得将 java 类与其他可通过类路径访问的资源分开的优势。相反,我觉得将测试代码分开是正确的(因为我想从分发包、运行时类路径等中排除)
    【解决方案4】:

    我试着自己给出答案。

    你怎么看?

    这是一个无用的额外复杂性。因此是错误的:见下文。

    maven 是否有充分的理由将资源与来源分开?

    我可能想到的唯一原因是,对于某些平台来说,这可能是一个很好的做法。例如,在 Mac OSX 应用程序中,资源与二进制文件分开打包(在不同的子文件夹中)。

    我认为“外部”资源和配置文件,即那些没有被打包到最终工件(jar 文件)中的资源和配置文件可以有充分的理由与源文件分开。因此,当 maven 打包 jar 时,他知道不必包含这些文件,因为例如,我们希望它们在 jar 之外,以便允许用户将这些文件作为配置进行编辑。

    但是,对于打包在 jar 中的东西(如翻译字符串和 xml 元数据,如休眠映射和 spring 配置),没有充分的理由将它们放在单独的位置,除非我们期望我们的用户作为配置过程的一部分,在部署后手动更改它们。

    资源可以用与类相同的包结构来组织,所以如果你在包 x.y.z 中有一个类,你可能希望在同一个包中拥有它所依赖的资源,因为它们代表相同的“逻辑单元” ":在这种情况下,将它们放在两个单独的文件夹中会导致在包重构和重组期间需要额外的注意,因为您希望保持同步。 ClassLoader 还提供了在 getResourceAsStream() 方法中指定相对路径的可能性。所以如果你有类 x.y.z.MyClass,你可以做 getClass().getResourceAsStream("foo-bar.properties"),资源将从同一个包 "x.y.z" 加载。因此,当它们具有紧密依赖关系时,将它们保持在一起非常有用。

    对于需要在可部署工件中保持分离的外部资源,将它们保持分离是一件好事,但在这种情况下,我不明白为什么 maven 对待 src/ main/resources 作为“java源文件夹”(maven-eclipse-plugin),因为它们实际上不能在类路径中,而是通过文件系统作为普通文件访问,特别是你不希望这些文件包含在 jar 中在 Maven 构建期间。应用程序图标、您可能希望放在 /etc 目录中的配置文件等就是这种情况。

    总之,在我看来,maven 处理资源的方式有问题:如果它们是“外部”资源,那么 maven 就没有理由将它们打包到最终的工件(jar)中。如果它们不是“外部”,则没有理由将它们保存在单独的“源文件夹”中。

    不使用 src/main/resources 和 src/test/resources 并使用 maven 将资源保留在 src/main/java 和 src/test/java 中是否有任何反指示?

    我不知道。

    大部分时间我都使用默认的 maven 布局来处理资源,但有几次我没有;采取预防措施在 pom 中声明非标准资源目录位置(参见resourcessuper-pom)。 可以更改 maven 项目的目录结构,在 pom 中指定:

     <build>
        <directory>target</directory>
        <outputDirectory>target/classes</outputDirectory>
        <finalName>${artifactId}-${version}</finalName>
        <testOutputDirectory>target/test-classes</testOutputDirectory>
        <sourceDirectory>src/main/java</sourceDirectory>
        <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
        <testSourceDirectory>src/test/java</testSourceDirectory>
        <resources>
          <resource>
            <directory>src/main/resources</directory>
          </resource>
        </resources>
        <testResources>
          <testResource>
            <directory>src/test/resources</directory>
          </testResource>
        </testResources>
      </build>
    

    而且我没有发现它有什么特别的问题。

    我希望 maven 家伙重新考虑一下这个约定,因为具有更简单的项目结构有助于开发人员更多地专注于开发,而不是在浏览应用程序代码时在项目结构中的多个文件夹中查找内容。

    【讨论】:

      【解决方案5】:

      我区分两者的方式是 java 文件夹默认不允许在生成的 jar 中构建任何内容,除了我列出的文件类型(例如 *.class*.html*.properties,如果你'正在使用 Wicket),而 resources 中的所有内容都将复制到构建中,除了我列出的少数例外情况。

      当然,这只是我的私人约定,我自己遵守的。

      【讨论】:

        【解决方案6】:

        1) 你怎么看?

        我认为,这是一个很好的约定。

        2) maven 是否有充分的理由将资源与来源分开?

        是的。你遵守约定。获得项目代码访问权限的每个人都将知道在哪里查找代码以及在哪里查找其他所有内容。当然,测试用例在哪里。以及执行该插件时哪个文件会发生什么。

        3) 不使用 src/main/resources 和 src/test/resources 并使用 maven 将资源保留在 src/main/java 和 src/test/java 中是否有任何反指示?

        没有计数器指示。这是约定俗成的问题,您安排自己的代码的方式并没有错。事实上,如果您曾经创建过 Wicket 项目,您将看到您的 HTML 和您的 Java 代码并排保留在 Java 包中。但是,它只是 Wicket,您知道 HTML 将在 Java 文件旁边。但其他所有内容都在 resources 文件夹中。

        【讨论】:

        • 我认为您没有为问题 2 提供一个好的答案。问题本质上是“为什么 maven 使用单独的资源文件夹的约定很好”,而您基本上用“因为这是一个约定”。为什么是这样的约定,而不是其他可能(在某些人眼中)更简单的东西?
        • @ColinD 我从 ANT 世界来到 Maven 世界,我在 ANT 构建中看到了 10 种不同的约定。他们都有充分的理由来解释文件夹结构。这个想法是可理解的关注点分离。我早期在 ANT 中的一个项目有 core/java、core/ui、core/settings、staging/classes、staging/output、test/——这也很有意义。我只是不想通过提及关注点分离来深入争论,因为人们也会想出其他适合的模式。
        • 大约 2.,如果你不解释为什么它是好的,你不能说它是好的,只是“因为它是一个约定”。有了这样的答案,任何约定都是好的,因为它是约定:不管它好不好。
        • 那为什么不为xml、属性、图标、图片、xsd、sql、文本等设置单独的文件夹呢?如果我们不关心将 java 源代码与“其他所有内容”混合在一起,为什么我们不关心将属性文件与 xml 文件混合在一起呢?这会让事情变得更容易还是更难处理?
        • @Luigi:从技术上讲,根据标准目录布局,“配置”类型的文件可能应该放在src/main/config 下。无论如何,任何一种标准结构在某种程度上都是任意的......设计它的人想出了一组源文件进入的组,他们选择了他们认为合适的粒度级别.不是每个人都喜欢,但至少看的时候比较容易理解。
        【解决方案7】:

        也许我是这里唯一一个没有 Java 背景的人。我认为混合资源和代码非常混乱。随着项目的发展,关注点分离使您的项目更清晰,更易于理解/维护。

        我尝试将自己的目录约定应用于我的所有软件开发项目:

        • bin:用户使用的最终二进制文件
        • build:中间二进制文件,它们可能会在构建后被删除
        • lib:外部库
        • src:仅源代码(在本例中为 Java 源文件)
        • resources:所有资源(配置文件、xmls、图片、html等)
        • 文档:文档

        当我需要更改此目录结构时,我很少找到开发环境。

        使用 Maven,如果您想进行快速设置,请使用默认设置。如果你想使用你的目录约定,你只需要改变 pom.xml 中的设置,如前所述。但是请注意,一些 Maven 插件认为您使用默认 Maven 目录来存储资源/源/输出二进制文件,如果您更改这些目录,则需要调整一些插件设置。

        【讨论】:

        • 例如,为什么您认为 xml 不是“源代码”?同样的问题也可以用于 html 和(某种)配置文件。
        • XML 只是静态数据(至少它们是为此目的而创建的)。无法像任何编程语言那样执行分支和循环。 HTML 比较复杂;如果有嵌入的 javascript,它们处于临界状态。我从不将代码放在配置文件中,因为这会混合数据和代码。
        【解决方案8】:

        我认为代码/资源分离通常很有用:

        1. 包必须遵循 Java 规范规则,例如,不允许使用保留字,如 'int'。在源代码文件夹中包含非法包会令人困惑。

        2. 资源具有实例性质,类具有类性质。例如,您可能在同一个包中有 ContinentCountry 类,但资源更好地组织为树:

          • 非洲/
            • morocco.txt
            • kenya.txt
          • 欧洲/
            • 波兰.txt
        3. 在我看来,资源结构比代码结构更稳定。代码重构经常发生,类从一个包移动到另一个包以提高可读性。将资源转移到重构上的义务会阻止开发人员重构。

        4. 如果即使仅 1% 的资源文件也需要单独的资源文件夹,则将剩余的 99% 移动到该文件夹​​并仅将资源保留在一个位置是合理的。

        但是,在某些情况下,将资源和 Java 代码放在一起很有用。例如,单元测试比较输入和输出 - 很高兴将输入和输出文件与单元测试代码放在同一文件夹中。

        【讨论】:

          猜你喜欢
          • 2010-10-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多