简体   繁体   中英

How to add static files to jar using Gradle build in Spring Boot + Angular 2 project

I have a Spring Boot + Angular 2 project. I want to deploy it to Heroku. I'm able to run the npm build then copy the generated files over to the public folder (src/resources/public) manually, then run the backend build. What I want to do is to set up a gradle build that will do all of that at once. What I have so far is a gradle build that will build the front end, build the backend, however it does not copy the static files before generating the jar. Since the jar does not contain said static files, it won't work on Heroku.

Here's the project folder structure:

root
 backend
  src/main/java
  src/main/resources
 frontend
  --> angular files go here
 build/libs -> where the JAR file goes

The gradle build file:

buildscript {
repositories {
    mavenCentral()
}
dependencies {
    // spring
    classpath('org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE')
    classpath('org.springframework:springloaded:1.2.6.RELEASE')
    }
}

plugins {
    id "com.moowork.node" version "1.2.0"
}

// gradle wrapper
task wrapper(type: Wrapper) {
    gradleVersion = '3.4'
}

// configure gradle-node-plugin
node {
    version = '8.1.4'
    npmVersion = '5.0.3'
    download = true
    workDir = file("${project.projectDir}/node")
    nodeModulesDir = file("${project.projectDir}/")
}

// clean node/node_modules/dist
task npmClean(type: Delete) {
    final def webDir = "${rootDir}/frontend"
    delete "${webDir}/node"
    delete "${webDir}/node_modules"
    delete "${webDir}/dist"
    delete "${webDir}/coverage"
    delete "${rootDir}/backend/src/main/resources/public"
}

// clean task for npm

task copyFiles {
    doLast {
        copy {
            from "${rootDir}/frontend/dist"
            into "${rootDir}/backend/src/main/resources/public"
        }
    }    
}

// build task for npm
task frontendBuild {}
frontendBuild.dependsOn(npm_install)
frontendBuild.dependsOn(npm_run_build)

npm_install {
  args = ['--prefix', './frontend']
}

npm_run_build {
  args = ['--prefix', './frontend']
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

sourceSets {
    main {
        java {
            srcDirs = ['backend/src/main/java']
        }
        resources {
            srcDirs = ['backend/src/main/resources']
        }
    }
}

copyFiles.dependsOn(frontendBuild);
compileJava.dependsOn(frontendBuild);

task backendBuild {}
backendBuild.dependsOn(compileJava)
backendBuild.dependsOn(jar)

jar.dependsOn(copyFiles)

repositories {
    mavenCentral()
}

eclipse {
    classpath {
         containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
         containers('org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8')
    }
}

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("${buildDir}/classes/main/")
    }
}

jar {
    baseName = 'expense-splitter'
    version = '0.0.1'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

configurations {
    dev
}

dependencies {
    // spring
    compile('org.springframework.boot:spring-boot-starter-web:1.5.2.RELEASE')
    compile('org.springframework.boot:spring-boot-starter-data-jpa:1.5.2.RELEASE')
    compile('org.springframework.boot:spring-boot-starter-security:1.5.2.RELEASE')

    compile('org.apache.commons:commons-lang3:3.3.2')

    // to make hibernate handle java 8 date and time types correctly
    // it's marked as deprecated but we need to keep it until
    // spring boot jpa starts using hibernate 5.2
    compile('org.hibernate:hibernate-java8:5.1.0.Final')

    // json web tokens
    compile ('io.jsonwebtoken:jjwt:0.7.0')

    compile 'mysql:mysql-connector-java'
    // google gson
    compile('com.google.code.gson:gson:2.8.0')
    // jackson - parsing of java 8 date and time types
    compile('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.7')


    // spring dev tools
    dev('org.springframework.boot:spring-boot-devtools:1.5.2.RELEASE')

    // testing
    testCompile('org.springframework.boot:spring-boot-starter-test:1.5.2.RELEASE')
}

// run spring boot app
bootRun {
    //addResources = true
    classpath = sourceSets.main.runtimeClasspath + configurations.dev
    jvmArgs = ["-Xdebug -agentlib:jdwp=transport=dt_socket,address=8080,server=y,suspend=n"]
}

// run all task
task runAll {}
runAll.dependsOn(bootRun)

Thanks in advance,

Try a different approach. Instead of manually copying the resources, tell Gradle that when it processes resources for the JAR, also take into consideration what is in frontend/dist/ :

processResources {
    from ('frontend/dist/') {
        into 'public'
    }
}

This should result in a JAR containing a public/ directory, with the contents of frontend/dist/ inside of it.

Gradle configuration for Spring Boot 1.5\\2.x + Angular 2-6

Angular in sub-folder frontend

Frontend module

Crate build.gradle :

plugins {
  id "com.moowork.node" version "1.2.0"
}

node {
  version = '8.11.3'
  npmVersion = '5.6.0'
  download = true
  workDir = file("${project.buildDir}/node")
  nodeModulesDir = file("${project.projectDir}")
}

task build(type: NpmTask) {
  args = ['run', 'build']
}

build.dependsOn(npm_install)

Note for Angular 6

Update outputPath value in angular.json to 'dist'

Backend module

Edit build.gradle for backend module:

Spring Boot 2.X:

bootJar {
    archiveName = "yourapp.jar"
    mainClassName = 'com.company.app.Application'

    from('frontend/dist') {
        into 'static'
    }
}

Spring Boot 1.5.X:

jar {
    archiveName = "yourapp.jar"
    manifest {
        attributes 'Main-Class': 'com.company.app.Application'
    }
    from('frontend/dist') {
        into 'static'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

Finally execute bootRepackage or bootJar task and check results in builds/libs

Assume that front end is located at the following folder: src/main/webapp/fe-ui/ , the following solution for the Spring Boot version 2.1.1.RELEASE could be considered:

bootJar {
    baseName = 'jar-name'
    version = '0.1.0'
    from('src/main/webapp/fe-ui/build') {
        into 'public'
    }
}

task installFeDependencies(type: NpmTask) {
    args = ['install']
}

task buildFe(type: NpmTask) {
    args = ['run', 'build']
    dependsOn installFeDependencies
}

compileJava {
    dependsOn buildFe
}

Running gradlew build will install, build front end as well as will invoke bootJar . The latter will package built front end bundle.

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