maven - Spring Boot/Angular 2/4 项目战构建

Posted

技术标签:

【中文标题】maven - Spring Boot/Angular 2/4 项目战构建【英文标题】:maven - Spring Boot/Angular 2/4 project war build 【发布时间】:2018-02-11 23:24:24 【问题描述】:

我有一个 Spring Boot 和 Angular 2/4 项目,我想将其打包成一个 WAR 并在 Tomcat 上提供服务。我有一个包含 2 个模块(1 个用于 Angular,1 个用于 Spring Boot)的父项目,具有以下 pom.xml 配置:

父/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>mk.edu.ukim.feit.bolt</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.3.RELEASE</version>
    <relativePath/>
</parent>

<modules>
    <module>api</module>
    <module>frontend</module>
</modules>

前端/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>

<artifactId>frontend</artifactId>


<name>Bolt Client</name>
<description>Bolt messaging app frontend client</description>

<parent>
    <groupId>mk.edu.ukim.feit.bolt</groupId>
    <artifactId>parent</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <version>1.4</version>

            <configuration>
                <nodeVersion>v7.9.0</nodeVersion>
                <npmVersion>4.6.1</npmVersion>
                <workingDirectory>src/main/frontend</workingDirectory>
            </configuration>

            <executions>
                <execution>
                    <id>install node and npm</id>
                    <goals>
                        <goal>install-node-and-npm</goal>
                    </goals>
                </execution>

                <execution>
                    <id>npm install</id>
                    <goals>
                        <goal>npm</goal>
                    </goals>
                </execution>

                <execution>
                    <id>npm run prod</id>

                    <goals>
                        <goal>npm</goal>
                    </goals>

                    <configuration>
                        <arguments>run prod</arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>

    <resources>
        <resource>
            <directory>target/frontend</directory>
            <targetPath>static</targetPath>
        </resource>
    </resources>
</build>

api/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>

<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>

<name>Bolt API</name>
<description>Bolt messaging app REST API</description>
<packaging>war</packaging>

<parent>
    <groupId>mk.edu.ukim.feit.bolt</groupId>
    <artifactId>parent</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-jdbc</artifactId>-->
    <!--</dependency>-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web-services</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
        <version>1.5.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.restdocs</groupId>
        <artifactId>spring-restdocs-mockmvc</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>$h2.version</version>
        <scope>compile</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.6.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>2.9.9</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
    <dependency>
        <groupId>commons-validator</groupId>
        <artifactId>commons-validator</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
        <groupId>mk.edu.ukim.feit.bolt</groupId>
        <artifactId>frontend</artifactId>
        <version>$project.version</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

我正在关注this 教程,最终成功构建了从父目录运行 mvn clean install 的项目,然后进入 api 目录并运行 mvn spring-boot:run,应用程序在 localhost:8080 上提供服务,但是,在构建战争文件时,Maven 不知何故没有拾取从 Angular 编译的静态文件,并在 http://localhost:8080 上给了我一个 404。任何人都可以就这里可能出现的问题提供提示吗?

我还尝试将 "prod" NPM 脚本设置为 "ng build --prod --base-href=\"./\"" 认为它可能需要对它做点什么,但它没有帮助。有什么想法吗?

【问题讨论】:

【参考方案1】:

我找到的解决方案:

    将单个“index.html”文件复制到“src/main/resources/templates” 服务器项目下的文件夹。请注意,HTML 文件结构必须非常严格(所有打开的标签都必须关闭等)。 添加maven依赖“spring-boot-starter-thymeleaf” 为请求将“/”映射到“索引”模板创建 HomeController

示例:

@Controller
public class HomeContoller 
    @RequestMapping("/")
    public String index() 
        return "index";
    

见GitHub commit with HomeController 如果有人知道更清洁/更好的解决方案并可以分享,那就太好了。

【讨论】:

【参考方案2】:

我有一个类似的布局,其中 Angular 应用程序和 webapp 位于不同的 maven 模块中。要在 webapp 中捆绑 angular 应用程序,我执行以下操作:-

target/classes/static/frontend 目录中构建 Angular 应用程序文件。我使用 architect.build.options 部分的 angular.json 文件中的 outputPath 参数进行配置。我也在此处配置 bashHrefdeployUrl 设置。请注意,architect.serve.options 是不同的,因此我可以使用 ng serve 进行调试和测试。

  
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": 
  "scrapit2myob-frontend": 
    "projectType": "application",
    "root": "projects/scrapit2myob-frontend",
    "sourceRoot": "projects/scrapit2myob-frontend/src",
    "architect": 
      "build": 
        "builder": "@angular-devkit/build-angular:browser",
        "options": 
          "aot": true,
          "outputPath": "target/classes/static/frontend",
          "index": "projects/scrapit2myob-frontend/src/index.html",
          "main": "projects/scrapit2myob-frontend/src/main.ts",
          "baseHref": "/frontend/",
          "deployUrl": "/frontend/",
          ...
        ,
        ...
      ,
      "serve": 
        "builder": "@angular-devkit/build-angular:dev-server",
        "options": 
          "browserTarget": "scrapit2myob-frontend:build",
          "proxyConfig": "proxy.config.json",
          "baseHref": "/",
          "deployUrl": "/"
        ,
        ...

让 maven 将 Angular 应用打包为 jar 文件。所有编译的 Angular 文件都应该出现在该 jar 的 static/frontend 文件夹中。

将 Angular 应用程序 jar 文件添加为 Web 应用程序项目的 maven 依赖项。由于 Angular 文件位于 static/frontend 文件夹中,因此默认情况下它们应该可以从 Web 应用程序公开访问。

在 webapp 中,我有一个 WebMvcConfigurer 来配置重定向。如果您的 Angular 前端中有深层链接,这是必要的,如果用户从浏览器刷新该链接,您需要服务器为这些链接提供 index.html。然后角度路由器负责渲染正确的组件...

  @Configuration
  public class WebMvcConfiguration implements WebMvcConfigurer 
      @Override
      public void addViewControllers(ViewControllerRegistry registry) 
          registry.addViewController("/").setViewName("forward:/frontend/index.html");
          registry.addViewController("/silent-refresh.html").setViewName("forward:/frontend/index.html");
          registry.addViewController("/receipts/**").setViewName("forward:/frontend/index.html");
          registry.addViewController("/tickets/**").setViewName("forward:/frontend/index.html");
          registry.addViewController("/supplier-mapping/**").setViewName("forward:/frontend/index.html");
          registry.addViewController("/item-mapping/**").setViewName("forward:/frontend/index.html");
          registry.addViewController("/account-mapping/**").setViewName("forward:/frontend/index.html");
          registry.addViewController("/job/**").setViewName("forward:/frontend/index.html");
          registry.addViewController("/login").setViewName("login");
          registry.addViewController("/swagger-ui").setViewName("redirect:/swagger-ui/index.html");
          registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
      
  

我使用 /static/frontend 而不仅仅是 /static 的原因是,如果您要保护 api,然后可以配置匹配 /frontend 的 url 以被 spring 安全过滤器忽略...

公共类 ServerSecurityConfig 扩展 WebSecurityConfigurerAdapter 公共无效配置(WebSecurity web)抛出异常 web.ignoring.antMatchers("/frontend/**");

【讨论】:

以上是关于maven - Spring Boot/Angular 2/4 项目战构建的主要内容,如果未能解决你的问题,请参考以下文章

使用Maven管理Spring

spring mvc 跟 maven spring mvc 有啥区别

Maven:Spring 4 + Spring Security

`spring-boot-maven-plugin` 和 `maven-compiler-plugin` 有啥区别?

spring 02-Maven搭建Spring开发环境

java框架-----spring框架------在自己的项目中如何用maven管理spring相关jar包