我正在Spring Boot的基础上构建一组库,这些库打包为Spring Boot Starters。我需要能够定义功能,以便在应用程序启动时它们可以查看环境(即读取属性),并以此为基础设置其他属性值。我最初尝试在EnvironmentPostProcessor
中进行此操作,但这似乎不是正确的地方,因为您遇到的问题是尚无法使用所有PropertySource
的问题。
我的特定用例是我要寻找spring.boot.admin.client.url
属性的存在。如果找不到,则设置属性spring.boot.admin.client.enabled=false
。
在配置服务器端,对于具有不同配置文件的不同应用程序,我们具有不同的配置,其中一些将spring.boot.admin.client.url
设置为一个值,而另一些则没有。应用程序本身捆绑了spring-boot-admin-starter-client
依赖项。是否启用它仅由应用程序的运行时目标决定。
我想知道什么是正确的方法?
我想到了ApplicationListener
或ApplicationContextInitializer
。
ApplicationContextInitializer
在启动时被触发两次,一次用于引导上下文,一次用于主上下文。 ApplicationEnvironmentPreparedEvent
被触发两次(正好在每个ApplicationContextInitializer
的调用之前)。到那时,配置服务属性源还不存在,我正在寻找的属性值还不存在。
然后发射了一堆不同的ApplicationPreparedEvent
和ApplicationStartedEvent
(我算了3 ApplicationPreparedEvent
(事件的相同实例ID),然后是2 {{1} }(事件的相同实例ID),然后是2 ApplicationStartedEvent
(事件的相同实例ID)。
已于2018年9月28日更新
我也想补充一些我尝试过的测试。我从Spring Initializr构建了一个空白应用程序。我按照以下步骤构建了ApplicationReadyEvent
和ApplicationContextInitializr
:
ApplicationListener
:
application.yml
spring:
application:
name: TestEventStartup
boot:
admin:
client:
url: http://localhost:8888
jackson:
serialization:
write-dates-as-timestamps: false
resources:
chain:
strategy:
content:
enabled: true
logging:
level:
root: warn
com.testeventstartup: debug
server:
compression:
enabled: true
mime-types: application/json,text/css,text/html
min-response-size: 2048
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
info:
git:
mode: full
public class AppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println(String.format("Initializing Context - Got spring.boot.admin.client.url=%s", applicationContext.getEnvironment().getProperty("spring.boot.admin.client.url")));
}
}
public class AppListener implements ApplicationListener<SpringApplicationEvent> {
@Override
public void onApplicationEvent(SpringApplicationEvent event) {
System.out.println(String.format("Got event: %s", ToStringBuilder.reflectionToString(event)));
findEnvironment(event)
.ifPresent(environment -> System.out.println(String.format("%s: spring.boot.admin.client.url=%s", event.getClass().getSimpleName(), environment.getProperty("spring.boot.admin.client.url"))));
}
private Optional<Environment> findEnvironment(Object obj) {
return Optional.ofNullable(Optional.ofNullable(ReflectionUtils.findMethod(obj.getClass(), "getEnvironment"))
.map(method -> ReflectionUtils.invokeMethod(method, obj))
.orElseGet(() ->
Optional.ofNullable(ReflectionUtils.findMethod(obj.getClass(), "getApplicationContext"))
.map(method -> ReflectionUtils.invokeMethod(method, obj))
.flatMap(this::findEnvironment)
.orElse(null)
))
.filter(Environment.class::isInstance)
.map(Environment.class::cast);
}
}
:
/META-INF/spring.factories
当我启动没有org.springframework.context.ApplicationListener=\
com.testeventstartup.listener.AppListener
org.springframework.context.ApplicationContextInitializer=\
com.testeventstartup.listener.AppContextInitializer
依赖项的应用程序时,我在日志中看到了这一点:
org.springframework.cloud:spring-cloud-starter-config
然后我添加Got event: org.springframework.boot.context.event.ApplicationStartingEvent@32709393[args={},timestamp=1538141292580]
Got event: org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent@399f45b1[environment=StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[StubPropertySource {name='servletConfigInitParams'}, StubPropertySource {name='servletContextInitParams'}, MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml] (document #1)'}]},args={},timestamp=1538141292628]
ApplicationEnvironmentPreparedEvent: spring.boot.admin.client.url=http://localhost:8888
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
Initializing Context - Got spring.boot.admin.client.url=http://localhost:8888
2018-09-28 09:28:12.918 INFO 2534 --- [ main] c.t.TestEventStartupApplication : Starting TestEventStartupApplication on MAC-22XG8WL with PID 2534 (/Users/edeandre/workspaces/IntelliJ/test-event-startup/build/classes/java/main started by edeandre in /Users/edeandre/workspaces/IntelliJ/test-event-startup)
2018-09-28 09:28:12.920 DEBUG 2534 --- [ main] c.t.TestEventStartupApplication : Running with Spring Boot v2.0.5.RELEASE, Spring v5.0.9.RELEASE
2018-09-28 09:28:12.922 INFO 2534 --- [ main] c.t.TestEventStartupApplication : No active profile set, falling back to default profiles: default
Got event: org.springframework.boot.context.event.ApplicationPreparedEvent@6a370f4[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@49e53c76: startup date [Wed Dec 31 19:00:00 EST 1969]; root of context hierarchy,args={},timestamp=1538141292961]
ApplicationPreparedEvent: spring.boot.admin.client.url=http://localhost:8888
2018-09-28 09:28:15.385 INFO 2534 --- [ main] c.t.TestEventStartupApplication : Started TestEventStartupApplication in 2.809 seconds (JVM running for 3.32)
Got event: org.springframework.boot.context.event.ApplicationStartedEvent@7159139f[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@49e53c76: startup date [Fri Sep 28 09:28:12 EDT 2018]; root of context hierarchy,args={},timestamp=1538141295385]
ApplicationStartedEvent: spring.boot.admin.client.url=http://localhost:8888
Got event: org.springframework.boot.context.event.ApplicationReadyEvent@232cce0[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@49e53c76: startup date [Fri Sep 28 09:28:12 EDT 2018]; root of context hierarchy,args={},timestamp=1538141295387]
ApplicationReadyEvent: spring.boot.admin.client.url=http://localhost:8888
依赖项并将其连接到配置服务器中时,这就是我在启动时看到的内容:
org.springframework.cloud:spring-cloud-starter-config
连接到配置服务器后,您会注意到一些事情:
Got event: org.springframework.boot.context.event.ApplicationStartingEvent@23faf8f2[args={},timestamp=1538141399719]
Got event: org.springframework.boot.context.event.ApplicationStartingEvent@306279ee[args={},timestamp=1538141399814]
Got event: org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent@7cc0cdad[environment=StandardEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, MapPropertySource {name='bootstrap'}, MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, MapPropertySource {name='springCloudClientHostInfo'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/bootstrap.yml]'}]},args={},timestamp=1538141399815]
ApplicationEnvironmentPreparedEvent: spring.boot.admin.client.url=null
Initializing Context - Got spring.boot.admin.client.url=null
Got event: org.springframework.boot.context.event.ApplicationPreparedEvent@4e7912d8[context=org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f: startup date [Wed Dec 31 19:00:00 EST 1969]; root of context hierarchy,args={},timestamp=1538141400176]
ApplicationPreparedEvent: spring.boot.admin.client.url=null
2018-09-28 09:30:00.183 INFO 2568 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f: startup date [Fri Sep 28 09:30:00 EDT 2018]; root of context hierarchy
2018-09-28 09:30:00.362 INFO 2568 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$f1570cbf] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Got event: org.springframework.boot.context.event.ApplicationPreparedEvent@4e7912d8[context=org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f: startup date [Fri Sep 28 09:30:00 EDT 2018]; root of context hierarchy,args={},timestamp=1538141400176]
ApplicationPreparedEvent: spring.boot.admin.client.url=null
Got event: org.springframework.boot.context.event.ApplicationStartedEvent@460ebd80[context=org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f: startup date [Fri Sep 28 09:30:00 EDT 2018]; root of context hierarchy,args={},timestamp=1538141400608]
ApplicationStartedEvent: spring.boot.admin.client.url=null
Got event: org.springframework.boot.context.event.ApplicationReadyEvent@16fdec90[context=org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f: startup date [Fri Sep 28 09:30:00 EDT 2018]; root of context hierarchy,args={},timestamp=1538141400609]
ApplicationReadyEvent: spring.boot.admin.client.url=null
Got event: org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent@e8df99a[environment=StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, StubPropertySource {name='servletConfigInitParams'}, StubPropertySource {name='servletContextInitParams'}, MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml] (document #1)'}, ExtendedDefaultPropertySource {name='defaultProperties'}, MapPropertySource {name='springCloudClientHostInfo'}]},args={},timestamp=1538141399781]
ApplicationEnvironmentPreparedEvent: spring.boot.admin.client.url=http://localhost:8888
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
Initializing Context - Got spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
2018-09-28 09:30:01.683 INFO 2568 --- [ main] c.t.TestEventStartupApplication : No active profile set, falling back to default profiles: default
Got event: org.springframework.boot.context.event.ApplicationPreparedEvent@5c87bfe2[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Wed Dec 31 19:00:00 EST 1969]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141401690]
ApplicationPreparedEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
Got event: org.springframework.boot.context.event.ApplicationPreparedEvent@5c87bfe2[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Fri Sep 28 09:30:01 EDT 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141401690]
ApplicationPreparedEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
Got event: org.springframework.boot.context.event.ApplicationPreparedEvent@5c87bfe2[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Fri Sep 28 09:30:01 EDT 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141401690]
ApplicationPreparedEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
2018-09-28 09:30:03.858 INFO 2568 --- [ main] c.t.TestEventStartupApplication : Started TestEventStartupApplication in 4.143 seconds (JVM running for 4.88)
Got event: org.springframework.boot.context.event.ApplicationStartedEvent@3dfa819[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Fri Sep 28 09:30:01 EDT 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141403859]
ApplicationStartedEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
Got event: org.springframework.boot.context.event.ApplicationStartedEvent@3dfa819[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Fri Sep 28 09:30:01 EDT 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141403859]
ApplicationStartedEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
Got event: org.springframework.boot.context.event.ApplicationReadyEvent@4ce94d2f[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Fri Sep 28 09:30:01 EDT 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141403861]
ApplicationReadyEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
Got event: org.springframework.boot.context.event.ApplicationReadyEvent@4ce94d2f[context=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@2eee3069: startup date [Fri Sep 28 09:30:01 EDT 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@815b41f,args={},timestamp=1538141403861]
ApplicationReadyEvent: spring.boot.admin.client.url=https://myspringbootadminserver.mycompany.com
会触发两次-一次引导,一次“ main”应用
ApplicationContextInitializer
(触发两次),org.springframework.boot.context.event.ApplicationPreparedEvent@732c2a62
(触发3次),org.springframework.boot.context.event.ApplicationPreparedEvent@6b9ce1bf
(触发两次) ),org.springframework.boot.context.event.ApplicationStartedEvent@1f6917fb
(触发两次)我知道lifecycle events应该被触发两次,一次用于引导程序上下文和“主”上下文,但是在每个上下文的生命周期内,为什么同一事件实例多次触发?
此外-整件事使我朝着解决方案的方向走,我应该使用org.springframework.boot.context.event.ApplicationReadyEvent@41eb94bc
作为满足我要求的最佳解决方案(检查当前ApplicationContextInitializer
状态值,然后有条件地设置一个属性值/添加一个额外的Environment
。如果我的需求只是以编程方式添加属性值,那么继续使用PropertySource
可能是更好的解决方案。
现在的缺点是EnvironmentPostProcessor
中的逻辑发生两次,每种情况下发生一次。 ApplicationContextInitializer
是否有办法了解它在哪个上下文中运行,然后才真正在“主”上下文中执行我们想要的操作?
有人可以在我的观察中权衡一下吗? @AndyWilkinson?
答案 0 :(得分:0)
您可以使用Spring应用程序的事件和侦听器。就您而言,您可以像使用ApplicationEnvironmentPreparedEvent
:
public class OverridePropertiesListener implements
ApplicationListener<ApplicationEnvironmentPreparedEvent> {
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
Properties props = new Properties();
props.put("myProperty", "<my value>");
environment.getPropertySources().addFirst(new PropertiesPropertySource("myProps", props));
}
}
有关监听器的更多信息:Application Events and Listeners