.NET CORE与Spring Boot编写控制台程序应有的优雅姿势

本文分别说明.NET CORE 与 Spring Boot 编写控制台程序应有的“正确”方法,以便.NET 程序员、JAVA 程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写的控制台程序要好。

本文所说的编写控制台程序应有的“正确”方法,我把正确二字加上引号,因为没有绝对的正确,因人而异,因系统设计需求而异,我这里所谓的正确方法是指使用面向对象,依赖注入 IOC,切面控制 AOP 等编码规范来提升程序的性能、整洁度、可读性、可维护性等,最终达到让人感觉有点高大上,有点优雅的样子

先来说说.NET CORE 编写控制台程序,目前网络上大把的讲解 ASP.NET CORE 的编写规范,反而对于.NET CORE 控制台程序编写规范介绍比较少,大多停留在 Hello Word 程序中,而本文则来讲讲.NET CORE 控制台的编写规范(应有的优雅姿势)^ v ^

 如果说不讲什么 IOC,DI,AOP 等,不讲扩展性,规范性,全部面向过程(方法)编程,那估计没什么好讲的,因为无非就是定义一个 class,然后在 class 中定义一堆的 method(方法),如果在方法中需要使用到其它第三方组件,则直接单独引用,引用后进行简单封装 util 工具类的静态方法,甚至也不用封装,直接使用原生的方法,总之全部都是方法调方法。而这里所演示的编写控制台方法均是尽可能的使用.NET CORE 所具有的特性,只有这样才能体现出.NET CORE 框架的优势,否则普通控制台程序与.NET CORE 控制台程序有什么区别。

编写.NET CORE 控制台程序优雅姿势一:(直接使用.NET CORE 的 IOC、Logging、Config 组件)

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//Program.cs
 
using Microsoft.Extensions.DependencyInjection;
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.Configuration;
using System.IO;
 
namespace NetCoreConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //设置config文件
            var config = new ConfigurationBuilder()
                                .SetBasePath(Directory.GetCurrentDirectory())
                                .AddJsonFile("appSettings.json", optional: true, reloadOnChange: true).Build();
 
            //设置依赖注入
            var provider = new ServiceCollection()
                                    .AddLogging(configLogging => //设置日志组件
                                    {
                                        configLogging.SetMinimumLevel(LogLevel.Information);
                                        configLogging.AddConsole();
                                    })
                                   .AddScoped<IConfiguration>(p => config)
                                   .AddScoped<HostService>()
                                   .BuildServiceProvider();
 
            var hostService = provider.GetService<HostService>();
 
            hostService.RunAsync();//统一入口服务
 
            Console.WriteLine("提示:程序已正常启动运行,按任意键停止运行并关闭程序...");
            Console.ReadLine();
 
        }
    }
}
 
 
//HostService.cs<br>
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
 
namespace NetCoreConsoleApp
{
    public class HostService
    {
        private readonly IConfiguration config;
        private readonly ILogger<HostService> logger;
        public HostService(IConfiguration config, ILogger<HostService> logger)
        {
            this.config = config;
            this.logger = logger;
        }
 
        public void RunAsync()
        {
            Task.Run((Action)Execute);
        }
 
        /// <summary>
        /// 控制台核心执行入口方法
        /// </summary>
        private void Execute()
        {
            //TODO 业务逻辑代码,如下模拟
            Stopwatch stopwatch = Stopwatch.StartNew();
            for (int i = 1; i <= 100; i++)
            {
                Console.WriteLine("test WriteLine:" + i);
                Thread.Sleep(100);
            }
 
            stopwatch.Stop();
 
            logger.LogInformation("Logging - Execute Elapsed Times:{}ms", stopwatch.ElapsedMilliseconds);
        }
 
    }
}

因为要使用.NET CORE 相关核心组件,故需要引用相关的 NuGet 包(引用包的方式有多种方式),而且默认的.NET CORE 控制台只会生成 DLL 并不会生成 EXE 启动程序,故如果仅在 WIN 系统下使用,还需要设置生成方式等,详细配置属性如下:(项目文件 csproj)

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<SelfContained>false</SelfContained>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
</ItemGroup>

</Project>

View Code

 如上代码虽简单但代码编写顺序很关键,这里进行说明一下:

1. 因为一般应用程序都会有 config 文件,故我们需要先通过 new ConfigurationBuilder 来设置 config 文件的方式及路径;

2. 因为要使用.NET CORE 默认的 IOC 框架,故 new ServiceCollection,然后将相关的依赖服务组件注册到 IOC 容器中;

3.config、logging 均是一个程序最基本的依赖组件,故将其注册到 IOC 容器中,注册 logging 有专门的扩展方法(AddLogging),而 config 没有则直接使用通过的注册方法(当然也可以基于 ServiceCollection 写一个 AddConfiguration 扩展方法)

4. 控制台需要一个核心的入口方法,用于处理核心业务,不要直接在 Program 中写方法,这样就不能使用 IOC,同时也没有做到职责分明,Program 仅是程序启动入口,业务处理应该有专门的入口,故上述代码中有 HostService 类(即:核心宿主服务类, 意为存在于控制台中的服务处理类,在这个类的构造涵数中列出所需依赖的服务组件,以便实例化时 IOC 可以自动注入这个参数),并注册到 IOC 容器中,当然也可以先定义一个 IHostService 接口然后实现这个接口。(如果有多个 HostService 类实例,建议定义一个 IHostService 接口,接口中只需要入口方法定义即可,如:RunAsync)

5. 当各组件初始化设置 OK、IOC 注册到位后,就应该通过 IOC 解析获得 HostService 类实例,并执行入口方法:RunAsync,该方法为异步后台执行,即调用该方法后,会在单独的后台线程处理核心业务,然后主线程继续往下面走,输出关闭提示信息,最后的 Console.ReadLine(); 很关键,这个是等待输入流并挂起当前主线程,目的大家都知道,不要让控制台程序关闭。

 通过上述的讲解及源代码展示,有没有感觉优雅呢?如果觉得这样还算优雅,那下面展示的第二种更优雅的姿势

编写.NET CORE 控制台程序优雅姿势二:(使用通用主机也称泛型主机 HostBuilder)

代码如下:Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using System.IO;
using Polly;
using System;
 
namespace NetCoreConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new HostBuilder()
                .ConfigureHostConfiguration(configHost =>
                {
                    configHost.SetBasePath(Directory.GetCurrentDirectory());
                })
                .ConfigureAppConfiguration(configApp =>
                {
                    configApp.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                })
                .ConfigureServices((context, services) =>
                {
                    //添加数据访问组件示例:services.AddTransient<IDbAccesser>(provider =>
                    //{
                    //    string connStr = context.Configuration.GetConnectionString("ConnDbStr");
                    //    return new SqlDapperEasyUtil(connStr);
                    //});
 
                    //添加HttpClient封装类示例:services.AddHttpClient<GitHubApiClient>()
                    //.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(3, t => TimeSpan.FromMilliseconds(800)));
 
                    services.AddHostedService<DemoHostedService>();
                })
                .ConfigureLogging((context, configLogging) =>
                {
                    configLogging.ClearProviders();
                    configLogging.SetMinimumLevel(LogLevel.Trace);
                    configLogging.AddNLog(context.Configuration);
                })
                .UseConsoleLifetime()
                .Build();
 
            host.Run();
        }
    }
}

DemoHostedService 类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
 
namespace NetCoreConsoleApp
{
    public class DemoHostedService : IHostedService
    {
        private readonly IConfiguration config;
        private readonly ILogger logger;
 
        public DemoHostedService(IConfiguration config, ILogger<DemoHostedService> logger)
        {
            this.config = config;
            this.logger = logger;
        }
 
        public Task StartAsync(CancellationToken cancellationToken)
        {
            Console.WriteLine(nameof(DemoHostedService) + "已开始执行...");
 
            //TODO 业务逻辑代码,如下模拟
            Stopwatch stopwatch = Stopwatch.StartNew();
            for (int i = 1; i <= 100; i++)
            {
                Console.WriteLine("test WriteLine:" + i);
                Thread.Sleep(100);
            }
 
            stopwatch.Stop();
 
            logger.LogInformation("Logging - Execute Elapsed Times:{}ms", stopwatch.ElapsedMilliseconds);
 
            return Task.FromResult(0);
        }
 
        public Task StopAsync(CancellationToken cancellationToken)
        {
            Console.WriteLine(nameof(DemoHostedService) + "已被停止");
            return Task.FromResult(0);
        }
 
    }
}

 

因为要使用 HostBuilder 类及相关的.NET CORE 组件(如上代码主要使用到了:Host、Dapper、Nlog、Polly 等),故仍需引用相关的 NuGet 包,详细配置属性如下:(项目文件 csproj)

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<SelfContained>false</SelfContained>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapper" Version="1.60.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="2.2.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.5.1" />
<PackageReference Include="System.Collections.Concurrent" Version="4.3.0" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>

View Code

 如上代码所示,写过 ASP.NET CORE 程序的人可能比较眼熟,这与 ASP.NET CORE 的写法很类似,是的,你没有看错,HostBuilder是通用主机,是可以广泛应用于非 HTTP 的环境下,而 ASP.NET CORE 中的WebHostBuilder 主要用于 HTTP WEB 环境,使用方式基本类似,都是先定义 HostBuilder,然后利用扩展方法注册、配置各种组件(中间件),最后调用 Host 的 Run 方法,开启后台服务执行,不同的是 WebHostBuilder 多了属于 HTTP 专有的一些属性及方法及其适用的中间件。

由于这种写法比较通用,适用于已熟悉.NET CORE 或 ASP.NET CORE 的人群,上手也较简单,故建议采取这种方式来写.NET CORE 控制台程序。需要注意的是 HostBuilder 中最重要的是:注册HostedService 服务,如上代码中的 DemoHostedService 即是实现了 IHostedService 接口的宿主后台服务类,可以定义多个,然后都注册到 IOC 中,最后 Host 会按注册先后顺序执行多个 HostedService 服务的 StartAsync 方法,当停止时同样会执行多个 HostedService 服务的 StopAsync 方法

下面再来看看使用 Spring&Spring Boot 框架来优雅的编写控制台程序 

编写 Spring 控制台程序优雅姿势一:(只引用所必需的 spring jar 包、logger jar 包,追求极简风)

使用 IDEA +MAVEN 创建一个 quickstart 控制台项目,在 maven POM XML 中先引用所必需的 spring jar 包、logger jar 包等,配置如下:

<?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">

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">modelVersion</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>4.0.0<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">modelVersion</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-console<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-console<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> FIXME change it to the project's website </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">url</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>http://www.zuowenjun.cn<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">url</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">project.build.sourceEncoding</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>UTF-8<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">project.build.sourceEncoding</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">maven.compiler.source</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>1.8<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">maven.compiler.source</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">maven.compiler.target</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>1.8<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">maven.compiler.target</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">spring.version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>5.1.8.RELEASE<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">spring.version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependencies</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>junit<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>junit<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>4.11<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">scope</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>test<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">scope</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-core<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>${spring.version}<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-beans<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>${spring.version}<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-context<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>${spring.version}<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.slf4j<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>slf4j-api<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>1.7.25<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>ch.qos.logback<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>logback-classic<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>1.2.3<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependencies</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">build</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">pluginManagement</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span><span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-clean-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>3.1.0<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-resources-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>3.0.2<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-compiler-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>3.8.0<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-surefire-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>2.22.1<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-jar-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>3.0.2<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-install-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>2.5.2<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-deploy-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>2.8.2<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-site-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>3.7.1<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-project-info-reports-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>3.0.0<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">pluginManagement</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.apache.maven.plugins<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>maven-compiler-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">configuration</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">source</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>8<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">source</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">target</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>8<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">target</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">configuration</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">build</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

</project>

View Code

然后采取自定义注解类 (SpringBeansConfig) 的方式注册相关 Bean(包含配置映射类 Bean:AppProperties),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//app.java
 
package cn.zuowenjun.spring;
 
import cn.zuowenjun.spring.cn.zuowenjun.spring.services.HostService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
import java.io.IOException;
 
/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringBeansConfig.class);
        HostService hostService = applicationContext.getBean(HostService.class);
 
        hostService.run();
 
        applicationContext.registerShutdownHook();
 
        try {
            System.in.read();
        } catch (IOException e) {
            System.out.println("等待读取输入数据报错:" + e.getMessage() + ",将直接退出程序!");
        }
    }
}
 
 
//AppProperties.java
 
package cn.zuowenjun.spring;
 
import org.springframework.beans.factory.annotation.Value;
 
 
public class AppProperties {
 
    @Value("${app.name}")
    private String appName;
 
    @Value("${app.author}")
    private String appAuthor;
 
    @Value("${app.test.msg}")
    private String testMsg;
 
    public String getAppName() {
        return appName;
    }
 
    public void setAppName(String appName) {
        this.appName = appName;
    }
 
    public String getAppAuthor() {
        return appAuthor;
    }
 
    public void setAppAuthor(String appAuthor) {
        this.appAuthor = appAuthor;
    }
 
    public String getTestMsg() {
        return testMsg;
    }
 
    public void setTestMsg(String testMsg) {
        this.testMsg = testMsg;
    }
}
 
 
//SpringBeansConfig.java
 
package cn.zuowenjun.spring;
 
import cn.zuowenjun.spring.cn.zuowenjun.spring.services.HostService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
 
@Configuration
@PropertySource(value = "classpath:app.properties", ignoreResourceNotFound = false)
public class SpringBeansConfig {
 
    @Bean
    @Order(1)
    public HostService hostService() {
        return new HostService();
    }
 
    @Bean
    @Order(0)
    @Scope("singleton")
    public AppProperties appProperties() {
        return new AppProperties();
    }
 
    //注册其它所需Bean...
}
 
 
//HostService.java
 
package cn.zuowenjun.spring.cn.zuowenjun.spring.services;
 
import cn.zuowenjun.spring.AppProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StopWatch;
 
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
 
public class HostService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(HostService.class);
 
    @Autowired
    private AppProperties appProperties;
 
    //可以添加其它属性注入
 
    public void run() {
//        ExecutorService pool = Executors.newSingleThreadExecutor();
//        pool.execute(() -> execute());
 
        new Thread(this::execute).start();
    }
 
    /// <summary>
    /// 控制台核心执行入口方法
    /// </summary>
    private void execute() {
        //TODO 业务逻辑代码,如下模拟
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();
        for (int i = 1; i <= 100; i++) {
            System.out.println("test WriteLine:" + i);
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
        }
        stopwatch.stop();
 
        System.out.println(String.join("", Collections.nCopies(30, "=")));
 
        System.out.printf("app name is:%s %n", appProperties.getAppName());
        System.out.printf("app author is:%s %n", appProperties.getAppAuthor());
        System.out.printf("app test msg:%s %n", appProperties.getTestMsg());
 
        LOGGER.info("Logging - Execute Elapsed Times:{}ms", stopwatch.getTotalTimeMillis());
    }
}

 app.properties 配置文件内容如下,注意应放在 classpth 目录下(即:resources 目录下,没有需自行创建并设为 resources 目录):

1
2
3
app.name=demo spring console
app.author=zuowenjun
app.test.msg=hello java spring console app!

 如上即上实现一个 spring 的控制台程序,当然由于是示例,故只引用了 logger 包,正常还需引用 jdbc 或 ORM 框架的相关 jar 包, 上述代码关键逻辑说明(同样要注意顺序):

1.new AnnotationConfigApplicationContext类(spring IOC 容器),创建一个 IOC 容器,类似.NET CORE 中的 ServiceProvider 类;

2. 定义 SpringBeansConfig bean 注册配置类(注册相关依赖),这个类中依次注入相关的 bean,如果 bean 之间有依赖顺序关系,建议添加 @Order 并指明序号;该类作为 AnnotationConfigApplicationContext 的构造函数参数传入,以便 IOC 自动解析并完成实际注册;

3. 同样是定义一个 HostService 宿主服务类,并实现 run 方法逻辑,一般采取后台线程异步执行,为了演示效果与.NET CORE 的 HostService 类相同,示例逻辑基本相同。另外还定义了 AppProperties 配置映射类,便于直接读取配置,.NET CORE 同样也有类似注册 bind 到配置类中,然后在服务类中使用:IOptions< 配置类 > 作为构造函数参数实现构造函数注入。只是由于篇幅有限故.NET CORE 部份直接采取了注入 IConfiguration,大家有兴趣可以查看网上相关资料。

4.IOC 容器初始化并注册成功后,即可解析 HostService 类获得实例,执行 run 方法,run 方法会开启线程在后台处理,并返回到主线程,直至 in.read() 阻塞挂起主线程,防止程序自动关闭。

编写 Spring boot 控制台程序优雅姿势二:(引用 spring boot jar 包)

 使用 IDEA+Spring Initializr 来创建一个 spring boot 项目,创建过程中按需选择依赖的框架,我这里是示例,故除了默认 spring-boot-starter 依赖外,其余什么依赖都不添加,创建后 Maven 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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.zuowenjun.spring</groupId>
    <artifactId>springboot-console</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-console</name>
    <description>Demo project for Spring Boot</description>
    <url>http://www.zuowenjun.cn</url>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">java.version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>1.8<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">java.version</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">properties</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependencies</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-boot-starter<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-boot-starter-test<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">scope</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>test<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">scope</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">dependencies</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">build</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>spring-boot-maven-plugin<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
        <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">build</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

</project>

View Code

然后创建相关的 Bean 类:HostService(宿主服务类,这个与前文定义类均相同)、AppProperties(配置映射类,这个是映射默认的 application.properties 配置文件,注意这里的映射方式与前文所描述稍有不周,采用:@ConfigurationProperties+ 属性映射,无需加 @Value 注解,映射属性时如果有 - 则应写成驼峰式,如果有. 则应定义内部静态类,呈现层级属性完成映射,具体的用法可以参见我之前的文章):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//HostService.java
 
package cn.zuowenjun.spring.services;
 
import cn.zuowenjun.spring.AppProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
 
import java.util.Collections;
 
@Component
public class HostService {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(HostService.class);
 
    @Autowired
    private AppProperties appProperties;
 
    //可以添加其它属性注入
 
    public void run() {
//        ExecutorService pool = Executors.newSingleThreadExecutor();
//        pool.execute(() -> execute());
 
        new Thread(this::execute).start();
    }
 
    /// <summary>
    /// 控制台核心执行入口方法
    /// </summary>
    private void execute() {
        //TODO 业务逻辑代码,如下模拟
        StopWatch stopwatch = new StopWatch();
        stopwatch.start();
        for (int i = 1; i <= 100; i++) {
            System.out.println("test WriteLine:" + i);
            try {
                Thread.sleep(100);
            } catch (Exception e) {
            }
        }
        stopwatch.stop();
 
        System.out.println(String.join("", Collections.nCopies(30, "=")));
 
        System.out.printf("app name is:%s %n", appProperties.getName());
        System.out.printf("app author is:%s %n", appProperties.getAuthor());
        System.out.printf("app test msg:%s %n", appProperties.getTestMsg());
 
        LOGGER.info("Logging - Execute Elapsed Times:{}ms", stopwatch.getTotalTimeMillis());
    }
}
 
 
//AppProperties.java
 
package cn.zuowenjun.spring;
 
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
 
    private String name;
 
    private String author;
 
    private String testMsg;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getAuthor() {
        return author;
    }
 
    public void setAuthor(String author) {
        this.author = author;
    }
 
    public String getTestMsg() {
        return testMsg;
    }
 
    public void setTestMsg(String testMsg) {
        this.testMsg = testMsg;
    }
}

application.properties 配置文件:(注意 app.test.msg 此处改为了 app.test-msg,因为这样就可以直接映射到类的属性中,否则得定义内部类有点麻烦)

1
2
3
app.name=demo spring console
app.author=zuowenjun
app.test-msg=hello java spring console app!

 最后改造 spring boot application 类,让 SpringbootConsoleApplication 类实现 ApplicationRunner 接口,并在 run 方法中编写通过属性依赖注入获得 HostService 类的实例,最后执行 HostService 的 run 方法即可,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cn.zuowenjun.spring;
 
import cn.zuowenjun.spring.services.HostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class SpringbootConsoleApplication implements ApplicationRunner {
 
    @Autowired
    private HostService hostService;
 
    public static void main(String[] args) {
        SpringApplication.run(SpringbootConsoleApplication.class, args);
    }
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
        hostService.run();
    }
}

 如上步骤即完成了优雅编写 spring boot 控制台程序,关键点是 ApplicationRunner,这个是给 spring boot 执行的入口,另一种思路,我们其实还可以把 HostService 类改造一下,让其实现 ApplicationRunner 接口,那么 run 方法即为 spring boot 的启动入口。

总结一下:.

NET CORE 控制台程序优雅姿势一与 Spring 控制台优雅姿势一核心思想是一样的,都是手动创建各个依赖组件及 IOC 容器的实例,都是通过 IOC 容器显式的解析获得 HostService 类的实例,最后运行 HostService#run 方法。

NET CORE 控制台程序优雅姿势二与 Spring 控制台优雅姿势二核心思想也是一样的,都是利用 IOC 容器来直接管理注册的各个依赖组件,并由.NET CORE、Spring boot 框架自行调度 HostService#run 方法。

我个人更倾向优雅姿势二的方法来编写.NET CORE 或 Spring Boot 的控制台程序,因为写得更少,做得更多。