アウトライン
スプリング容器は、本明細書SpringCloudの導入で特定Springコンテナを作成する点、ならびにこれらのコンテナとの間の差との関係の詳細な分析の観点からこのソースを複数のプロジェクトを作成します。
この記事では、SpringコンテナがFinchley.RELEASE関連プロジェクトに基づいて説明します。
容器は、大きく3つの層に分かれて:
- ブートストラップSpringコンテナは:SpringCloudコンテキストSpringCloudを初期化するために、リスナーによって作成されました
- SpringBootのSpringコンテナ:SpringBootによって作成されたが、このプロジェクトはまた、一般的にSpringコンテナで使用されています。
- スプリングマイクロサービス関連コンテナ:クラスに対応するリボンコンテキストふりおよび構成、容器の構成により作成NamedContextFactory抽象工場、分離のためのコンテナ。
個別に撮影。
春のブートストラップ容器
それが作成されたときに最初にブートストラップSpringコンテナを見てください。
前回のブログでは、「SpringBoot2 |プロセスのソースコード解析(a)の開始SpringBoot」起動時にSpringBootを参照して、それが関連するリスナーのシリーズをトリガする、リスナーは、いくつかの初期化前処理操作を行うには、その職務を遂行します。SpringCloudは彼のリスナーを実現:BootstrapApplicationListener
SpringCloudコンテキストを初期化します。
リスナーがトリガされた後、処理ロジックを見て:
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
//如果未开启SpringCloud,直接返回
if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
true)) {
return;
}
// don't listen to events in a bootstrap context
//判断该监听器是否已经执行过,如果执行过,直接返回
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
//这里返回了一个 Spring 容器
ConfigurableApplicationContext context = bootstrapServiceContext(environment,
event.getSpringApplication());
apply(context, event.getSpringApplication(), environment);
}
bootstrapServiceContext
この方法は、Springコンテナを作成します。ConfigurableApplicationContextは、見てみましょう:
private ConfigurableApplicationContext bootstrapServiceContext( ConfigurableEnvironment environment, final SpringApplication application) { StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); MutablePropertySources bootstrapProperties = bootstrapEnvironment .getPropertySources(); for (PropertySource<?> source : bootstrapProperties) { bootstrapProperties.remove(source.getName()); } //设置读取 bootstrap 文件 String configName = environment .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); //设置 bootstrap 文件路径 String configLocation = environment .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); Map<String, Object> bootstrapMap = new HashMap<>(); bootstrapMap.put("spring.config.name", configName); if (StringUtils.hasText(configLocation)) { bootstrapMap.put("spring.config.location", configLocation); } //设置是否已经初始化BootStrap环境 bootstrapProperties.addFirst( new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for (PropertySource<?> source : environment.getPropertySources()) { bootstrapProperties.addLast(source); } //...... //加载BootstrapConfiguration 配置类 List<String> names = SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader); for (String name : StringUtils.commaDelimitedListToStringArray( environment.getProperty("spring.cloud.bootstrap.sources", ""))) { names.add(name); } //创建 Spring 容器 SpringApplicationBuilder builder = new SpringApplicationBuilder() .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) .environment(bootstrapEnvironment) .properties("spring.application.name:" + configName) .registerShutdownHook(false) .logStartupInfo(false) .web(false); List<Class<?>> sources = new ArrayList<>();
builder.sources(sources.toArray(new Class[sources.size()])); AnnotationAwareOrderComparator.sort(sources); final ConfigurableApplicationContext context = builder.run(); //创建祖先容器 addAncestorInitializer(application, context); bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); return context; }
まず、SpringBoot
プロジェクトがであるSpringApplicationBuilder
上記のロジックで始まる、彼はまた、構築された、SpringApplicationBuilder
オブジェクトの実行方法を再度実行し、それは別のプロファイルと設定クラスがちょうど読んで、二回実行するプロセスを開始します。私は頼まれた前に、SpringCloudプロジェクトApplicationContextInitializer
二回実行するためのロジックで実装クラスは、その理由は、起動プロセスが2回実行されていることです。
第二の作成時に同様に、SpringApplicationBuilder
時間と開始し、リスナーが再び起動し、作成しないだろうSpringApplicationBuilder
、それを?
確かではありません。そうでなければ、それは死のサイクルです。既に述べたように、SpringCloudは、識別子によってBOOTSTRAP_PROPERTY_SOURCE_NAME
判断しました。値がある場合、リスナーの実装後、それが実行され表す、次の開始前の値に対応する変数を設定します。
キーコードの一番上の行があります:addAncestorInitializer(application, context);
ancestor
祖先は意味、見て:
private void addAncestorInitializer(SpringApplication application, ConfigurableApplicationContext context) { boolean installed = false; //遍历所有的initializer,判断是否已经存在 祖先initializer for (ApplicationContextInitializer<?> initializer : application .getInitializers()) { if (initializer instanceof AncestorInitializer) { installed = true; // 如果存在,则设置 bootStrapApplication ((AncestorInitializer) initializer).setParent(context); } } //如果不存在,则创建。 if (!installed) { application.addInitializers(new AncestorInitializer(context)); } }
基本的にAncestorInitializerオブジェクトを作成し、ブートストラップアプリケーションSpringCloudが作成渡します。つまり、コンテナの祖先です。
SpringCloudブートストラップ環境の初期化が完了すると、SpringBootはすべて初期化子をトリガーする、SpringBootの初期化プロセスに戻るには、祖先のイニシャライザを実行したとき:AncestorInitializerは、それが親にコンテナをBootStrapApplicationます。private static class AncestorInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private ConfigurableApplicationContext parent; public AncestorInitializer(ConfigurableApplicationContext parent) { this.parent = parent; } @Override public void initialize(ConfigurableApplicationContext context) { //如果已经存在父容器,则直接取出 while (context.getParent() != null && context.getParent() != context) { context = (ConfigurableApplicationContext) context.getParent(); } reorderSources(context.getEnvironment()); //设置父容器 new ParentContextApplicationContextInitializer(this.parent) .initialize(context); }
}
親コンテナに委託論理設定の上記の方法ParentContextApplicationContextInitializer
の取り扱いは、見てinitialize
の方法:
public class ParentContextApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private int order = Ordered.HIGHEST_PRECEDENCE; private final ApplicationContext parent; @Override public void initialize(ConfigurableApplicationContext applicationContext) { if (applicationContext != this.parent) { //设置父容器 applicationContext.setParent(this.parent); //创建监听器,主要用来发布项目中存在父子容器事件 applicationContext.addApplicationListener(EventPublisher.INSTANCE); } }
}
アクションブートストラップApplicationコンテナ:
早期ロードSpringCloud関連の設定クラスは、事前に構成されたブートストラップアプリケーションのロードされた中央構成クラスとして、プラスの優先順位は、読み込むbootstrap
設定ファイルロジックを。
次のようにロードし、デフォルトの設定は次のとおりです。
SpringBoot春のコンテナ
Springコンテナを作成SpringBoot、コア・コンテナは、最も使用さSpringコンテナです。
オブジェクトは3種類、サーブレット、反応性、およびデフォルトが存在します作成しました。
以下のように決定SpringBoot2.xバージョン:
public class SpringApplication { //...... protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } //...... }
具体细节不多介绍了,创建流程可参考之前 SpringBoot启动流程源码分析文章。
微服务配置容器
上面 uml 图中提到了一个关键类:
NamedContextFactory
,从命名可以看出,这是一个工厂类:抽象容器工厂。同 hystrix 线程隔离原理一样,该工厂根据不同的服务名称,创建不同的容器。该容器有2个实现类,FeignContext
和SpringClientFactory
,分别用来加载对应的配置。
来看一下相关的核心代码:public abstract class NamedContextFactory<C extends NamedContextFactory.Specification> implements DisposableBean, ApplicationContextAware {
//Feign 和Ribbon 配置抽象接口 public interface Specification { String getName(); Class<?>[] getConfiguration(); } //Application集合 private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>(); protected AnnotationConfigApplicationContext getContext(String name) { //根据服务名称获取对应配置工厂,如果没有,则创建 if (!this.contexts.containsKey(name)) { synchronized (this.contexts) { if (!this.contexts.containsKey(name)) { //创建并进行缓存 this.contexts.put(name, createContext(name)); } } } return this.contexts.get(name); } protected AnnotationConfigApplicationContext createContext(String name) { //创建一个 Spring 容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); if (this.configurations.containsKey(name)) { for (Class<?> configuration : this.configurations.get(name) .getConfiguration()) { //注入配置类 context.register(configuration); } } //注入默认的Feign或Ribbon配置类 for (Map.Entry<String, C> entry : this.configurations.entrySet()) { if (entry.getKey().startsWith("default.")) { for (Class<?> configuration : entry.getValue().getConfiguration()) { context.register(configuration); } } } context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType); context.getEnvironment().getPropertySources().addFirst(new MapPropertySource( this.propertySourceName, Collections.<String, Object> singletonMap(this.propertyName, name))); if (this.parent != null) { // Uses Environment from parent as well as beans //设置父类为 SpringBoot 创建的Spring 容器 context.setParent(this.parent); } //启动容器 context.refresh(); return context; }
}
具体执行细节这里不做展开了,之前的文章《SpringCloud | SpringCloud Feign的前世今生【源码深入分析】》有详细介绍。
所以,具体Feign 和 Ribbon配置类会创建多少实例,和项目本身依赖发服务有关。如果依赖10个服务,那就是20个微服务配置容器+SpringBoot容器+BootStrap容器。哪里看到呢?
如果项目引入了SpringBoot
监控模块Spring Boot Actuator
,那在idea中可以看到已经创建的容器:
注意:由于Ribbon 默认会采用懒加载,也就是只有第一次请求的时候才会加载。所以idea这里不会显示 Ribbon 相关配置类容器,只显示项目启动流程中创建完成的 Spring 容器。
这也是微服务经常第一次超时的根本原理,创建并启动一个Spring容器需要一定的时间。
总结
本篇主要介绍了 SpringCloud 项目中创建的 Spring 容器:
まずSpringBootプロジェクトは、トリガー・リスナーを開始してSpringCloudを導入した場合、BootstrapApplicationListener
は、初期化SpringCloud関連するコンテキストを開始:ブートストラップのApplicationContextを、コンテナの祖先にそれを設定し、その子の作成に進みます。SpringBootアプリケーションを。
あなたがFeignClientを導入する場合は、コンテナ工場、サービス名キー、装うリボンやコンテナの構成クラス値をインスタンス化します、アイソレーションコンフィギュレーション与えられ、親コンテナはSpringBootアプリケーションです。