Record the Jar deployment process of splitting the front and rear modules at one time

background

For a single application, the front end uses the React framework, and static resources (JS, CSS, etc.) are placed under the src\main\resources\static Directory:

.babelrc
.gitignore
.mvn
node_modules
package-lock.json
package.json
pom.xml
src
--main
	--java
	--resources
		--static
		--templates
			--dist
webpack.config.js

The front-end build configuration file webpack config. js:

var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')

let devServer = {}
let plugins = [
    new webpack.DefinePlugin({
        ISDEV: JSON.stringify(process.env.NODE_ENV === 'development'),
    }),
    new HtmlWebpackPlugin({
        template: "./src/main/resources/templates/index.html",
        // ?
        inject: true
    }),
]
if (process.env.NODE_ENV === 'development') {
    devServer = {
        contentBase: path.join(__dirname, './src/main/resources/templates/dist'),
        host: 'localhost',
        port: '8000',
        open: true, // Automatically pull up the browser
        hot: true, // Thermal loading
    }
    plugins.push(new webpack.HotModuleReplacementPlugin())
}

module.exports = {
    entry: './src/main/resources/static/index.js',
    output: {
        path: path.resolve(__dirname, './src/main/resources/templates/dist'),
        publicPath: process.env.NODE_ENV === 'development' ? '' : './templates/dist/',
        filename: process.env.NODE_ENV === 'development' ? 'build.js' : 'build.[chunkhash].js'
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env', "@babel/preset-react"],
                        plugins: ['@babel/plugin-proposal-object-rest-spread', "@babel/plugin-proposal-function-bind", '@babel/plugin-proposal-class-properties']
                    }
                },
                    // {
                    //   Loader: 'eslint loader', / / specify to enable eslint loader
                    //   options: {
                    //     formatter: require('eslint-friendly-formatter'),
                    //     emitWarning: false
                    //   }
                    // }
                ]
            },
            {
                test: /\.(scss|css|less)$/,
                exclude: /node_modules/,  // It is used for custom style processing, and node needs to be removed_ Modules folder
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader",
                    options: {
                        modules: true,   // Add styles in the way of className={styles.xxx}
                        localIdentName: '[local]--[hash:base64:5]'
                    }
                }, {
                    loader: "less-loader",
                }]
            },
            {
                test: /\.(scss|css|less)$/,
                include: /node_modules/,  // For antd, including node_modules
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader",
                }, {
                    loader: "less-loader",
                    options: {
                        modifyVars: {
                        	// Custom theme color
                            "primary-color": "#6E54B0",
                            "link-color": "#6E54B0",
                            'border-radius-base': '2px',
                        },
                        javascriptEnabled: true,
                    }
                }]
            },
            {
                test: /\.(eot|ttf|woff|woff2)(\?\S*)?$/,
                loader: 'file-loader'
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]?[hash]'
                }
            }
        ]
    },
    resolve: {
        extensions: ['.js', '.jsx', '.json'],
    },
    performance: {hints: false},
    plugins: plugins,
    devServer
}

Front end package JSON files are nothing special:

 {
 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "set NODE_ENV=development&& webpack --mode=development",
    "pre": "set NODE_ENV=development&& webpack --mode=development",
    "prod": "set NODE_ENV=production&& webpack --mode=production",
    "start": "set NODE_ENV=development&&webpack-dev-server --mode development --port=8001"
  }
}

The backend has a configuration file staticresourcesconfig java:

@Configuration
public class StaticResourcesConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
        super.addResourceHandlers(registry);
    }

    // Regardless of this topic, solve cross domain configuration
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

Now, if you want to separate the front and rear ends, this is the background.

practice

Project structure adjustment:

Root directory 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>
    <parent>
        <groupId>com.aaa.framework.microservice</groupId>
        <artifactId>microservice-starter-parent</artifactId>
        <version>1.1.9</version>
    </parent>
    <groupId>com.aaa.cbd.platform</groupId>
    <artifactId>octopus-backend-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>
    <modules>
        <module>octopus-backend-ui</module>
        <module>octopus-backend</module>
    </modules>

    <properties>
        <module.version>1.0.0-SNAPSHOT</module.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.aaa.cbd.platform</groupId>
                <artifactId>octopus-backend</artifactId>
                <version>${module.version}</version>
            </dependency>
            <dependency>
                <groupId>com.aaa.cbd.platform</groupId>
                <artifactId>octopus-backend-ui</artifactId>
                <version>${module.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <verbose>true</verbose>
                        <fork>true</fork>
                        <compilerVersion>${java.version}</compilerVersion>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                        <encoding>UTF8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

Backend POM The XML file depends on the front-end build product Jar:

<project>
    <parent>
        <groupId>com.aaa.cbd.platform</groupId>
        <artifactId>octopus-backend-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>octopus-backend</artifactId>

    <properties>
        <octopus-backend-ui.version>1.0.0-SNAPSHOT</octopus-backend-ui.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.aaa.cbd.platform</groupId>
            <artifactId>octopus-backend-ui</artifactId>
            <version>${octopus-backend-ui.version}</version>
        </dependency>
    </dependencies>
</project>

The POM of front-end engineering The XML file configuration is as follows:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.aaa.cbd.platform</groupId>
    <version>1.0.0-SNAPSHOT</version>
    <artifactId>octopus-backend-ui</artifactId>
    <properties>
        <build-plugin.exec.version>1.4.0</build-plugin.exec.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>exec-npm-install</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <configuration>
                            <executable>npm</executable>
                            <arguments>
                                <argument>install</argument>
                            </arguments>
                        </configuration>
                    </execution>
                    <execution>
                        <id>npm-build</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <configuration>
                            <executable>npm</executable>
                            <arguments>
                                <argument>run</argument>
                                <argument>build</argument>
                            </arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <nonFilteredFileExtensions>
                        <nonFilteredFileExtension>woff</nonFilteredFileExtension>
                        <nonFilteredFileExtension>ttf</nonFilteredFileExtension>
                        <nonFilteredFileExtension>woff2</nonFilteredFileExtension>
                        <nonFilteredFileExtension>eot</nonFilteredFileExtension>
                        <nonFilteredFileExtension>swf</nonFilteredFileExtension>
                        <nonFilteredFileExtension>ico</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>dist</directory>
                <targetPath>META-INF/ui</targetPath>
                <filtering>true</filtering>
            </resource>
        </resources>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <version>${build-plugin.exec.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>2.6</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

Front end webpack config. JS only lists the changed parts:

let plugins = [
    new HtmlWebpackPlugin({
    	// change 1
        template: "./index.html",
    }),
]
if (process.env.NODE_ENV === 'development') {
    devServer = {
		// change 2
        contentBase: path.join(__dirname, './dist'),
        host: 'localhost',
        port: '8000',
        open: true,
        hot: true,
    }
    plugins.push(new webpack.HotModuleReplacementPlugin())
}

module.exports = {
	// change 3
    entry: './src/index.js',
    output: {
    	// change 4
        path: path.resolve(__dirname, './dist'),
        // change 5
        publicPath: process.env.NODE_ENV === 'development' ? '' : './',
        filename: process.env.NODE_ENV === 'development' ? 'build.js' : 'build.[chunkhash].js'
    }
}

According to the pom file configuration of the parent project, open the maven panel, execute clean and then install:

The following construction products can be obtained:

It can be found that the front-end project will be packaged as a Jar and placed as a dependency in the Jar package of the back-end Spring Boot application.

At this point, we can start the application: Java - Jar - dserver port=8082 octopus-backend-1.0. 0-SNAPSHOT. jar

Browser input: http://localhost:8082/hs , return the response OK. Because we write an application health Controller interface:

@RestController
public class HealthController {
    @RequestMapping(value = "/hs")
    public String hs() {
        return "OK";
    }

The question is, how to access the front-end page?

The answer is: StaticResourcesConfig configuration file.

The previous configuration was:
registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");

We replace with:
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/META-INF/");

At the same time, POM The XML file is updated to:

<resources>
    <resource>
        <directory>dist</directory>
        <targetPath>META-INF/static</targetPath>
        <filtering>true</filtering>
    </resource>
<resources>

Then enter in the browser: http://localhost:8082/static/static/index.html#/ , you can access:

How to remove two layers of static nesting?

For the attempt of blood and tears, ResourceHandler must be a two-tier Directory:
registry.addResourceHandler("/**/**").addResourceLocations("classpath:/META-INF/");

At the same time, POM The XML file is updated to:

<resources>
    <resource>
        <directory>dist</directory>
        <targetPath>META-INF/</targetPath>
        <filtering>true</filtering>
    </resource>
</resources>

Unzip the front end engineering Jar package:

The dist folder was found and copied to the META-INF directory.

New problems

The browser opens the login page
http://localhost:8082/index.html#/

After successful login, enter the home page: 404
http://localhost:8082/index

The first page that can actually be displayed normally:
http://localhost:8082/index.html#/index

Other pages:
http://localhost:8082/index.html#/appList

By implication, the page needs to be nested with another layer of index html#/

How to solve this problem? It seems that the problem is the front-end path. At this time, if you focus on the front-end build file webpack config. JS is a detour.

The final solution is to configure the StaticResourcesConfig file:

package config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class StaticResourcesConfig extends WebMvcConfigurerAdapter {
    @Value("${server.contextPath:}")
    private String contextPath;

    @Value("${applications.indexForwardPath:index.html}")
    private String forwardPath;

    @Value("${applications.resourceLocations:classpath:/static/,classpath:/public/}")
    private String resourceLocations;

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // contextPath is null, if condition does not hold
        if (StringUtils.hasText(contextPath)) {
            registry.addRedirectViewController(contextPath, contextPath + "/");
        }
        // What are the differences between the following two addviewcontrollers
        registry.addViewController(contextPath + "/").setViewName(forwardPath);
        registry.addViewController("/*/").setViewName("forward:index.html");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        resourceLocations += ",classpath:/META-INF/";
        registry.addResourceHandler("/**")
                .addResourceLocations(resourceLocations.split(","))
                .resourceChain(true);
    }
}

In addition, remove a Controller:

@RequestMapping("")
@Controller
public class UserController {
    @GetMapping
    public String goToLogin() {
        return "dist/index";
    }
}

Added by banacan on Fri, 24 Dec 2021 04:07:18 +0200