简体   繁体   中英

Using a spring-beans .xml file (in 'resources' folder) from a shared module with gradle multi module (subproject)

(To assist with this question, I have github links at the bottom of this question)

I have (and am "starting with" the below gradle multiple module/project)

settings.gradle

rootProject.name = 'com.mycompany.myteam.myapp-rootProjectName'

include ':source:java:myapproot:myapp-toplayer-console-di-xml-one'
include ':source:java:myapproot:myapp-business-logic'
(maybe even a few that are purely .java/.class "code logic" libraries)

In the subproject:

':source:java:myapproot:myapp-toplayer-console-di-xml-one'

I have the typical Spring-Boot startup and a spring-bean setup using applicationContext.xml.

./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/java/demo/SpringBootApplicationContextXmlConsoleApplication.java
--
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/resources/applicationContext.xml
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/resources/orch.one.di.xml
./source/java/myapproot/myapp-toplayer-console-di-xml-one/src/main/resources/clientporxy.one.di.xml

The contents of "applicationContext.xml" are below. Note, it has a "pointer" 'import' entries to some other.xml files.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


    <import resource="orch.one.di.xml"/>

    <import resource="clientproxy.one.di.xml"/>

</beans>

Everything above works just fine. I've been doing the above approach for years and for many different projects.

============================================================

What I am trying to do is put the 2 "extra" *.di.xml files in another gradle module.

settings.gradle

rootProject.name = 'com.mycompany.myteam.myapp-rootProjectName'

include ':source:java:myapproot:myapp-toplayer-console-di-xml-one'
include ':source:java:myapproot:myapp-business-logic'

    (new one below)
include ':source:java:myapproot:myapp-shared-resxex'

And I want to move the *.di.xml files to this different/new gradle-subproject/module. (':source:java:myapproot:myapp-shared-resxex' to be clear)

So now I have moved the two *.di.xml files.

File location is here: (under "resources")

./source/java/myapproot/myapp-shared-resxex/src/main/resources/orch.one.di.xml
./source/java/myapproot/myapp-shared-resxex/src/main/resources/clientproxy.one.di.xml

So now when I start the "main" application, I get errors like below (and I kinda expected these errors)

Caused by: java.io.FileNotFoundException: 

    ./source/java/myapproot/myapp-toplayer-console-di-xml-one/build/resources/main/clientproxy.one.di.xml

    (No such file or directory)
    at java.base/java.io.FileInputStream.open0(Native Method)
    at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
    at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86)
    at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:184)
    at org.springframework.core.io.UrlResource.getInputStream(UrlResource.java:187)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:333)

I have "tried" (and worked through) things like these:

https://docs.gradle.org/current/userguide/cross_project_publications.html

Gradle: common resource dependency for multiple java projects

How module can use resources of another module using gradle multi-module

https://discuss.gradle.org/t/how-to-bundle-static-resources-from-other-subproject-into-java-application/42076

Like many days and many hours been trying to figure out the magic-sauce.

So, my "in general" question is

How can I get a spring bean.xml file to work from a different gradle-subproject/module?

I will show my latest attempt:

./source/java/myapproot/myapp-shared-resxex/build.gradle

configurations {
    myCoolConfigurationName
}

task tryToPackageStuffForOtherSubprojectsTask(type: Jar) {
    archiveClassifier
    from sourceSets.main.resources
}

artifacts {
    myCoolConfigurationName tryToPackageStuffForOtherSubprojectsTask
}

dependencies {

    implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-domain')
    implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-interfaces')
    implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-concrete')


}

and then:

./source/java/myapproot/myapp-toplayer-console-di-xml-one/build.gradle

plugins {
    id "application"
}

apply plugin : "java"

ext {
    javaMainClass = "demo.SpringBootApplicationContextXmlConsoleApplication"
}

application {
    mainClassName = javaMainClass
}


task myCustomPrintClasspathTask  {
    doLast {
        configurations.runtimeClasspath.each { println 'helloThere->' + it }
    }
}


dependencies {

    //the below line, if left not-commented-out, will produce a warning : "Execution optimizations have been disabled for task"
    implementation project(path: ':source:java:myapproot:myapp-shared-resxex', configuration: 'myCoolConfigurationName')


    implementation(group: 'org.springframework.boot', name: 'spring-boot-starter', version: springBootVersion) {
        exclude module: "logback-classic"
    }

    implementation(group: 'org.springframework.boot', name: 'spring-boot-autoconfigure', version: springBootVersion) {
        exclude module: "logback-classic"
    }

    implementation project(":source:java:myapproot:myapp-shared-resxex")
    implementation project(':source:java:myapproot:myapp-business-logic')

    implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-domain')
    implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-interfaces')
    implementation project(':source:java:myapproot:clientproxies:myclientproxy-root:myclientproxy-concrete')


    implementation group: 'org.apache.commons', name: 'commons-lang3', version: commonsLangVersion
    implementation group: 'javax.inject', name: 'javax.inject', version: "${javaxInjectVersion}"

    implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
    implementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4jSimpleVersion

    testImplementation group: 'junit', name: 'junit', version: junitVersion
    testImplementation group: 'org.mockito', name: 'mockito-core', version: mockitoVersion

}

When I run (a clean build and then) the custom task:

$ ./gradlew clean build
$ ./gradlew myCustomPrintClasspathTask

I get the below output:

> Task :source:java:myapproot:myapp-toplayer-console-di-xml-one:myCustomPrintClasspathTask

helloThere->./source/java/myapproot/myapp-shared-resxex/build/libs/myapp-shared-resxex.jar

helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter/2.7.5/c28e1546461803490588085345ba5d2897d232bc/spring-boot-starter-2.7.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/2.7.5/96646e63a2296d0a3209383e81cdb8c87ab2f913/spring-boot-autoconfigure-2.7.5.jar
helloThere->./source/java/myapproot/myapp-business-logic/build/libs/myapp-business-logic.jar
helloThere->./source/java/myapproot/clientproxies/myclientproxy-root/myclientproxy-concrete/build/libs/myclientproxy-concrete.jar
helloThere->./source/java/myapproot/clientproxies/myclientproxy-root/myclientproxy-interfaces/build/libs/myclientproxy-interfaces.jar
helloThere->./source/java/myapproot/clientproxies/myclientproxy-root/myclientproxy-domain/build/libs/myclientproxy-domain.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.12.0/c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e/commons-lang3-3.12.0.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-simple/1.7.36/a41f9cfe6faafb2eb83a1c7dd2d0dfd844e2a936/slf4j-simple-1.7.36.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-logging/2.7.5/61f4c53e35baa31a269bbeb7bb9d5e781448feef/spring-boot-starter-logging-2.7.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-to-slf4j/2.17.2/17dd0fae2747d9a28c67bc9534108823d2376b46/log4j-to-slf4j-2.17.2.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.slf4j/jul-to-slf4j/1.7.36/ed46d81cef9c412a88caef405b58f93a678ff2ca/jul-to-slf4j-1.7.36.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.36/6c62681a2f655b49963a5983b8b0950a6120ae14/slf4j-api-1.7.36.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/2.7.5/fd04e228e6e21b7ad13c10ae29afd31868d842e5/spring-boot-2.7.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/jakarta.annotation/jakarta.annotation-api/1.3.5/59eb84ee0d616332ff44aba065f3888cf002cd2d/jakarta.annotation-api-1.3.5.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/5.3.23/530b36b2ce2c9e471c6a260c3f181bcd20325a58/spring-context-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/5.3.23/30d0034ba29178e98781d85f51a7eb709a628e9b/spring-aop-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/5.3.23/3bdefbf6042ed742cbe16f27d2d14cca9096a606/spring-beans-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/5.3.23/3a676bf4b9bc42bd37ab5ad264acb6ceb63397a2/spring-expression-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/5.3.23/91407dc1106ea423c44150f3da1a0b4f8e25e5ca/spring-core-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.yaml/snakeyaml/1.30/8fde7fe2586328ac3c68db92045e1c8759125000/snakeyaml-1.30.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jcl/5.3.23/3c7eb5fcca67b611065f73ff4325e398f8b051a3/spring-jcl-5.3.23.jar
helloThere->/Users/MyDomainUserName/.gradle/caches/modules-2/files-2.1/org.apache.logging.log4j/log4j-api/2.17.2/f42d6afa111b4dec5d2aea0fe2197240749a4ea6/log4j-api-2.17.2.jar

If I "peek" into the jar:

./source/java/myapproot/myapp-shared-resxex/build/libs/myapp-shared-resxex.jar

I do see the two *.di.xml files.

and then if I run:

$ ./gradlew run

I continue to get:

Caused by: java.io.FileNotFoundException: ./source/java/myapproot/myapp-toplayer-console-di-xml-one/build/resources/main/clientproxy.one.di.xml (No such file or directory)
        at java.base/java.io.FileInputStream.open0(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
        at java.base/sun.net.www.protocol.file.FileURLConnection.connect(FileURLConnection.java:86)
        at java.base/sun.net.www.protocol.file.FileURLConnection.getInputStream(FileURLConnection.java:184)
        at org.springframework.core.io.UrlResource.getInputStream(UrlResource.java:187)
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:333)
        ... 32 more

My./gradle/wrapper/gradle-wrapper.properties file (for completeness and to know my gradle-version)

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

.....

Github repo with the code.

https://github.com/granadacoder/gradle-multi-with-spring-bean-proof

"main" branch...has a working spring-bean di.xml setup. "main" branch relates to my comment above "everything above works just fine".

I have a second branch (and a PR so you can see the "diff").

branch:

https://github.com/granadacoder/gradle-multi-with-spring-bean-proof/tree/feature/move-spring-bean-xml-files-try-1

and PR (from the feature branch to the main branch to show the "diff")

https://github.com/granadacoder/gradle-multi-with-spring-bean-proof/pull/2/files

Ok.

I found an "old" documentation page.

https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch04s07.html

I'll post the quote from the above URL, in case it "disappears".. especially since it is "old" documentation.

//START ARTICLE QUOTE

4.7.2.2 The classpath*: prefix When constructing an XML-based application context, a location string may use the special classpath*: prefix:

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml"); This special prefix specifies that all classpath resources that match the given name must be obtained (internally, this essentially happens via a ClassLoader.getResources(...) call), and then merged to form the final application context definition.

[Note] Classpath*: portability The wildcard classpath relies on the getResources() method of the underlying classloader. As most application servers nowadays supply their own classloader implementation, the behavior might differ especially when dealing with jar files. A simple test to check if classpath* works is to use the classloader to load a file from within a jar on the classpath: getClass().getClassLoader().getResources(""). Try this test with files that have the same name but are placed inside two different locations. In case an inappropriate result is returned, check the application server documentation for settings that might affect the classloader behavior.

The "classpath*:" prefix can also be combined with a PathMatcher pattern in the rest of the location path, for example "classpath*:META-INF/*-beans.xml". In this case, the resolution strategy is fairly simple: a ClassLoader.getResources() call is used on the last non-wildcard path segment to get all the matching resources in the class loader hierarchy, and then off each resource the same PathMatcher resoltion strategy described above is used for the wildcard subpath.

//END ARTICLE QUOTE

So I had forgotten about the "string-magic" of using classpath (before file-names, etc).

I was able to update my applicationContext.xml file to the below.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">


    <!-- below are "imports" to OTHER (spring-bean) di.xml files -->

    <import resource="classpath:clientproxy.one.di.xml"/>

    <import resource="classpath*:orchestrators.one.di.xml"/>



</beans>

You can read about the difference between using the "*" (after classpath) in the below SOF question/answers.

Spring classpath prefix difference

For clarity....all I did was "move" the two di.xml files to the "other" gradle/module-subproject. I did not use any of the "myCoolConfigurationName" stuff I showed in my original question... as one of my "tries".

"so simple"........I was happy and in disbelief with my days of struggle with this.....

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM