Spring 插件扩展
本文汇总一些常见的扩展 Spring 的库的场景和扩展方法。
自定义加载 Properties 文件
Apollo 是携程框架部门研发的分布式配置中心,它的 Java 客户端可以在 Spring 启动的时候将这些配置加载到本地,与 Spring 无缝整合,它的 Java 客户端使用文档请参考 Java 客户端使用指南。无论是支持注解方式 @Value("${someKeyFromApollo:someDefaultValue}")
引用,还是在文件中引用 Apollo 服务器上的配置 spring.datasource.url: ${someKeyFromApollo:someDefaultValue}
都是没有问题的。
它的实现原理如下:
public class ApolloApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> , EnvironmentPostProcessor, Ordered {
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
if (!environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false)) {
logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
return;
}
logger.debug("Apollo bootstrap config is enabled for context {}", context);
initialize(environment);
}
/**
* Initialize Apollo Configurations Just after environment is ready.
*
* @param environment
*/
protected void initialize(ConfigurableEnvironment environment) {
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//already initialized
return;
}
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
logger.debug("Apollo bootstrap namespaces: {}", namespaces);
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);
}
/**
* To fill system properties from environment config
*/
void initializeSystemProperty(ConfigurableEnvironment environment) {
for (String propertyName : APOLLO_SYSTEM_PROPERTIES) {
fillSystemPropertyFromEnvironment(environment, propertyName);
}
}
private void fillSystemPropertyFromEnvironment(ConfigurableEnvironment environment, String propertyName) {
if (System.getProperty(propertyName) != null) {
return;
}
String propertyValue = environment.getProperty(propertyName);
if (Strings.isNullOrEmpty(propertyValue)) {
return;
}
System.setProperty(propertyName, propertyValue);
}
/**
*
* In order to load Apollo configurations as early as even before Spring loading logging system phase,
* this EnvironmentPostProcessor can be called Just After ConfigFileApplicationListener has succeeded.
*
* <br />
* The processing sequence would be like this: <br />
* Load Bootstrap properties and application properties -----> load Apollo configuration properties ----> Initialize Logging systems
*
* @param configurableEnvironment
* @param springApplication
*/
@Override
public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
// should always initialize system properties like app.id in the first place
initializeSystemProperty(configurableEnvironment);
Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);
//EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
if (!eagerLoadEnabled) {
return;
}
Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
if (bootstrapEnabled) {
initialize(configurableEnvironment);
}
}
}
从上述代码可以看出,最主要的是在 initialize
和 postProcessEnvironment
方法中,将从服务器获取好的 Config
,添加到 environment.getPropertySources()
中,并且添加的时候是用的 addFirst()
方法添加到了首位。
而 SpringBoot 框架的 SpringApplication
在启动的时候,会扫描所有 JAR 包中的每一个 /META-INF/spring.factories
文件,下面是 apollo-client
的该文件内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
SpringApplication
在构造器内部,会自动加载所有声明在 /META-INF/spring.factories
文件中且实现了 org.springframework.context.ApplicationContextInitializer
接口的类,然后根据 Ordered
接口的 getOrder()
方法将这些类进行排序,并在 applyInitializers
方法中,依次调用 initialize(context)
方法。