欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn)

 更新時(shí)間:2023年04月20日 15:56:44   作者:理想萬(wàn)歲萬(wàn)萬(wàn)歲  
本文主要介紹了springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、介紹

使用springboot開(kāi)發(fā)的同學(xué)們,都一定會(huì)從配置文件application.yml中讀取配置。比如我們常常會(huì)在上傳文件的功能中,把文件的保存路徑寫(xiě)在配置文件中,然后在代碼中通過(guò)@Value()注解從配置文件讀取對(duì)應(yīng)的配置,如下所示:

在配置文件中定義文件路徑

file:
  location: /data/files

在代碼中獲取保存路徑

@Component
public class upload {
    @Value("${file.location}")
    private String fileLocation; // 文件路徑/data/files
    
    public void upload(File file) {
        // 將文件保存到fileLocation中。
    }
}

這種讀取配置的方式非常方便,但是有一個(gè)讓人抓狂的缺點(diǎn)

多人協(xié)作開(kāi)發(fā)的情況下,同事A在配置文件中修改file.location的值為E:\\后將代碼提交到git倉(cāng)庫(kù),這時(shí)同事B把最新代碼拉下來(lái)后由于他的電腦中不存在E盤(pán)導(dǎo)致該功能出現(xiàn)bug,很多同學(xué)不嫌麻煩,每次拉下最新代碼后都會(huì)把這種配置重新修改以適合自己電腦的要求。

幸運(yùn)的是,springboot在讀取配置參數(shù)方面為我們提供了多種方式,并且不同方式之間存在優(yōu)先級(jí)差異,如命令行配置的優(yōu)先級(jí)大于配置文件的優(yōu)先級(jí)。如下圖為springboot官方的描述

從上圖可知,命令行配置是在非單元測(cè)試環(huán)境下優(yōu)先級(jí)最高的。

在我們通過(guò)java -jar命令啟動(dòng)項(xiàng)目時(shí),添加額外的參數(shù),就可以解決上面提及的多人協(xié)作開(kāi)發(fā)的問(wèn)題了。

二、通過(guò)應(yīng)用程序參數(shù)獲取配置

當(dāng)我們使用IDEA啟動(dòng)springboot項(xiàng)目時(shí),可以對(duì)項(xiàng)目的啟動(dòng)設(shè)置命令行參數(shù),命令行參數(shù)的格式為--name=value--name,如下所示

1. 通過(guò)bean獲取應(yīng)用程序參數(shù)

啟動(dòng)項(xiàng)目后,我們從IOC容器中獲取命令行參數(shù)對(duì)應(yīng)的beanspringApplicationArguments,再?gòu)脑揵ean中就可以獲取到我們?cè)诿钚兄信渲玫膮?shù)了。

springboot悄悄替我們向IOC容器中注冊(cè)一個(gè)ApplicationArguments類(lèi)型的bean,beanName為springApplicationArguments,該bean中保存著我們?cè)O(shè)置的應(yīng)用程序參數(shù)。

@SpringBootApplication
public class ArgumentApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(ArgumentApplication.class, args);

        // 獲取應(yīng)用程序參數(shù)
        ApplicationArguments applicationArguments =(ApplicationArguments)applicationContext
            																	.getBean("springApplicationArguments");
        // 獲取命令行中name的配置
        List<String> name = applicationArguments.getOptionValues("name");
        System.out.println(name);
    }
}

輸出如下所示

當(dāng)然,你也可以通過(guò)@Autowired的方式在類(lèi)里注入ApplicationArguments實(shí)例來(lái)獲取其中的配置。

2. 通過(guò)@Value注解獲取

當(dāng)然我們更常用的方式是通過(guò)@Value注解來(lái)獲取,如下所示

新建一個(gè)ComponentA,并用@Component注解標(biāo)注為springBean,然后為其定義@Value標(biāo)注的成員變量name

@Component
public class ComponentA {

    @Value("${name}")
    private String name;

    public ComponentA() {
    }

    public String getName() {
        return name;
    }
}

項(xiàng)目啟動(dòng)后,從IOC容器中獲取ComponentA,并調(diào)用getName()方法來(lái)驗(yàn)證name的值

@SpringBootApplication
public class ArgumentApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(ArgumentApplication.class, args);

        // 從配置文件中獲取
        ComponentA componentA = (ComponentA) applicationContext.getBean("componentA");
        System.out.println(componentA.getName());
    }
}

輸出,結(jié)果符合預(yù)期

三、源碼解讀 - 封裝應(yīng)用程序參數(shù)

springboot通過(guò)啟動(dòng)類(lèi)的main()方法接收命令行中以--定義的應(yīng)用程序參數(shù),將參數(shù)按照不同類(lèi)型以Map<String, List<String>>List<String>保存并封裝到CommandLineArgs對(duì)象中,然后以name="commandLineArgs",source=CommandLineArgs對(duì)象將其封裝到Source中,而SourceApplicationArguments內(nèi)部屬性,springboot將ApplicationArguments注入IOC容器。

從上面的例子中我們發(fā)現(xiàn),springboot把我們配置的命令行參數(shù)封裝到ApplicationArguments了,而ApplicationArguments又被springboot注冊(cè)到IOC容器中,其對(duì)應(yīng)的beanName為"springApplicationArguments",下面我們通過(guò)分析源碼來(lái)逐步解開(kāi)它是如何操作的。

首先,大家在寫(xiě)springboot啟動(dòng)類(lèi)時(shí),有沒(méi)有注意到其中main()方法的參數(shù)String[] args,如下所示

@SpringBootApplication
public class ArgumentApplication {

    public static void main(String[] args) {
        SpringApplication.run(ArgumentApplication.class, args);
    }
}

但這個(gè)參數(shù)想必有很多同學(xué)不知道它是干嘛用的,它的作用就是用來(lái)接收啟動(dòng)命令中設(shè)置的--name=key參數(shù),比如java -jarApplication.jar --name=key ,我們可以通過(guò)斷點(diǎn)進(jìn)行驗(yàn)證

在源碼run()方法中我們追蹤args這個(gè)參數(shù)的調(diào)用鏈如下:

public ConfigurableApplicationContext run(String... args) {
    // ...
    SpringApplicationRunListeners listeners = getRunListeners(args);
	// ...
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // ...
}

從源碼可以看出,參數(shù)args可以被用來(lái)獲取運(yùn)行監(jiān)聽(tīng)器構(gòu)造應(yīng)用參數(shù),因此我們把注意力放在構(gòu)造應(yīng)用參數(shù)上來(lái)。

1. DefaultApplicationArguments

看一下該類(lèi)的結(jié)構(gòu),從它的構(gòu)造方法我們得知,該類(lèi)是把我們傳入的--應(yīng)用程序參數(shù)封裝成一個(gè)Source對(duì)象,同時(shí)也保存一份原始的args參數(shù),當(dāng)我們需要獲取參數(shù)時(shí),都是調(diào)用Source對(duì)象提供的方法獲取的,因此Source這個(gè)類(lèi)尤其關(guān)鍵,我們需要弄清楚它是如何分析應(yīng)用程序參數(shù)并將其封裝到Source中的。

public class DefaultApplicationArguments implements ApplicationArguments {

	private final Source source;
	private final String[] args;

	public DefaultApplicationArguments(String... args) {
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args);
		this.args = args;
	}
	// ...
	private static class Source extends SimpleCommandLinePropertySource {

		Source(String[] args) {
			super(args);
		}
        // ...
	}
}

2. Source類(lèi)

Source類(lèi)是DefaultApplicationArguments的內(nèi)部類(lèi),上面已經(jīng)展示其具體實(shí)現(xiàn)的源碼,它的構(gòu)造函數(shù)就是把接收的應(yīng)用程序參數(shù)傳遞給父類(lèi)的構(gòu)造函數(shù)。

下面我們看一下他的UML圖

由于Source的構(gòu)造函數(shù)直接把參數(shù)args交給其父類(lèi)的構(gòu)造函數(shù),而Source本身沒(méi)有多余的處理,因此我們直接進(jìn)入其父類(lèi)SimpleCommandLinePropertySource。

3. SimpleCommandLinePropertySource

public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {

	public SimpleCommandLinePropertySource(String... args) {
		super(new SimpleCommandLineArgsParser().parse(args));
	}

	public SimpleCommandLinePropertySource(String name, String[] args) {
		super(name, new SimpleCommandLineArgsParser().parse(args));
	}
}

在這個(gè)類(lèi)中,又是直接調(diào)用父類(lèi)的構(gòu)造方法,且沒(méi)有自身的實(shí)現(xiàn)。但不同的,這里將我們?cè)O(shè)置的應(yīng)用程序進(jìn)行轉(zhuǎn)換成CommandLineArgs對(duì)象交給父類(lèi)構(gòu)造函數(shù)。

它是怎么分析我們傳入的應(yīng)用程序參數(shù)的,又將其轉(zhuǎn)換成什么樣的結(jié)構(gòu)呢?

4. SimpleCommandLineArgsParser

該類(lèi)只有一個(gè)靜態(tài)方法parse(),從命名也可以看出,該類(lèi)的功能就是對(duì)命令行參數(shù)提供簡(jiǎn)單的轉(zhuǎn)換器。

class SimpleCommandLineArgsParser {

	public CommandLineArgs parse(String... args) {
		CommandLineArgs commandLineArgs = new CommandLineArgs();
		for (String arg : args) {
            // 以 -- 開(kāi)頭的應(yīng)用程序參數(shù)
			if (arg.startsWith("--")) {
				String optionText = arg.substring(2);
				String optionName;
				String optionValue = null;
				int indexOfEqualsSign = optionText.indexOf('=');
				if (indexOfEqualsSign > -1) {
                    // --key=value這種形式的參數(shù)
					optionName = optionText.substring(0, indexOfEqualsSign);
					optionValue = optionText.substring(indexOfEqualsSign + 1);
				}
				else {
                    // --key這種形式的參數(shù)
					optionName = optionText;
				}
				if (optionName.isEmpty()) {
					throw new IllegalArgumentException("Invalid argument syntax: " + arg);
				}
				commandLineArgs.addOptionArg(optionName, optionValue);
			}
			else {
                // 不以 -- 開(kāi)頭的應(yīng)用程序參數(shù)
				commandLineArgs.addNonOptionArg(arg);
			}
		}
		return commandLineArgs;
	}
}

從源碼得知,應(yīng)用程序參數(shù)的轉(zhuǎn)換過(guò)程非常簡(jiǎn)單,就是根據(jù)--=進(jìn)行字符串裁剪,然后將這些參數(shù)封裝到CommandLineArgs里。而在CommandLineArgs中用不同的字段來(lái)保存不同類(lèi)型的應(yīng)用程序參數(shù)。如下

class CommandLineArgs {
	// 保存 --key=value  和 --key這兩種類(lèi)型的應(yīng)用程序參數(shù)
	private final Map<String, List<String>> optionArgs = new HashMap<>();
    // 保存 key 這一種類(lèi)型的應(yīng)用程序參數(shù)
	private final List<String> nonOptionArgs = new ArrayList<>();
}

回到上一節(jié)SimpleCommandLinePropertySource,它的構(gòu)造函數(shù)就是將應(yīng)用程序參數(shù)轉(zhuǎn)換為CommandLineArgs然后交給父類(lèi)構(gòu)造函數(shù),那下面我們看其父類(lèi)CommandLinePropertySource。

5. CommandLinePropertySource

CommandLinePropertySource中,我們主要看其構(gòu)造函數(shù)。

public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {

	public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";

	public CommandLinePropertySource(T source) {
		super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
	}
}

很顯然,又是直接調(diào)用父類(lèi)的構(gòu)造函數(shù),而且向其父類(lèi)構(gòu)造函數(shù)傳入的是"commandLineArgs"字符串 和 CommandLineArgs對(duì)象。那我們繼續(xù),進(jìn)入父類(lèi)EnumerablePropertySource,然后又將這兩個(gè)參數(shù)繼續(xù)傳遞給父類(lèi)PropertySource

public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
    
	public EnumerablePropertySource(String name, T source) {
		super(name, source);
	}
}

6. PropertySource

通過(guò)前面一系列對(duì)父類(lèi)構(gòu)造函數(shù)的調(diào)用,最終將name初始化為"commandLineArgs"字符串 ,將source初始化為 CommandLineArgs對(duì)象。

public abstract class PropertySource<T> {

	protected final String name;

	protected final T source;
    
	public PropertySource(String name, T source) {
		Assert.hasText(name, "Property source name must contain at least one character");
		Assert.notNull(source, "Property source must not be null");
		this.name = name;
		this.source = source;
	}
}

四、源碼解讀 - 為什么可以通過(guò)@Value注解獲取參數(shù)配置

在前面我們將應(yīng)用程序參數(shù)封裝到ApplicationArguments對(duì)象中后,springboot又將這些應(yīng)用程序參數(shù)添加到environment對(duì)象中,并且對(duì)已存在的配置進(jìn)行覆蓋,因此與配置文件中定義的參數(shù)類(lèi)似,都可以通過(guò)@Value注解獲取。

在下面的源碼中,主要表達(dá)的是應(yīng)用程序參數(shù)在各個(gè)方法調(diào)用中的傳遞,最關(guān)鍵的部分我們要看configurePropertySources()方法。該方法將應(yīng)用程序參數(shù)配置到運(yùn)行環(huán)境environment。

public ConfigurableApplicationContext run(String... args) {
    // ...
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
	ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
	// ...
}

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
}

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    // ...
    configurePropertySources(environment, args);
    // ...
}

// 將應(yīng)用程序設(shè)置到environment對(duì)象中,與配置文件中的參數(shù)處于同一environment對(duì)象中,因此可以通過(guò)@Value注解獲取參數(shù)配置
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    MutablePropertySources sources = environment.getPropertySources();
    DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
    if (this.addCommandLineProperties && args.length > 0) {
        String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
        if (sources.contains(name)) {
            // 環(huán)境中已存在相同的配置,則進(jìn)行覆蓋
            PropertySource<?> source = sources.get(name);
            CompositePropertySource composite = new CompositePropertySource(name);
            composite.addPropertySource(
                new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
            composite.addPropertySource(source);
            sources.replace(name, composite);
        }
        else {
            sources.addFirst(new SimpleCommandLinePropertySource(args));
        }
    }
}

五、源碼解讀 - 將應(yīng)用程序參數(shù)注冊(cè)到IOC容器

在前面的章節(jié),我們通過(guò)源碼分析得出結(jié)論,springboot將應(yīng)用程序參數(shù)封裝到ApplicationArguments和運(yùn)行環(huán)境Environment中。接下來(lái)我們看它是如何注冊(cè)到IOC容器的。

public ConfigurableApplicationContext run(String... args) {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    // ...
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
	// ...
}

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
    // ...
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    // ...
}

springboot將應(yīng)用程序參數(shù)ApplicationArguments直接通過(guò)beanFactory.registerSingleton()方法手動(dòng)地注冊(cè)到IOC容器中,beanName為springApplicationArguments。

六、總結(jié)

springboot將我們配置的命令行參數(shù)封裝到ApplicationArguments,并使用"springApplicationArguments"作為beanName將其注冊(cè)到IOC容器。

設(shè)置應(yīng)用程序參數(shù)時(shí),符合要求的設(shè)置為:--key=value--key 以及 key。可以通過(guò)@Value注解直接獲取應(yīng)用程序參數(shù)??梢酝ㄟ^(guò)@Autowired依賴(lài)注入一個(gè)ApplicationArguments實(shí)例來(lái)讀取應(yīng)用程序參數(shù)。
 

到此這篇關(guān)于springboot加載命令行參數(shù)ApplicationArguments的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)springboot加載命令行參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java 文件流的處理方式 文件打包成zip

    java 文件流的處理方式 文件打包成zip

    這篇文章主要介紹了java 文件流的處理方式 文件打包成zip,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java總結(jié)篇系列:Java泛型詳解

    Java總結(jié)篇系列:Java泛型詳解

    下面小編就為大家?guī)?lái)一篇Java總結(jié)篇系列:Java泛型詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-09-09
  • 聊聊Spring——AOP詳解(AOP概覽)

    聊聊Spring——AOP詳解(AOP概覽)

    這篇文章主要介紹了Spring——AOP詳解(AOP概覽),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java存儲(chǔ)以及java對(duì)象創(chuàng)建的流程(詳解)

    java存儲(chǔ)以及java對(duì)象創(chuàng)建的流程(詳解)

    下面小編就為大家?guī)?lái)一篇java存儲(chǔ)以及java對(duì)象創(chuàng)建的流程(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Java利用策略模式優(yōu)化過(guò)多if else代碼

    Java利用策略模式優(yōu)化過(guò)多if else代碼

    這篇文章主要介紹了Java利用策略模式優(yōu)化過(guò)多if else代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 利用openoffice+jodconverter-code-3.0-bate4實(shí)現(xiàn)ppt轉(zhuǎn)圖片

    利用openoffice+jodconverter-code-3.0-bate4實(shí)現(xiàn)ppt轉(zhuǎn)圖片

    這篇文章主要為大家詳細(xì)介紹了利用openoffice+jodconverter-code-3.0-bate4實(shí)現(xiàn)ppt轉(zhuǎn)圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • springboot操作靜態(tài)資源文件的方法

    springboot操作靜態(tài)資源文件的方法

    這篇文章主要介紹了springboot操作靜態(tài)資源文件的方法,本文給大家提到了兩種方法,小編在這里比較推薦第一種方法,具體內(nèi)容詳情大家跟隨腳本之家小編一起看看吧
    2018-07-07
  • Java線(xiàn)程通訊的實(shí)現(xiàn)方法總結(jié)

    Java線(xiàn)程通訊的實(shí)現(xiàn)方法總結(jié)

    線(xiàn)程通訊指的是多個(gè)線(xiàn)程之間通過(guò)共享內(nèi)存或消息傳遞等方式來(lái)協(xié)調(diào)和同步它們的執(zhí)行,線(xiàn)程通訊的實(shí)現(xiàn)方式主要有以下兩種:共享內(nèi)存和消息傳遞,本文詳細(xì)介紹了Java線(xiàn)程是如何通訊的,感興趣的同學(xué)可以參考閱讀
    2023-05-05
  • Spring Boot2.x集成JPA快速開(kāi)發(fā)的示例代碼

    Spring Boot2.x集成JPA快速開(kāi)發(fā)的示例代碼

    這篇文章主要介紹了Spring Boot2.x集成JPA快速開(kāi)發(fā),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Java十大經(jīng)典排序算法圖解

    Java十大經(jīng)典排序算法圖解

    這篇文章主要介紹了Java十大經(jīng)典排序算法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-11-11

最新評(píng)論