成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Framework&Spring Boot集成apollo源碼分析

開(kāi)發(fā) 架構(gòu)
ApolloConfigRegistrar最終將apollo中BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor、BeanPostProcessor實(shí)現(xiàn)類的注冊(cè)到spring容器中(具體實(shí)現(xiàn)類與Spring Framework部分基本是一致的),后續(xù)的邏輯與Spring Framework部分分析的是一致的了。

引言

為了在項(xiàng)目中用好框架,以及出現(xiàn)問(wèn)題時(shí)候能夠快速定位、分析、優(yōu)化,文章嘗試從源碼角度分析Spring集成apollo的過(guò)程。期望文章能夠把以下幾個(gè)事情描述清楚:

  • apollo通過(guò)使用Spring哪些擴(kuò)展點(diǎn),完成了與Spring的集成;
  • apollo中的配置如何融入到Spring Environment;
  • apollo中的配置項(xiàng)如何賦值給Spring Bean相關(guān)字段、方法;
  • 在應(yīng)用運(yùn)行過(guò)程中,當(dāng)修改apollo中的配置,配置如何在Spring Bean相關(guān)字段、方法上生效的

由于Spring Framework和Spring Boot集成apollo的方式有些許不同,分別進(jìn)行分析。

Spring Framework

示例

Spring XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.ctrip.com/schema/apollo
http://www.ctrip.com/schema/apollo.xsd"
>
<apollo:config namespaces="application,public_namespace"/>
<context:component-scan base-package="test.springframework"/>

<bean id="test" class="test.springframework.Test">
<property name="test1" value="${test1}"/>
</bean>
</beans>
  • apollo:config配置的是apollo app.id中的命令空間(app.id、app.meta配置可以參考apollo官方文檔);
  • context:component-scan配置的是Spring component注解的掃描路徑;
  • 另一個(gè)重要的配置是xmlns:apollo="http://www.ctrip.com/schema/apollo",后面內(nèi)容會(huì)有分析。

Spring Bean

package test.springframework;
public class Test {
@ApolloConfig
private Config config;
@Value("${test2:}")
private String test2;
private String test1;
public String getTest1(){
return test1;
}
public void setTest1(String test1){
this.test1 = test1;
}
@ApolloConfigChangeListener
public void onChange(ConfigChangeEvent changeEvent){
System.out.println(changeEvent);
}
}

集成分析

refresh時(shí)序圖

圖中概括地描述了refresh過(guò)程,其中標(biāo)識(shí)黃顏色的地方是這次分析的重點(diǎn),下面分別進(jìn)行描述。

解析apollo:config

當(dāng)解析xml文件apollo:config標(biāo)記的時(shí)候調(diào)用BeanDefinitionParserDelegate.parseCustomElement(…),主要流程如下:

個(gè)性化命名空間解析

圖中流程主要完成了兩件事:

  • 將xml中配置的apollo命名空間存儲(chǔ)到了PropertySourcesProcessor(屬于apollo jar包)類中的NAMESPACE_NAMES字段;
  • 將ConfigPropertySourcesProcessor(屬于apollo jar包)作為BeanDefinition注冊(cè)到了Spring容器中;ConfigPropertySourcesProcessor實(shí)現(xiàn)了Spring接口BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor。

調(diào)用BeanFactoryPostProcessor

apollo中ConfigPropertySourcesProcessor實(shí)現(xiàn)了Spring接口BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor,ConfigPropertySourcesProcessor實(shí)現(xiàn)如下:

public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor
implements BeanDefinitionRegistryPostProcessor {
//使用java SPI機(jī)制,ConfigPropertySourcesProcessorHelper對(duì)應(yīng)的實(shí)現(xiàn)類是DefaultConfigPropertySourcesProcessorHelper,這段代碼完成了apollo client初始化
private ConfigPropertySourcesProcessorHelper helper = ServiceBootstrap.loadPrimary(ConfigPropertySourcesProcessorHelper.class);

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
helper.postProcessBeanDefinitionRegistry(registry);
}
}

調(diào)用BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry

實(shí)際調(diào)用的是
DefaultConfigPropertySourcesProcessorHelper.postProcessBeanDefinitionRegistry方法,該方法主要完成以下幾件事:

  • 將PropertySourcesPlaceholderConfigurer注冊(cè)到spring容器中,該類實(shí)現(xiàn)了接口BeanFactoryPostProcessor,用于占位符的轉(zhuǎn)換處理;
  • 將ApolloAnnotationProcessor注冊(cè)到spring容器中,該類實(shí)現(xiàn)了接口BeanPostProcessor;
  • 將SpringValueProcessor注冊(cè)到spring容器中,該類實(shí)現(xiàn)了接口BeanFactoryPostProcessor和BeanPostProcessor;
  • 將ApolloJsonValueProcessor注冊(cè)到spring容器中,該類實(shí)現(xiàn)了接口BeanPostProcessor;
  • 將BeanDefinition中帶有占位符的所有屬性存儲(chǔ)到Map<BeanDefinitionRegistry, ?Multimap<String, SpringValueDefinition>>結(jié)構(gòu)中

調(diào)用BeanFactoryPostProcessor.postProcessBeanFactory

PropertySourcesProcessor

CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
ImmutableSortedSet<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
Iterator<Integer> iterator = orders.iterator();

while (iterator.hasNext()) {
int order = iterator.next();
for (String namespace : NAMESPACE_NAMES.get(order)) {
Config config = ConfigService.getConfig(namespace);
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
}
NAMESPACE_NAMES.clear();
environment.getPropertySources().addFirst(composite);
  • NAMESPACE_NAMES中存儲(chǔ)的是應(yīng)用配置的apollo命名空間;
  • 通過(guò)Config config = ConfigService.getConfig(namespace)獲取每個(gè)命名空間的配置對(duì)象,將Config對(duì)象封裝成ConfigPropertySource,接著將所有ConfigPropertySource放入CompositePropertySource,最后將CompositePropertySource加入到spring ConfigurableEnvironment中,此時(shí)spring容器的ConfigurableEnvironment已經(jīng)擁有了apollo命令空間的配置;
AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(environment, beanFactory);

List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
for (ConfigPropertySource configPropertySource : configPropertySources) {
configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
}
  • 為apollo命名空間對(duì)象添加監(jiān)聽(tīng)AutoUpdateConfigChangeListener,當(dāng)改變命名空間中配置的時(shí)候,該監(jiān)聽(tīng)完成Bean對(duì)象屬性值得更新,及方法的調(diào)用。

PropertySourcesPlaceholderConfigurer

  • 將ConfigurableEnvironment構(gòu)造成自身的PropertySource;
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
  • 通過(guò)自身的PropertySource構(gòu)造PropertySourcesPropertyResolver,PropertySourcesPropertyResolver中完成解析的類是PropertyPlaceholderHelper;
new PropertySourcesPropertyResolver(this.propertySources)
  • 構(gòu)造StringValueResolver,然后調(diào)用PropertySourcesPropertyResolver來(lái)進(jìn)行解析;
StringValueResolver valueResolver = strVal -> {
String resolved = (ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(nullValue) ? null : resolved);
};
  • 構(gòu)造BeanDefinitionVisitor,用于解析BeanDefinition包含的所有String值(屬性、構(gòu)造方法參數(shù)、元數(shù)據(jù)),解析找到的任何占位符。
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
if (beanDefinition.hasPropertyValues()) {
visitPropertyValues(beanDefinition.getPropertyValues());
}
if (beanDefinition.hasConstructorArgumentValues()) {
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
}

@Nullable
protected String resolveStringValue(String strVal) {
if (this.valueResolver == null) {
throw new IllegalStateException("No StringValueResolver specified - xxx");
}
// 最終調(diào)用的是PropertySourcesPropertyResolver中的resolveXXX方法String resolvedValue = this.valueResolver.resolveStringValue(strVal);
return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
}

調(diào)用BeanPostProcessor

ApolloAnnotationProcessor

用于解析apollo注解ApolloConfig、
ApolloConfigChangeListener。

ApolloConfig

為注解為ApolloConfig的字段賦值對(duì)應(yīng)的namespace Config對(duì)象。

protected void processField(Object bean, String beanName, Field field) {
ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
if (annotation == null) {
return;
}

Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()),
"Invalid type: %s for field: %s, should be Config", field.getType(), field);

String namespace = annotation.value();
Config config = ConfigService.getConfig(namespace);

ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, config);
}

ApolloConfigChangeListener

為注解為ApolloConfigChangeListener的方法添加namespace的監(jiān)聽(tīng)。

ApolloConfigChangeListener annotation = AnnotationUtils
.findAnnotation(method, ApolloConfigChangeListener.class);
String[] namespaces = annotation.value();
ConfigChangeListener configChangeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
ReflectionUtils.invokeMethod(method, bean, changeEvent);
}
};
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace);
config.addChangeListener(configChangeListener);
}

SpringValueProcessor

  • 將Bean中含有Value注解的字段、方法注冊(cè)到SpringValueRegistry(Map<BeanFactory, Multimap<String, SpringValue>>)中,SpringValue保存了Bean實(shí)例,對(duì)應(yīng)的key,F(xiàn)ield或Method;
protected void processField(Object bean, String beanName, Field field) {
Value value = field.getAnnotation(Value.class);
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
springValueRegistry.register(beanFactory, key, springValue);
}
}
protected void processMethod(Object bean, String beanName, Method method) {
Value value = method.getAnnotation(Value.class);
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
for (String key : keys) {
SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
springValueRegistry.register(beanFactory, key, springValue);
}
}
  • 將DefaultConfigPropertySourcesProcessorHelper中解析的Map<BeanDefinitionRegistry, Multimap<String, SpringValueDefinition>>轉(zhuǎn)換為SpringValue保存在SpringValueRegistry中;
  • 前面在PropertySourcesProcessor類中對(duì)每個(gè)namespace Config對(duì)象注冊(cè)了監(jiān)聽(tīng)AutoUpdateConfigChangeListener,AutoUpdateConfigChangeListener類含有SpringValueRegistry的引用;
  • 當(dāng)namespace Config配置改變的時(shí)候會(huì)通知到AutoUpdateConfigChangeListener,AutoUpdateConfigChangeListener通過(guò)配置key找到對(duì)應(yīng)的SpringValue對(duì)象,通過(guò)SpringValue改變Bean對(duì)應(yīng)的屬性,或調(diào)用Bean對(duì)應(yīng)的方法。

ApolloJsonValueProcessor

  • 將Bean中含有ApolloJsonValue注解的字段、方法注冊(cè)到SpringValueRegistry(Map<BeanFactory, Multimap<String, SpringValue>>)中,SpringValue保存了Bean實(shí)例,對(duì)應(yīng)的key,F(xiàn)ield或Method;
  • 對(duì)于注解在Field的情況,獲取ApolloJsonValue key的配置,將配置轉(zhuǎn)換為Field對(duì)應(yīng)類型的對(duì)象并完成對(duì)Field的賦值;
protected void processField(Object bean, String beanName, Field field) {
ApolloJsonValue apolloJsonValue = AnnotationUtils.getAnnotation(field, ApolloJsonValue.class);
String placeholder = apolloJsonValue.value();
Object propertyValue = placeholderHelper
.resolvePropertyValue(beanFactory, beanName, placeholder);
boolean accessible = field.isAccessible();
field.setAccessible(true);
ReflectionUtils
.setField(field, bean, parseJsonValue((String)propertyValue, field.getGenericType()));
field.setAccessible(accessible);
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
for (String key : keys) {
SpringValue springValue = new SpringValue(key, placeholder, bean, beanName, field, true);
springValueRegistry.register(beanFactory, key, springValue);
}
}
}
  • 對(duì)于注解在Method的情況,獲取ApolloJsonValue key的配置,將配置轉(zhuǎn)換為Method入?yún)?duì)應(yīng)類型的對(duì)象并完成對(duì)Method的調(diào)用
protected void processMethod(Object bean, String beanName, Method method) {
ApolloJsonValue apolloJsonValue = AnnotationUtils.getAnnotation(method, ApolloJsonValue.class);
String placeHolder = apolloJsonValue.value();
Object propertyValue = placeholderHelper
.resolvePropertyValue(beanFactory, beanName, placeHolder);
boolean accessible = method.isAccessible();
method.setAccessible(true);
ReflectionUtils.invokeMethod(method, bean, parseJsonValue((String)propertyValue, types[0]));
method.setAccessible(accessible);

if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeHolder);
for (String key : keys) {
SpringValue springValue = new SpringValue(key, apolloJsonValue.value(), bean, beanName,method, true);
springValueRegistry.register(beanFactory, key, springValue);
}
}
}

Spring Boot

示例

// Spring Boot啟動(dòng)類
@SpringBootApplication
@EnableApolloConfig
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
//測(cè)試Bean見(jiàn)Spring Framework部分示例

上面代碼中的@EnableApolloConfig是一個(gè)接入點(diǎn),后面會(huì)分析到。

集成分析

Spring Boot啟動(dòng)流程

圖中概括地描述了SpringApplication啟動(dòng)過(guò)程(其中會(huì)將啟動(dòng)類加入到spring容器中),其中標(biāo)識(shí)黃顏色的地方是這次分析的重點(diǎn),下面分別進(jìn)行描述。

加載apollo ApplicationContextInitializer

  • 通過(guò)SpringFactoriesLoader加載apollo jar包中META-INF/spring.factories文件,解析ApplicationContextInitializer實(shí)現(xiàn)類ApolloApplicationContextInitializer。
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 通過(guò)SpringFactoriesLoader加載、解析META-INF/spring.factories
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

apollo ApplicationContextInitializer initialize

  • ApolloApplicationContextInitializer解析apollo.bootstrap.namespaces配置的命名空間名稱,然后通過(guò)Config config = ConfigService.getConfig(namespace)獲取每個(gè)命名空間的配置對(duì)象,將Config對(duì)象封裝成ConfigPropertySource,接著將所有ConfigPropertySource放入CompositePropertySource,最后將CompositePropertySource加入到spring ConfigurableEnvironment中,此時(shí)spring容器的ConfigurableEnvironment已經(jīng)擁有了apollo命令空間的配置。
protected void initialize(ConfigurableEnvironment environment) {
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
for (String namespace : namespaceList) {
Config config = ConfigService.getConfig(namespace);

composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}

environment.getPropertySources().addFirst(composite);
}

初始化BeanDefinitionLoader

我們關(guān)注BeanDefinitionLoader類中屬性annotatedReader=new
AnnotatedBeanDefinitionReader(registry),AnnotatedBeanDefinitionReader構(gòu)造方法如下:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
// 將Annotation相關(guān)的處理類注冊(cè)到Spring容器中,如:// ConfigurationClassPostProcessor : 處理Configuration注解// AutowiredAnnotationBeanPostProcessor : 處理Value注解// RequiredAnnotationBeanPostProcessor : 處理Required注解// CommonAnnotationBeanPostProcessor : 處理PostConstruct/PreDestroy/Resource/Lazy注解// ... ...
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

與我們這次分析相關(guān)的類是:
ConfigurationClassPostProcessor,該類實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor接口。

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry

  • 判斷spring容器中BeanDefinition是否含有Configuration、Component、ComponentScan、Import

ImportResource、Bean注解,沒(méi)有則直接返回;

  • 構(gòu)造ConfigurationClassParser(Parses a Configuration class definition, populating a collection of ConfigurationClass objects (parsing a single Configuration class may result in any number of ConfigurationClass objects because one Configuration class may import another using the Import annotation)),接著對(duì)BeanDefinition上的Configuration、Component、ComponentScan、Import

ImportResource、Bean注解進(jìn)行解析。

  • 解析完成后,通過(guò)ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClass)進(jìn)行apollo相關(guān)BeanDefinition的加載:apollo EnableApolloConfig注解的定義如下
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 在上面parse階段會(huì)將Import封裝在ConfigurationClass中
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
int order() default Ordered.LOWEST_PRECEDENCE;
}
  • ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars會(huì)調(diào)用到ApolloConfigRegistrar類的registerBeanDefinitions方法
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata)
registrar.registerBeanDefinitions(metadata, this.registry));
}
  • ApolloConfigRegistrar最終將apollo中BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor、BeanPostProcessor實(shí)現(xiàn)類的注冊(cè)到spring容器中(具體實(shí)現(xiàn)類與Spring Framework部分基本是一致的),后續(xù)的邏輯與Spring Framework部分分析的是一致的了。
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {

private ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
// 該方法中將apollo相關(guān)類注冊(cè)到spring容器中,如:// PropertySourcesProcessor// ApolloAnnotationProcessor// SpringValueProcessor// SpringValueDefinitionProcessor// ApolloJsonValueProcessor
helper.registerBeanDefinitions(importingClassMetadata, registry);
}
}

總結(jié)

?文章主要介紹了apollo借助Spring擴(kuò)展點(diǎn)完成了與Spring的集成:Spring Framework集成方式使用到了loadBeanDefinitions階段中apollo:config NamespaceHandler,BeanDefinitionRegistryPostProcessor,BeanFactoryPostProcessor,BeanPostProcessor;Spring Boot集成方式使用到了ApplicationContextInitializer,BeanDefinitionRegistryPostProcessor,BeanFactoryPostProcessor,BeanPostProcessor;

apollo還提供了通過(guò)注解類ApolloAutoConfiguration與Spring Boot完成集成,原理是一致的。

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2009-06-22 11:54:28

Spring MVCSpringframe

2023-06-02 16:24:46

SpringBootSSM

2020-07-14 11:00:12

Spring BootRedisJava

2018-11-02 15:45:41

Spring BootRedis數(shù)據(jù)庫(kù)

2019-12-03 11:00:08

spring bootspring-kafkJava

2020-09-02 17:28:26

Spring Boot Redis集成

2009-06-18 14:45:55

Spring Fram

2011-12-26 09:17:23

JavaSpring

2024-02-23 10:33:34

SpringBean容器

2024-01-16 08:17:29

Mybatis驗(yàn)證業(yè)務(wù)

2021-01-05 05:36:39

設(shè)計(jì)Spring Boot填充

2021-12-28 11:13:05

安全認(rèn)證 Spring Boot

2021-04-28 06:26:11

Spring Secu功能實(shí)現(xiàn)源碼分析

2024-08-05 08:45:35

SpringKafkaSCRAM

2022-07-11 09:00:37

依賴配置文件Mybati

2012-02-23 12:53:40

JavaPlay Framew

2019-09-09 06:30:06

Springboot程序員開(kāi)發(fā)

2024-10-14 13:30:20

2017-09-20 09:46:38

Spring BootSpring Clou內(nèi)存

2023-11-02 18:01:24

SpringMVC配置
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 2一3sex性hd | 国产精品久久久久久久久久尿 | 日韩一区二区免费视频 | 国产在线观看一区二区三区 | 一区二区久久 | 成人一区二区三区在线观看 | 在线观看中文字幕 | 一级黄色录像毛片 | 国产成人精品一区二区 | 欧美成人二区 | 久草精品视频 | 国产激情综合五月久久 | 精品国产免费一区二区三区演员表 | 亚洲精品乱码8久久久久久日本 | 91高清在线观看 | 91资源在线| 精品国产一区二区久久 | 中文字幕在线免费观看 | 亚洲一区二区三区 | av片免费观看 | 精品久久影院 | 久久午夜电影 | 午夜精品久久久久久久星辰影院 | 国产 欧美 日韩 一区 | 中文字幕免费在线 | 亚洲一一在线 | 91网站在线看 | 久久久久久久av麻豆果冻 | 成人精品毛片 | 亚洲九色 | 日日干日日操 | 精品一二三| 久久久夜 | 四虎av电影 | 日韩在线视频免费观看 | 久久综合一区二区三区 | 欧美日本高清 | av天空| 欧美激情视频网站 | 黄色免费av | 91精品国产91久久久久久 |