Spring Boot + Angular 4

Modern web applications must rely on the best services frameworks and the best user interface frameworks to be most reliable, versatile, and the easiest to develop and maintain. That is why many software development teams choose Spring Boot for the services layer and Angular for the UI layer. Sustainable practices for continuous integration and development of these layers is a key to productivity of your team. Two more choices the team needs to make is the Integrated Development Environment (IDE) and a build automation tool. De facto leading choices are, respectively, IntelliJ IDEA with top of the line support of both backend and UI, and maven traditionally used for the backend with multiple plugins for the UI. Let me describe my latest setup for a project with Spring Boot 2.0.0, Angular 4.2, and Maven 3.3.9 using IntelliJ IDEA 2017.2.3. Backend code must reside in its own (server) maven module, while UI code must reside in a separate (client) maven module. A module, parent to both, provides easy means of building the entire application. Angular 4 project has its own dependency management with npm, but it can readily be integrated with maven using frontend-maven-plugin. I develop in Windows 10, but steps should be practically the same for other OSes. Part 1. Front-end module.
  1. Choose distribution of NodeJS and install on your machine. I installed v6.11.2.
  2. Installed NodeJS contains "npm" executable. Check its version as "npm -v". My version is 3.10.10.
  3. Install Angular command line interface package globally by running as Administrator "npm install -g @angular/cli". The previous version of this package exists under "angular-cli" name - do NOT install that one, as it only supports Angular 2.
  4. Create a maven module in IntelliJ for the UI (mine is named "search-client") with a "pom.xml" file, but without any other files or directories.
  5. Populate "search-client" module with a UI template by executing "ng new search-client --skip-git" in a folder parent to "search-client" folder. I have a separate version control repository and prefer to skip provided integration with git.
  6. Merge existing Angular 4 files into "search-client" project or write your UI from scratch, integrate with version control of choice.
  7. Open "package.json" and define a command for prod compilation:
    "scripts": {
    ...
      "prod": "ng build --prod --env=prod"
    },
  8. Use the following setup in "pom.xml" for search-client in <build><plugins> section. Match NodeJS and npm versions to the ones discovered above. "npm install" execution can be commented out after the first run to save time.
    <plugin>
      <groupId>com.github.eirslett</groupId>
      <artifactId>frontend-maven-plugin</artifactId>
      <version>${frontend-maven-plugin.version}</version>
      <executions>
        <execution>
          <id>install node and npm</id>
          <goals>
            <goal>install-node-and-npm</goal>
          </goals>
          <configuration>
            <nodeVersion>${node.version}</nodeVersion>
            <npmVersion>${npm.version}</npmVersion>
          </configuration>
        </execution>
    
        <execution>
          <id>npm install</id>
          <goals>
            <goal>npm</goal>
          </goals>
          <configuration>
            <arguments>install</arguments>
          </configuration>
        </execution>
        <execution>
          <id>prod</id>
          <goals>
            <goal>npm</goal>
          </goals>
          <configuration>
            <arguments>run-script prod</arguments>
          </configuration>
          <phase>generate-resources</phase>
        </execution>
      </executions>
    </plugin>
  9. Take note of the compilation output directory specified in ".angular-cli.json" file under option "apps -> outDir" and use it in pom.xml <build> section as a resource
    <resources>
        <resource>
          <filtering>false</filtering>
          <directory>dist</directory>
        </resource>
    </resources>
  10. Execution of "mvn clean install" leads to a jar file "search-client-1.0-SNAPSHOT.jar" in a local repository containing compiled frontend code. The command takes 20 seconds for a new project on a 2-nd and subsequent runs.
  11. Define a new "npm" Run/Debug Configuration in IntelliJ to run UI code in development mode: Run -> Edit Configurations -> "+"; then choose correct path to "package.json" file; Command -> run; Scripts -> ng; Arguments -> serve; choose a node interpreter, global one is fine.
  12. Run this configuration and open http://localhost:4200 in a browser. Try modifying Typescript, JS, CSS, or HTML files and observe how the displayed pages change.
Part 2. Integration with backend module.
  1. In our backend module (named "search-server") declare dependency on a frontend module in "pom.xml" file. Apart from providing access to the UI code, this ensures that the UI code builds before the backend code.
    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>${frontend.artifact.id}</artifactId>
            <version>${project.version}</version>
        </dependency>
  2. Here the property ${frontend.artifact.id} is defined in a parent module ("search-parent")
    <properties>
        <frontend.artifact.id>search-client</frontend.artifact.id>
    </properties>
  3. Define a path to UI files in "target" folder of compiled code in "search-server" module:
    <properties>
        <UI.files.folder>${project.build.directory}/classes/static</UI.files.folder>
    </properties>
  4. Use "maven-dependency-plugin" to unpack UI files into the target resources folder. Here the version of the plugin is managed by "spring-boot-starter-parent" project, which is the parent of "search-parent".
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
            <execution>
                <id>unpack</id>
                <phase>compile</phase>
                <goals>
                    <goal>unpack-dependencies</goal>
                </goals>
                <configuration>
                    <includeGroupIds>${project.groupId}</includeGroupIds>
                    <includeArtifactIds>search-client</includeArtifactIds>
                    <outputDirectory>${UI.files.folder}</outputDirectory>
                    <excludes>META-INF/**</excludes>
                    <overWriteReleases>true</overWriteReleases>
                    <overWriteSnapshots>true</overWriteSnapshots>
                </configuration>
            </execution>
        </executions>
    </plugin>
  5. Create a Run configuration in IntelliJ to execute the main application class, e.g. annotated with @SpringBootApplication or @ComponentScan or @EnableAutoConfiguration. After running "mvn install" and starting this configuration, the UI entry point, e.g. "index.html", will be accessible at the specified application root, port (and host).
  6. Executable "jar" file or "war" file is readily produced with "spring-boot-maven-plugin" used in <build><plugins> section, where the jar file of search-client dependency is excluded:
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <mainClass>SUBSTITUTE_YOUR_PACKAGE_NAME.SearchServerAppInitializer</mainClass>
            <classifier>exec</classifier>
            <excludeArtifactIds>search-client</excludeArtifactIds>
        </configuration>
    </plugin>
  7. An additional bind for "maven-clean-package" helps to refresh UI of a running application kicked off by starting the main application class configuration from 5. For that, run "mvn install" on a client module and "mvn install" on a service module (or can run "mvn install" on a parent module instead of both). New UI will load upon refreshing the browser page. The application doesn't need to be stopped and no "clean" goal needs to be issued:
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-clean-plugin</artifactId>
        <executions>
            <execution>
                <phase>generate-resources</phase>
                <configuration>
                    <excludeDefaultDirectories>true</excludeDefaultDirectories>
                    <filesets>
                        <fileset>
                            <directory>${UI.files.folder}</directory>
                        </fileset>
                    </filesets>
                </configuration>
                <goals>
                    <goal>clean</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
The provided instructions are optimized with respect to developer experience and compilation time, e.g. one doesn't have to "clean" each time. One can find similar setups online. This notable discussion operates with an old version of Angular CLI plugin. While global installation of "angular-cli" is not necessary, some suggest to use only a locally installed NodeJS to do the compilation. Then, however, one can't readily generate the UI project template. This github project uses a setup very similar to mine.

Leave a Reply

Your email address will not be published. Required fields are marked *