1、前言java
前面咱們從源碼簡單介紹了一下spring boot 的 事件機制的相關類。下面咱們接着介紹一個事件相關的 監聽器BootstrapApplicationListenerreact
2、類圖web
3、源碼分析spring
package org.springframework.cloud.bootstrap; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.Banner.Mode; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.ParentContextApplicationContextInitializer; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.logging.LoggingApplicationListener; import org.springframework.cloud.bootstrap.encrypt.EnvironmentDecryptApplicationInitializer; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource.StubPropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.SystemEnvironmentPropertySource; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; //它是用ApplicationContextInitializer beans經過一個單獨的引導環境去準備SpringApplication。 //這個引導上下文是從spring.factories源中做爲BootstrapConfiguration建立的SpringApplication, //而且用外部的文件如"bootstrap.properties"或 yml 初始化 ,而不是普通的application.properties public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap"; public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5; public static final String DEFAULT_PROPERTIES = "defaultProperties"; private int order = DEFAULT_ORDER; @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); 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; } ConfigurableApplicationContext context = null; String configName = environment .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); for (ApplicationContextInitializer<?> initializer : event.getSpringApplication() .getInitializers()) { if (initializer instanceof ParentContextApplicationContextInitializer) { context = findBootstrapContext( (ParentContextApplicationContextInitializer) initializer, configName); } } if (context == null) { context = bootstrapServiceContext(environment, event.getSpringApplication(), configName); } apply(context, event.getSpringApplication(), environment); } private ConfigurableApplicationContext findBootstrapContext( ParentContextApplicationContextInitializer initializer, String configName) { Field field = ReflectionUtils .findField(ParentContextApplicationContextInitializer.class, "parent"); ReflectionUtils.makeAccessible(field); ConfigurableApplicationContext parent = safeCast( ConfigurableApplicationContext.class, ReflectionUtils.getField(field, initializer)); if (parent != null && !configName.equals(parent.getId())) { parent = safeCast(ConfigurableApplicationContext.class, parent.getParent()); } return parent; } private <T> T safeCast(Class<T> type, Object object) { try { return type.cast(object); } catch (ClassCastException e) { return null; } } private ConfigurableApplicationContext bootstrapServiceContext( ConfigurableEnvironment environment, final SpringApplication application, String configName) { StandardEnvironment bootstrapEnvironment = new StandardEnvironment(); MutablePropertySources bootstrapProperties = bootstrapEnvironment .getPropertySources(); for (PropertySource<?> source : bootstrapProperties) { bootstrapProperties.remove(source.getName()); } String configLocation = environment .resolvePlaceholders("${spring.cloud.bootstrap.location:}"); Map<String, Object> bootstrapMap = new HashMap<>(); bootstrapMap.put("spring.config.name", configName); // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail // force the environment to use none, because if though it is set below in the builder // the environment overrides it bootstrapMap.put("spring.main.web-application-type", "none"); if (StringUtils.hasText(configLocation)) { bootstrapMap.put("spring.config.location", configLocation); } bootstrapProperties.addFirst( new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for (PropertySource<?> source : environment.getPropertySources()) { if (source instanceof StubPropertySource) { continue; } bootstrapProperties.addLast(source); } ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates List<String> names = new ArrayList<>(SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader)); for (String name : StringUtils.commaDelimitedListToStringArray( environment.getProperty("spring.cloud.bootstrap.sources", ""))) { names.add(name); } // TODO: is it possible or sensible to share a ResourceLoader? SpringApplicationBuilder builder = new SpringApplicationBuilder() .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) .environment(bootstrapEnvironment) // Don't use the default properties in this builder .registerShutdownHook(false).logStartupInfo(false) .web(WebApplicationType.NONE); if (environment.getPropertySources().contains("refreshArgs")) { // If we are doing a context refresh, really we only want to refresh the // Environment, and there are some toxic listeners (like the // LoggingApplicationListener) that affect global static state, so we need a // way to switch those off. builder.application() .setListeners(filterListeners(builder.application().getListeners())); } List<Class<?>> sources = new ArrayList<>(); for (String name : names) { Class<?> cls = ClassUtils.resolveClassName(name, null); try { cls.getDeclaredAnnotations(); } catch (Exception e) { continue; } sources.add(cls); } AnnotationAwareOrderComparator.sort(sources); builder.sources(sources.toArray(new Class[sources.size()])); final ConfigurableApplicationContext context = builder.run(); // gh-214 using spring.application.name=bootstrap to set the context id via // `ContextIdApplicationContextInitializer` prevents apps from getting the actual // spring.application.name // during the bootstrap phase. context.setId("bootstrap"); // Make the bootstrap context a parent of the app context addAncestorInitializer(application, context); // It only has properties in it now that we don't want in the parent so remove // it (and it will be added back later) bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties); return context; } private Collection<? extends ApplicationListener<?>> filterListeners( Set<ApplicationListener<?>> listeners) { Set<ApplicationListener<?>> result = new LinkedHashSet<>(); for (ApplicationListener<?> listener : listeners) { if (!(listener instanceof LoggingApplicationListener) && !(listener instanceof LoggingSystemShutdownListener)) { result.add(listener); } } return result; } private void mergeDefaultProperties(MutablePropertySources environment, MutablePropertySources bootstrap) { String name = DEFAULT_PROPERTIES; if (bootstrap.contains(name)) { PropertySource<?> source = bootstrap.get(name); if (!environment.contains(name)) { environment.addLast(source); } else { PropertySource<?> target = environment.get(name); if (target instanceof MapPropertySource) { Map<String, Object> targetMap = ((MapPropertySource) target) .getSource(); if (target != source) { if (source instanceof MapPropertySource) { Map<String, Object> map = ((MapPropertySource) source) .getSource(); for (String key : map.keySet()) { if (!target.containsProperty(key)) { targetMap.put(key, map.get(key)); } } } } } } } mergeAdditionalPropertySources(environment, bootstrap); } private void mergeAdditionalPropertySources(MutablePropertySources environment, MutablePropertySources bootstrap) { PropertySource<?> defaultProperties = environment.get(DEFAULT_PROPERTIES); ExtendedDefaultPropertySource result = defaultProperties instanceof ExtendedDefaultPropertySource ? (ExtendedDefaultPropertySource) defaultProperties : new ExtendedDefaultPropertySource(DEFAULT_PROPERTIES, defaultProperties); for (PropertySource<?> source : bootstrap) { if (!environment.contains(source.getName())) { result.add(source); } } for (String name : result.getPropertySourceNames()) { bootstrap.remove(name); } addOrReplace(environment, result); addOrReplace(bootstrap, result); } private void addOrReplace(MutablePropertySources environment, PropertySource<?> result) { if (environment.contains(result.getName())) { environment.replace(result.getName(), result); } else { environment.addLast(result); } } private void addAncestorInitializer(SpringApplication application, ConfigurableApplicationContext context) { boolean installed = false; for (ApplicationContextInitializer<?> initializer : application .getInitializers()) { if (initializer instanceof AncestorInitializer) { installed = true; // New parent ((AncestorInitializer) initializer).setParent(context); } } if (!installed) { application.addInitializers(new AncestorInitializer(context)); } } private void apply(ConfigurableApplicationContext context, SpringApplication application, ConfigurableEnvironment environment) { @SuppressWarnings("rawtypes") List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context, ApplicationContextInitializer.class); application.addInitializers(initializers .toArray(new ApplicationContextInitializer[initializers.size()])); addBootstrapDecryptInitializer(application); } private void addBootstrapDecryptInitializer(SpringApplication application) { DelegatingEnvironmentDecryptApplicationInitializer decrypter = null; for (ApplicationContextInitializer<?> initializer : application .getInitializers()) { if (initializer instanceof EnvironmentDecryptApplicationInitializer) { @SuppressWarnings("unchecked") ApplicationContextInitializer<ConfigurableApplicationContext> delegate = (ApplicationContextInitializer<ConfigurableApplicationContext>) initializer; decrypter = new DelegatingEnvironmentDecryptApplicationInitializer( delegate); } } if (decrypter != null) { application.addInitializers(decrypter); } } private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context, Class<T> type) { List<T> result = new ArrayList<T>(); for (String name : context.getBeanNamesForType(type)) { result.add(context.getBean(name, type)); } AnnotationAwareOrderComparator.sort(result); return result; } public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } private static class AncestorInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { private ConfigurableApplicationContext parent; public AncestorInitializer(ConfigurableApplicationContext parent) { this.parent = parent; } public void setParent(ConfigurableApplicationContext parent) { this.parent = parent; } @Override public int getOrder() { // Need to run not too late (so not unordered), so that, for instance, the // ContextIdApplicationContextInitializer runs later and picks up the merged // Environment. Also needs to be quite early so that other initializers can // pick up the parent (especially the Environment). return Ordered.HIGHEST_PRECEDENCE + 5; } @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); } private void reorderSources(ConfigurableEnvironment environment) { PropertySource<?> removed = environment.getPropertySources() .remove(DEFAULT_PROPERTIES); if (removed instanceof ExtendedDefaultPropertySource) { ExtendedDefaultPropertySource defaultProperties = (ExtendedDefaultPropertySource) removed; environment.getPropertySources().addLast(new MapPropertySource( DEFAULT_PROPERTIES, defaultProperties.getSource())); for (PropertySource<?> source : defaultProperties.getPropertySources() .getPropertySources()) { if (!environment.getPropertySources().contains(source.getName())) { environment.getPropertySources().addBefore(DEFAULT_PROPERTIES, source); } } } } } /** * A special initializer designed to run before the property source bootstrap and * decrypt any properties needed there (e.g. URL of config server). */ @Order(Ordered.HIGHEST_PRECEDENCE + 9) private static class DelegatingEnvironmentDecryptApplicationInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { private ApplicationContextInitializer<ConfigurableApplicationContext> delegate; public DelegatingEnvironmentDecryptApplicationInitializer( ApplicationContextInitializer<ConfigurableApplicationContext> delegate) { this.delegate = delegate; } @Override public void initialize(ConfigurableApplicationContext applicationContext) { this.delegate.initialize(applicationContext); } } private static class ExtendedDefaultPropertySource extends SystemEnvironmentPropertySource { private final CompositePropertySource sources; private final List<String> names = new ArrayList<>(); public ExtendedDefaultPropertySource(String name, PropertySource<?> propertySource) { super(name, findMap(propertySource)); this.sources = new CompositePropertySource(name); } public CompositePropertySource getPropertySources() { return this.sources; } public List<String> getPropertySourceNames() { return this.names; } public void add(PropertySource<?> source) { if (source instanceof EnumerablePropertySource && !this.names.contains(source.getName())) { this.sources.addPropertySource(source); this.names.add(source.getName()); } } @Override public Object getProperty(String name) { if (this.sources.containsProperty(name)) { return this.sources.getProperty(name); } return super.getProperty(name); } @Override public boolean containsProperty(String name) { if (this.sources.containsProperty(name)) { return true; } return super.containsProperty(name); } @Override public String[] getPropertyNames() { List<String> names = new ArrayList<>(); names.addAll(Arrays.asList(this.sources.getPropertyNames())); names.addAll(Arrays.asList(super.getPropertyNames())); return names.toArray(new String[0]); } @SuppressWarnings("unchecked") private static Map<String, Object> findMap(PropertySource<?> propertySource) { if (propertySource instanceof MapPropertySource) { return (Map<String, Object>) propertySource.getSource(); } return new LinkedHashMap<String, Object>(); } } }
未完待續...bootstrap