国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring Boot 系統(tǒng)初始化器詳解

這篇具有很好參考價值的文章主要介紹了Spring Boot 系統(tǒng)初始化器詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Spring Boot 3.x系列文章

  1. Spring Boot 2.7.8 中文參考指南(一)
  2. Spring Boot 2.7.8 中文參考指南(二)-Web
  3. Spring Boot 源碼閱讀初始化環(huán)境搭建
  4. Spring Boot 框架整體啟動流程詳解
  5. Spring Boot 系統(tǒng)初始化器詳解

自定義系統(tǒng)初始化器

Spring Boot 有多種加載自定義初始化器的方法:
1、創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在spring.factories中添加,如MyInitializer
2、創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在SpringApplication 中使用addInitializers添加,如MyInitializer2
3、創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在application.ymlapplication.properties中使用context.initializer.classes添加,如MyInitializer3
4、創(chuàng)建一個實現(xiàn)EnvironmentPostProcessor接口的類,在spring.factories中添加,如MyEnvironmentPostProcessor

代碼如下所示:

MyInitializer.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(2)
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key", "value");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        configurableEnvironment.getPropertySources().addLast(mapPropertySource);
        log.info("My Initializer run");
    }
}

MyInitializer2.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(1)
public class MyInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key2", "value2");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        configurableEnvironment.getPropertySources().addLast(mapPropertySource);
        log.info("My Initializer2 run");
    }
}

MyInitializer3.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(10)
public class MyInitializer3 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key3", "value3");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        configurableEnvironment.getPropertySources().addLast(mapPropertySource);
        log.info("My Initializer3 run");
    }
}

MyEnvironmentPostProcessor.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(5)
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Map<String, Object> map = new HashMap<>();
        map.put("key", "value");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        environment.getPropertySources().addLast(mapPropertySource);
        //為什么不打印日志
//        log.info("My EnvironmentPostProcessor run");
        System.out.println("My EnvironmentPostProcessor run");
    }
}

啟動后截圖:
Spring Boot 系統(tǒng)初始化器詳解

疑問?

  • 在MyEnvironmentPostProcessor的示例中,用log.info("My EnvironmentPostProcessor run"); 不會打印日志。
  • MyInitializer3的輸出怎么會在MyInitializer2之前。

加載原理

實例1加載原理

在之前的文章中《Spring Boot 框架整體啟動流程詳解》有介紹到Spring Boot 應(yīng)用程序初始化的時候會從META-INF/spring.factories加載ApplicationContextInitializer類實例

Spring Boot 系統(tǒng)初始化器詳解
SpringFactoriesLoader 是Spring 框架中的類,用于從多個Jar文件的META-INF/spring.factories中加載并實例化給定的類型,spring.factories文件必須采用Properties格式,其中key是接口或抽象類的完全限定名稱,value是以逗號分隔的實現(xiàn)類名列表。例如:
example.MyService=example.MyServicesImpl1,example.MyService Impl2
其中example.MyService是接口的名稱,MyServiceImpl1和MyServiceImpl2是兩個實現(xiàn)。

獲取實例分成了兩部分,首先從多個Jar文件的META-INF/spring.factories中加載key和value,返回一個SpringFactoriesLoader實例,然后調(diào)用SpringFactoriesLoader的load方法初始化指定key(key為接口或者抽象類的全限定名)對應(yīng)的所有value(接口實現(xiàn)類),返回實例列表。

spring.factories的加載

Spring Boot 系統(tǒng)初始化器詳解
FACTORIES_RESOURCE_LOCATION指定了加載的路徑

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
// 判斷資源路徑是否為空,若為空則拋出異常
	Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
// 獲取資源對應(yīng)的類加載器,若傳入的類加載器為空,則使用SpringFactoriesLoader類的類加載器
	ClassLoader resourceClassLoader = (classLoader != null ? classLoader :
			SpringFactoriesLoader.class.getClassLoader());
// 從緩存中獲取SpringFactoriesLoader,若不存在,則創(chuàng)建一個并緩存 Map<String, SpringFactoriesLoader>,key為ClassLoader,資源對應(yīng)的類加載器
	Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(
			resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
// 返回resourceLocation對應(yīng)的SpringFactoriesLoader對象,若不存在,則創(chuàng)建一個并緩存,key為resourceLocation,資源路徑
	return loaders.computeIfAbsent(resourceLocation, key ->
			new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}

computeIfAbsent 返回的是key關(guān)聯(lián)的value值

最后一步value創(chuàng)建了一個SpringFactoriesLoader實例,loadFactoriesResource 使用給定的資源類加載器從"META-INF/spring.factories"中加載

protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
//實現(xiàn)列表,key=接口或抽象類全限定名 value=實現(xiàn)類全限定名
	Map<String, List<String>> result = new LinkedHashMap<>();
	try {
	//獲取指定路徑下所有的資源URL
		Enumeration<URL> urls = classLoader.getResources(resourceLocation);
		while (urls.hasMoreElements()) {
			UrlResource resource = new UrlResource(urls.nextElement());
			//從URL資源中讀取配置
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			properties.forEach((name, value) -> {
			//實現(xiàn)類逗號分割,轉(zhuǎn)換為數(shù)組
				String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);
				//接口的實現(xiàn)類列表
				List<String> implementations = result.computeIfAbsent(((String) name).trim(),
						key -> new ArrayList<>(factoryImplementationNames.length));

//去掉實現(xiàn)類兩邊空格,并插入實現(xiàn)類列表
				Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);
			});
		}
		
//去重
result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);
	}
	//返回不可修改的map
	return Collections.unmodifiableMap(result);
}

加載部分有很多的key,value 要分清楚。

spring.factories接口實現(xiàn)類的實例化

實例化通過調(diào)用SpringFactoriesLoader的load方法

	public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {
		return load(factoryType, argumentResolver, null);
	}

factoryType指定要實例化的類型,這里為 org.springframework.context.ApplicationContextInitializer
argumentResolver 實例化需要的參數(shù),這里為null

public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,
		@Nullable FailureHandler failureHandler) {

	Assert.notNull(factoryType, "'factoryType' must not be null");
	//從factories 中獲取指定接口類型的所有實現(xiàn)
	//factories就是加載步驟中返回的result
	List<String> implementationNames = loadFactoryNames(factoryType);
	logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
	List<T> result = new ArrayList<>(implementationNames.size());
	//定義失敗處理器
	FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;
	//循環(huán),實例化
	for (String implementationName : implementationNames) {
	//通過構(gòu)造函數(shù)實例化
		T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
		if (factory != null) {
			result.add(factory);
		}
	}
	//根據(jù)order 排序
	AnnotationAwareOrderComparator.sort(result);
	return result;
}

最終返回排序后的ApplicationContextInitializer 實例,賦值SpringApplication 的 initializers 變量。

執(zhí)行

執(zhí)行會在SpringApplication類的prepareContext(準備上下文)中進行調(diào)用,如圖所示:
Spring Boot 系統(tǒng)初始化器詳解

	//返回一個只讀的有序,LinkedHashSet 類型
	public Set<ApplicationContextInitializer<?>> getInitializers() {
		return asUnmodifiableOrderedSet(this.initializers);
	}
	protected void applyInitializers(ConfigurableApplicationContext context) {
	//獲取所有的ApplicationContextInitializer實例
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			// 判斷ApplicationContextInitializer實例泛型是否與context對象類型一致
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			// 調(diào)用ApplicationContextInitializer實例的initialize方法進行初始化操作
			initializer.initialize(context);
		}
	}

實例2加載原理

創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在SpringApplication 中使用addInitializers添加,如MyInitializer2。

我們使用addInitializers 將ApplicationContextInitializer接口的實現(xiàn)加入到SpringApplication中。

	public void addInitializers(ApplicationContextInitializer<?>... initializers) {
		this.initializers.addAll(Arrays.asList(initializers));
	}

initializers 就是 SpringApplication中的initializers變量,執(zhí)行點同實例1,在準備上下文的時候執(zhí)行,由于執(zhí)行前會進行一次排序,所以他們兩的順序是正確的。

實例3加載原理

創(chuàng)建一個實現(xiàn)ApplicationContextInitializer接口的類,在application.ymlapplication.properties中使用context.initializer.classes添加,如MyInitializer3

該處通過配置文件添加ApplicationContextInitializer實現(xiàn)類,并且通過DelegatingApplicationContextInitializer 初始化器進行加載和執(zhí)行。

Spring Boot 系統(tǒng)初始化器詳解
DelegatingApplicationContextInitializer 被定義在了spring-boot.jar 的 META-INF/spring.factories中,并且由于他的order是0,所以會在我們自定義MyInitializer和MyInitializer2 前執(zhí)行,它是另外一種獨立的初始化器,專門用于將配置文件中的ApplicationContextInitializer實現(xiàn)類加載到Spring容器中。

Spring Boot 系統(tǒng)初始化器詳解
執(zhí)行在DelegatingApplicationContextInitializer類的applyInitializers方法中

private void applyInitializers(ConfigurableApplicationContext context,
		List<ApplicationContextInitializer<?>> initializers) {
		//排序
	initializers.sort(new AnnotationAwareOrderComparator());
	for (ApplicationContextInitializer initializer : initializers) {
	//調(diào)用initialize方法
		initializer.initialize(context);
	}
}

實例4加載原理

創(chuàng)建一個實現(xiàn)EnvironmentPostProcessor接口的類,在spring.factories中添加,如MyEnvironmentPostProcessor。
實例4是在所有的測試中最先打印日志的,是因為它是在prepareEnvironment(準備環(huán)境)中執(zhí)行,而前面3個實例都是在prepareContext(準備上下文)中執(zhí)行。
該實例中EventPublishingRunListener會調(diào)用prepareEnvironment方法,EventPublishingRunListener被定義在Spring Boot Jar包的META-INF/spring.factories中,用于發(fā)布各種SpringApplicationEvent事件。

EventPublishingRunListener類中

public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
		ConfigurableEnvironment environment) {
		//廣播環(huán)境準備完成事件
	multicastInitialEvent(
			new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}

private void multicastInitialEvent(ApplicationEvent event) {
	//刷新SimpleApplicationEventMulticaster中的事件列表
	refreshApplicationListeners();
	//廣播事件
	this.initialMulticaster.multicastEvent(event);
}

private void refreshApplicationListeners() {
	this.application.getListeners().forEach(this.initialMulticaster::addApplicationListener);
}

SimpleApplicationEventMulticaster類中

public void multicastEvent(ApplicationEvent event) {
	multicastEvent(event, null);
}

@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
	// 獲取執(zhí)行事件的線程池
	Executor executor = getTaskExecutor();
	//獲取指定事件類型的事件集合
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
	//如果定義了執(zhí)行線程池,則用線程池調(diào)用
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
		//同步調(diào)用監(jiān)聽器
			invokeListener(listener, event);
		}
	}
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//獲取失敗處理器
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) {
		try {
			doInvokeListener(listener, event);
		}
		catch (Throwable err) {
			errorHandler.handleError(err);
		}
	}
	else {
		doInvokeListener(listener, event);
	}
}

@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
	//此處執(zhí)行事件監(jiān)聽器的onApplicationEvent方法
		listener.onApplicationEvent(event);
	}
	catch (ClassCastException ex) {
		String msg = ex.getMessage();
		if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
				(event instanceof PayloadApplicationEvent payloadEvent &&
						matchesClassCastMessage(msg, payloadEvent.getPayload().getClass()))) {
			// Possibly a lambda-defined listener which we could not resolve the generic event type for
			// -> let's suppress the exception.
			Log loggerToUse = this.lazyLogger;
			if (loggerToUse == null) {
				loggerToUse = LogFactory.getLog(getClass());
				this.lazyLogger = loggerToUse;
			}
			if (loggerToUse.isTraceEnabled()) {
				loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
			}
		}
		else {
			throw ex;
		}
	}
}

listener.onApplicationEvent(event);處,在本例中為EnvironmentPostProcessorApplicationListener

EnvironmentPostProcessorApplicationListener類中:

public void onApplicationEvent(ApplicationEvent event) {
//根據(jù)各個事件類型分別去處理
	if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
		onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
	}
	if (event instanceof ApplicationPreparedEvent) {
		onApplicationPreparedEvent();
	}
	if (event instanceof ApplicationFailedEvent) {
		onApplicationFailedEvent();
	}
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	ConfigurableEnvironment environment = event.getEnvironment();
	SpringApplication application = event.getSpringApplication();
	// 獲取所有的 EnvironmentPostProcessor,然后執(zhí)行其 postProcessEnvironment 方法
	for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
			event.getBootstrapContext())) {
		postProcessor.postProcessEnvironment(environment, application);
	}
}

// 獲取所有的 EnvironmentPostProcessor
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
		ConfigurableBootstrapContext bootstrapContext) {
	ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
	//postProcessorsFactory 是一個函數(shù)表達式
	EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
	return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}

EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);中的postProcessorsFactory是在EnvironmentPostProcessorApplicationListener實例化的時候初始化,根據(jù)前面的文章我們知道EnvironmentPostProcessorApplicationListener是一個監(jiān)聽器,會在SpringBoot初始化的時候初始化。

public EnvironmentPostProcessorApplicationListener() {
	this(EnvironmentPostProcessorsFactory::fromSpringFactories);
}

private EnvironmentPostProcessorApplicationListener(
		Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory) {
	this.postProcessorsFactory = postProcessorsFactory;
	this.deferredLogs = new DeferredLogs();
}

static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
	return new SpringFactoriesEnvironmentPostProcessorsFactory(
			SpringFactoriesLoader.forDefaultResourceLocation(classLoader));
}

EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) 會在EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader); apply的時候調(diào)用,如果沒有加載META-INF/spring.factories會再這里再次加載。

EnvironmentPostProcessorsFactory 的主要作用是實例化EnvironmentPostProcessorSpringFactoriesEnvironmentPostProcessorsFactory是其子類。

SpringFactoriesEnvironmentPostProcessorsFactory類中:

public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
		ConfigurableBootstrapContext bootstrapContext) {
	ArgumentResolver argumentResolver = ArgumentResolver.of(DeferredLogFactory.class, logFactory);
	//向argumentResolver對象中添加ConfigurableBootstrapContext.class和bootstrapContext,獲取更新后的argumentResolver對象
	argumentResolver = argumentResolver.and(ConfigurableBootstrapContext.class, bootstrapContext);
	// // 向argumentResolver對象中添加BootstrapRegistry.class和bootstrapContext,獲取更新后的argumentResolver對象
	argumentResolver = argumentResolver.and(BootstrapContext.class, bootstrapContext);
	 通過this.loader.load方法加載EnvironmentPostProcessor類型的對象,參數(shù)為argumentResolver
	argumentResolver = argumentResolver.and(BootstrapRegistry.class, bootstrapContext);
	//加載EnvironmentPostProcessor類型的對象
	return this.loader.load(EnvironmentPostProcessor.class, argumentResolver);
}

最后循環(huán)調(diào)用postProcessor.postProcessEnvironment(environment, application);完成執(zhí)行。

總結(jié)

同樣的,用一張圖來總結(jié)本文整個流程:
Spring Boot 系統(tǒng)初始化器詳解


作者其他文章:
Prometheus 系列文章

  1. Prometheus 的介紹和安裝
  2. 直觀感受PromQL及其數(shù)據(jù)類型
  3. PromQL之選擇器和運算符
  4. PromQL之函數(shù)
  5. Prometheus 告警機制介紹及命令解讀
  6. Prometheus 告警模塊配置深度解析
  7. Prometheus 配置身份認證
  8. Prometheus 動態(tài)拉取監(jiān)控服務(wù)
  9. Prometheus 監(jiān)控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介紹和安裝
  2. Grafana監(jiān)控大屏配置參數(shù)介紹(一)
  3. Grafana監(jiān)控大屏配置參數(shù)介紹(二)
  4. Grafana監(jiān)控大屏可視化圖表
  5. Grafana 查詢數(shù)據(jù)和轉(zhuǎn)換數(shù)據(jù)
  6. Grafana 告警模塊介紹
  7. Grafana 告警接入飛書通知

Spring Boot Admin 系列文章來源地址http://www.zghlxwxcb.cn/news/detail-509230.html

  1. Spring Boot Admin 參考指南
  2. SpringBoot Admin服務(wù)離線、不顯示健康信息的問題
  3. Spring Boot Admin2 @EnableAdminServer的加載
  4. Spring Boot Admin2 AdminServerAutoConfiguration詳解
  5. Spring Boot Admin2 實例狀態(tài)監(jiān)控詳解
  6. Spring Boot Admin2 自定義JVM監(jiān)控通知
  7. Spring Boot Admin2 自定義異常監(jiān)控
  8. Spring Boot Admin 監(jiān)控指標接入Grafana可視化

到了這里,關(guān)于Spring Boot 系統(tǒng)初始化器詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包