Polish "Add startup time metrics"

See gh-27878
This commit is contained in:
Phillip Webb
2021-09-21 13:38:46 -07:00
parent c9dc40a465
commit 98a0e07dd5
15 changed files with 148 additions and 152 deletions

View File

@@ -20,7 +20,7 @@ import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.metrics.startup.StartupTimeMetrics;
import org.springframework.boot.actuate.metrics.startup.StartupTimeMetricsListener;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@@ -39,12 +39,12 @@ import org.springframework.context.annotation.Configuration;
@AutoConfigureAfter({ MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
@ConditionalOnClass(MeterRegistry.class)
@ConditionalOnBean(MeterRegistry.class)
public class StartupTimeMetricsAutoConfiguration {
public class StartupTimeMetricsListenerAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public StartupTimeMetrics startupTimeMetrics(MeterRegistry meterRegistry) {
return new StartupTimeMetrics(meterRegistry);
public StartupTimeMetricsListener startupTimeMetrics(MeterRegistry meterRegistry) {
return new StartupTimeMetricsListener(meterRegistry);
}
}

View File

@@ -75,7 +75,7 @@ org.springframework.boot.actuate.autoconfigure.metrics.mongo.MongoMetricsAutoCon
org.springframework.boot.actuate.autoconfigure.metrics.orm.jpa.HibernateMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.r2dbc.ConnectionPoolMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.redis.LettuceMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsListenerAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.metrics.web.jetty.JettyMetricsAutoConfiguration,\

View File

@@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun;
import org.springframework.boot.actuate.metrics.startup.StartupTimeMetrics;
import org.springframework.boot.actuate.metrics.startup.StartupTimeMetricsListener;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
@@ -35,20 +35,20 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link StartupTimeMetricsAutoConfiguration}.
* Tests for {@link StartupTimeMetricsListenerAutoConfiguration}.
*
* @author Chris Bono
* @author Stephane Nicoll
*/
class StartupTimeMetricsAutoConfigurationTests {
class StartupTimeMetricsListenerAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().with(MetricsRun.simple())
.withConfiguration(AutoConfigurations.of(StartupTimeMetricsAutoConfiguration.class));
.withConfiguration(AutoConfigurations.of(StartupTimeMetricsListenerAutoConfiguration.class));
@Test
void startupTimeMetricsAreRecorded() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(StartupTimeMetrics.class);
assertThat(context).hasSingleBean(StartupTimeMetricsListener.class);
SimpleMeterRegistry registry = context.getBean(SimpleMeterRegistry.class);
context.publishEvent(new ApplicationStartedEvent(new SpringApplication(), null,
context.getSourceApplicationContext(), Duration.ofMillis(1500)));
@@ -80,8 +80,9 @@ class StartupTimeMetricsAutoConfigurationTests {
@Test
void customStartupTimeMetricsAreRespected() {
this.contextRunner
.withBean("customStartupTimeMetrics", StartupTimeMetrics.class, () -> mock(StartupTimeMetrics.class))
.run((context) -> assertThat(context).hasSingleBean(StartupTimeMetrics.class)
.withBean("customStartupTimeMetrics", StartupTimeMetricsListener.class,
() -> mock(StartupTimeMetricsListener.class))
.run((context) -> assertThat(context).hasSingleBean(StartupTimeMetricsListener.class)
.hasBean("customStartupTimeMetrics"));
}

View File

@@ -36,9 +36,10 @@ import org.springframework.context.event.SmartApplicationListener;
* {@link ApplicationReadyEvent}.
*
* @author Chris Bono
* @author Phillip Webb
* @since 2.6.0
*/
public class StartupTimeMetrics implements SmartApplicationListener {
public class StartupTimeMetricsListener implements SmartApplicationListener {
/**
* The default name to use for the application started time metric.
@@ -52,11 +53,11 @@ public class StartupTimeMetrics implements SmartApplicationListener {
private final MeterRegistry meterRegistry;
private final String applicationStartedTimeMetricName;
private final String startedTimeMetricName;
private final String applicationReadyTimeMetricName;
private final String readyTimeMetricName;
private final Iterable<Tag> tags;
private final Tags tags;
/**
* Create a new instance using default metric names.
@@ -64,26 +65,25 @@ public class StartupTimeMetrics implements SmartApplicationListener {
* @see #APPLICATION_STARTED_TIME_METRIC_NAME
* @see #APPLICATION_READY_TIME_METRIC_NAME
*/
public StartupTimeMetrics(MeterRegistry meterRegistry) {
this(meterRegistry, Collections.emptyList(), APPLICATION_STARTED_TIME_METRIC_NAME,
APPLICATION_READY_TIME_METRIC_NAME);
public StartupTimeMetricsListener(MeterRegistry meterRegistry) {
this(meterRegistry, APPLICATION_STARTED_TIME_METRIC_NAME, APPLICATION_READY_TIME_METRIC_NAME,
Collections.emptyList());
}
/**
* Create a new instance using the specified options.
* @param meterRegistry the registry to use
* @param startedTimeMetricName the name to use for the application started time
* metric
* @param readyTimeMetricName the name to use for the application ready time metric
* @param tags the tags to associate to application startup metrics
* @param applicationStartedTimeMetricName the name to use for the application started
* time metric
* @param applicationReadyTimeMetricName the name to use for the application ready
* time metric
*/
public StartupTimeMetrics(MeterRegistry meterRegistry, Iterable<Tag> tags, String applicationStartedTimeMetricName,
String applicationReadyTimeMetricName) {
public StartupTimeMetricsListener(MeterRegistry meterRegistry, String startedTimeMetricName,
String readyTimeMetricName, Iterable<Tag> tags) {
this.meterRegistry = meterRegistry;
this.tags = (tags != null) ? tags : Collections.emptyList();
this.applicationStartedTimeMetricName = applicationStartedTimeMetricName;
this.applicationReadyTimeMetricName = applicationReadyTimeMetricName;
this.startedTimeMetricName = startedTimeMetricName;
this.readyTimeMetricName = readyTimeMetricName;
this.tags = Tags.of(tags);
}
@Override
@@ -103,33 +103,27 @@ public class StartupTimeMetrics implements SmartApplicationListener {
}
private void onApplicationStarted(ApplicationStartedEvent event) {
if (event.getStartedTime() == null) {
return;
}
registerGauge(this.applicationStartedTimeMetricName, "Time taken (ms) to start the application",
event.getStartedTime(), createTagsFrom(event.getSpringApplication()));
registerGauge(this.startedTimeMetricName, "Time taken (ms) to start the application", event.getTimeTaken(),
event.getSpringApplication());
}
private void onApplicationReady(ApplicationReadyEvent event) {
if (event.getReadyTime() == null) {
return;
}
registerGauge(this.applicationReadyTimeMetricName,
"Time taken (ms) for the application to be ready to service requests", event.getReadyTime(),
createTagsFrom(event.getSpringApplication()));
registerGauge(this.readyTimeMetricName, "Time taken (ms) for the application to be ready to service requests",
event.getTimeTaken(), event.getSpringApplication());
}
private void registerGauge(String metricName, String description, Duration time, Iterable<Tag> tags) {
TimeGauge.builder(metricName, time::toMillis, TimeUnit.MILLISECONDS).tags(tags).description(description)
.register(this.meterRegistry);
private void registerGauge(String name, String description, Duration timeTaken,
SpringApplication springApplication) {
if (timeTaken != null) {
Iterable<Tag> tags = createTagsFrom(springApplication);
TimeGauge.builder(name, timeTaken::toMillis, TimeUnit.MILLISECONDS).tags(tags).description(description)
.register(this.meterRegistry);
}
}
private Iterable<Tag> createTagsFrom(SpringApplication springApplication) {
Class<?> mainClass = springApplication.getMainApplicationClass();
if (mainClass == null) {
return this.tags;
}
return Tags.concat(this.tags, "main-application-class", mainClass.getName());
return (mainClass != null) ? this.tags.and("main-application-class", mainClass.getName()) : this.tags;
}
}

View File

@@ -35,26 +35,26 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link StartupTimeMetrics}.
* Tests for {@link StartupTimeMetricsListener}.
*
* @author Chris Bono
*/
class StartupTimeMetricsTests {
class StartupTimeMetricsListenerTests {
private MeterRegistry registry;
private StartupTimeMetrics metrics;
private StartupTimeMetricsListener listener;
@BeforeEach
void setup() {
this.registry = new SimpleMeterRegistry();
this.metrics = new StartupTimeMetrics(this.registry);
this.listener = new StartupTimeMetricsListener(this.registry);
}
@Test
void metricsRecordedWithoutCustomTags() {
this.metrics.onApplicationEvent(applicationStartedEvent(2000L));
this.metrics.onApplicationEvent(applicationReadyEvent(2200L));
this.listener.onApplicationEvent(applicationStartedEvent(2000L));
this.listener.onApplicationEvent(applicationReadyEvent(2200L));
assertMetricExistsWithValue("application.started.time", 2000L);
assertMetricExistsWithValue("application.ready.time", 2200L);
}
@@ -62,9 +62,9 @@ class StartupTimeMetricsTests {
@Test
void metricsRecordedWithCustomTagsAndMetricNames() {
Tags tags = Tags.of("foo", "bar");
this.metrics = new StartupTimeMetrics(this.registry, tags, "m1", "m2");
this.metrics.onApplicationEvent(applicationStartedEvent(1000L));
this.metrics.onApplicationEvent(applicationReadyEvent(1050L));
this.listener = new StartupTimeMetricsListener(this.registry, "m1", "m2", tags);
this.listener.onApplicationEvent(applicationStartedEvent(1000L));
this.listener.onApplicationEvent(applicationReadyEvent(1050L));
assertMetricExistsWithCustomTagsAndValue("m1", tags, 1000L);
assertMetricExistsWithCustomTagsAndValue("m2", tags, 1050L);
}
@@ -72,7 +72,7 @@ class StartupTimeMetricsTests {
@Test
void metricRecordedWithoutMainAppClassTag() {
SpringApplication application = mock(SpringApplication.class);
this.metrics.onApplicationEvent(new ApplicationStartedEvent(application, null, null, Duration.ofSeconds(2)));
this.listener.onApplicationEvent(new ApplicationStartedEvent(application, null, null, Duration.ofSeconds(2)));
TimeGauge applicationStartedGague = this.registry.find("application.started.time").timeGauge();
assertThat(applicationStartedGague).isNotNull();
assertThat(applicationStartedGague.getId().getTags()).isEmpty();
@@ -82,8 +82,8 @@ class StartupTimeMetricsTests {
void metricRecordedWithoutMainAppClassTagAndAdditionalTags() {
SpringApplication application = mock(SpringApplication.class);
Tags tags = Tags.of("foo", "bar");
this.metrics = new StartupTimeMetrics(this.registry, tags, "started", "ready");
this.metrics.onApplicationEvent(new ApplicationReadyEvent(application, null, null, Duration.ofSeconds(2)));
this.listener = new StartupTimeMetricsListener(this.registry, "started", "ready", tags);
this.listener.onApplicationEvent(new ApplicationReadyEvent(application, null, null, Duration.ofSeconds(2)));
TimeGauge applicationReadyGague = this.registry.find("ready").timeGauge();
assertThat(applicationReadyGague).isNotNull();
assertThat(applicationReadyGague.getId().getTags()).containsExactlyElementsOf(tags);
@@ -91,8 +91,8 @@ class StartupTimeMetricsTests {
@Test
void metricsNotRecordedWhenStartupTimeNotAvailable() {
this.metrics.onApplicationEvent(applicationStartedEvent(null));
this.metrics.onApplicationEvent(applicationReadyEvent(null));
this.listener.onApplicationEvent(applicationStartedEvent(null));
this.listener.onApplicationEvent(applicationReadyEvent(null));
assertThat(this.registry.find("application.started.time").timeGauge()).isNull();
assertThat(this.registry.find("application.ready.time").timeGauge()).isNull();
}

View File

@@ -78,7 +78,6 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
/**
@@ -286,8 +285,7 @@ public class SpringApplication {
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
@@ -303,23 +301,20 @@ public class SpringApplication {
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
Duration startedTime = Duration.ofMillis(stopWatch.getTotalTimeMillis());
stopWatch.start();
Duration timeTakeToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startedTime);
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakeToStartup);
}
listeners.started(context, startedTime);
listeners.started(context, timeTakeToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
stopWatch.stop();
listeners.running(context, Duration.ofMillis(stopWatch.getTotalTimeMillis()));
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);

View File

@@ -77,13 +77,11 @@ public interface SpringApplicationRunListener {
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
* @deprecated since 2.6.0 for removal in 2.8.0 in favour of
* {@link #started(ConfigurableApplicationContext, Duration)}
* @param timeTaken the time taken to start the application or {@code null} if unknown
* @since 2.6.0
*/
@Deprecated
default void started(ConfigurableApplicationContext context) {
started(context, null);
default void started(ConfigurableApplicationContext context, Duration timeTaken) {
started(context);
}
/**
@@ -91,12 +89,12 @@ public interface SpringApplicationRunListener {
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @param startedTime the time taken to start the application or {@code null} if
* unknown
* @since 2.6.0
* @since 2.0.0
* @deprecated since 2.6.0 for removal in 2.8.0 in favor of
* {@link #started(ConfigurableApplicationContext, Duration)}
*/
default void started(ConfigurableApplicationContext context, Duration startedTime) {
started(context);
@Deprecated
default void started(ConfigurableApplicationContext context) {
}
/**
@@ -104,26 +102,25 @@ public interface SpringApplicationRunListener {
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @deprecated since 2.6.0 for removal in 2.8.0 in favour of
* {@link #running(ConfigurableApplicationContext, Duration)}
* @param timeTaken the time taken for the application to be ready or {@code null} if
* unknown
* @since 2.6.0
*/
default void ready(ConfigurableApplicationContext context, Duration timeTaken) {
running(context);
}
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @deprecated since 2.6.0 for removal in 2.8.0 in favor of
* {@link #ready(ConfigurableApplicationContext, Duration)}
* @since 2.0.0
*/
@Deprecated
default void running(ConfigurableApplicationContext context) {
running(context, null);
}
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @param readyTime the time taken for the application to be ready to service requests
* or {@code null} if unknown
* @since 2.6.0
*/
default void running(ConfigurableApplicationContext context, Duration readyTime) {
running(context);
}
/**

View File

@@ -74,12 +74,12 @@ class SpringApplicationRunListeners {
doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context));
}
void started(ConfigurableApplicationContext context, Duration startupTime) {
doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, startupTime));
void started(ConfigurableApplicationContext context, Duration timeTaken) {
doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken));
}
void running(ConfigurableApplicationContext context, Duration startupTime) {
doWithListeners("spring.boot.application.running", (listener) -> listener.running(context, startupTime));
void ready(ConfigurableApplicationContext context, Duration timeTaken) {
doWithListeners("spring.boot.application.running", (listener) -> listener.ready(context, timeTaken));
}
void failed(ConfigurableApplicationContext context, Throwable exception) {

View File

@@ -56,9 +56,9 @@ class StartupInfoLogger {
applicationLog.debug(LogMessage.of(this::getRunningMessage));
}
void logStarted(Log applicationLog, Duration startupTime) {
void logStarted(Log applicationLog, Duration timeTakeToStartup) {
if (applicationLog.isInfoEnabled()) {
applicationLog.info(getStartedMessage(startupTime));
applicationLog.info(getStartedMessage(timeTakeToStartup));
}
}
@@ -83,12 +83,12 @@ class StartupInfoLogger {
return message;
}
private CharSequence getStartedMessage(Duration startupTime) {
private CharSequence getStartedMessage(Duration timeTakeToStartup) {
StringBuilder message = new StringBuilder();
message.append("Started ");
appendApplicationName(message);
message.append(" in ");
message.append(startupTime.toMillis() / 1000.0);
message.append(timeTakeToStartup.toMillis() / 1000.0);
message.append(" seconds");
try {
double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0;

View File

@@ -37,7 +37,7 @@ public class ApplicationReadyEvent extends SpringApplicationEvent {
private final ConfigurableApplicationContext context;
private final Duration readyTime;
private final Duration timeTaken;
/**
* Create a new {@link ApplicationReadyEvent} instance.
@@ -57,14 +57,14 @@ public class ApplicationReadyEvent extends SpringApplicationEvent {
* @param application the current application
* @param args the arguments the application is running with
* @param context the context that was being created
* @param readyTime the time taken to get the application ready to service requests
* @param timeTaken the time taken to get the application ready to service requests
* @since 2.6.0
*/
public ApplicationReadyEvent(SpringApplication application, String[] args, ConfigurableApplicationContext context,
Duration readyTime) {
Duration timeTaken) {
super(application, args);
this.context = context;
this.readyTime = readyTime;
this.timeTaken = timeTaken;
}
/**
@@ -79,9 +79,10 @@ public class ApplicationReadyEvent extends SpringApplicationEvent {
* Return the time taken for the application to be ready to service requests, or
* {@code null} if unknown.
* @return the time taken to be ready to service requests
* @since 2.6.0
*/
public Duration getReadyTime() {
return this.readyTime;
public Duration getTimeTaken() {
return this.timeTaken;
}
}

View File

@@ -36,7 +36,7 @@ public class ApplicationStartedEvent extends SpringApplicationEvent {
private final ConfigurableApplicationContext context;
private final Duration startedTime;
private final Duration timeTaken;
/**
* Create a new {@link ApplicationStartedEvent} instance.
@@ -57,14 +57,14 @@ public class ApplicationStartedEvent extends SpringApplicationEvent {
* @param application the current application
* @param args the arguments the application is running with
* @param context the context that was being created
* @param startedTime the time taken to start the application
* @param timeTaken the time taken to start the application
* @since 2.6.0
*/
public ApplicationStartedEvent(SpringApplication application, String[] args, ConfigurableApplicationContext context,
Duration startedTime) {
Duration timeTaken) {
super(application, args);
this.context = context;
this.startedTime = startedTime;
this.timeTaken = timeTaken;
}
/**
@@ -78,9 +78,10 @@ public class ApplicationStartedEvent extends SpringApplicationEvent {
/**
* Return the time taken to start the application, or {@code null} if unknown.
* @return the startup time
* @since 2.6.0
*/
public Duration getStartedTime() {
return this.startedTime;
public Duration getTimeTaken() {
return this.timeTaken;
}
}

View File

@@ -104,14 +104,14 @@ public class EventPublishingRunListener implements SpringApplicationRunListener,
}
@Override
public void started(ConfigurableApplicationContext context, Duration startedTime) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, startedTime));
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
@Override
public void running(ConfigurableApplicationContext context, Duration readyTime) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, readyTime));
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}

View File

@@ -363,7 +363,7 @@ class SpringApplicationTests {
void applicationRunningEventListener() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
AtomicReference<ApplicationReadyEvent> reference = setupListener(application, ApplicationReadyEvent.class);
AtomicReference<ApplicationReadyEvent> reference = addListener(application, ApplicationReadyEvent.class);
this.context = application.run("--foo=bar");
assertThat(application).isSameAs(reference.get().getSpringApplication());
}
@@ -372,7 +372,7 @@ class SpringApplicationTests {
void contextRefreshedEventListener() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
AtomicReference<ContextRefreshedEvent> reference = setupListener(application, ContextRefreshedEvent.class);
AtomicReference<ContextRefreshedEvent> reference = addListener(application, ContextRefreshedEvent.class);
this.context = application.run("--foo=bar");
assertThat(this.context).isSameAs(reference.get().getApplicationContext());
// Custom initializers do not switch off the defaults
@@ -405,18 +405,18 @@ class SpringApplicationTests {
void applicationStartedEventHasStartedTime() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
AtomicReference<ApplicationStartedEvent> reference = setupListener(application, ApplicationStartedEvent.class);
AtomicReference<ApplicationStartedEvent> reference = addListener(application, ApplicationStartedEvent.class);
this.context = application.run();
assertThat(reference.get()).isNotNull().extracting(ApplicationStartedEvent::getStartedTime).isNotNull();
assertThat(reference.get()).isNotNull().extracting(ApplicationStartedEvent::getTimeTaken).isNotNull();
}
@Test
void applicationReadyEventHasReadyTime() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
AtomicReference<ApplicationReadyEvent> reference = setupListener(application, ApplicationReadyEvent.class);
AtomicReference<ApplicationReadyEvent> reference = addListener(application, ApplicationReadyEvent.class);
this.context = application.run();
assertThat(reference.get()).isNotNull().extracting(ApplicationReadyEvent::getReadyTime).isNotNull();
assertThat(reference.get()).isNotNull().extracting(ApplicationReadyEvent::getTimeTaken).isNotNull();
}
@Test
@@ -1252,24 +1252,10 @@ class SpringApplicationTests {
&& ((AvailabilityChangeEvent<?>) argument).getState().equals(state);
}
private <T extends ApplicationEvent> AtomicReference<T> setupListener(SpringApplication application,
Class<T> targetEventType) {
final AtomicReference<T> reference = new AtomicReference<>();
class TestEventListener implements SmartApplicationListener {
@Override
@SuppressWarnings("unchecked")
public void onApplicationEvent(ApplicationEvent event) {
reference.set((T) event);
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return targetEventType.isAssignableFrom(eventType);
}
}
application.addListeners(new TestEventListener());
private <E extends ApplicationEvent> AtomicReference<E> addListener(SpringApplication application,
Class<E> eventType) {
AtomicReference<E> reference = new AtomicReference<>();
application.addListeners(new TestEventListener<>(eventType, reference));
return reference;
}
@@ -1302,6 +1288,30 @@ class SpringApplicationTests {
};
}
static class TestEventListener<E extends ApplicationEvent> implements SmartApplicationListener {
private final Class<E> eventType;
private final AtomicReference<E> reference;
TestEventListener(Class<E> eventType, AtomicReference<E> reference) {
this.eventType = eventType;
this.reference = reference;
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return this.eventType.isAssignableFrom(eventType);
}
@Override
@SuppressWarnings("unchecked")
public void onApplicationEvent(ApplicationEvent event) {
this.reference.set((E) event);
}
}
@Configuration
static class InaccessibleConfiguration {

View File

@@ -25,7 +25,6 @@ import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.boot.system.ApplicationPid;
import org.springframework.util.StopWatch;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -56,11 +55,9 @@ class StartupInfoLoggerTests {
@Test
void startedFormat() {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
given(this.log.isInfoEnabled()).willReturn(true);
stopWatch.stop();
new StartupInfoLogger(getClass()).logStarted(this.log, Duration.ofMillis(stopWatch.getTotalTimeMillis()));
Duration timeTakeToStartup = Duration.ofMillis(10);
new StartupInfoLogger(getClass()).logStarted(this.log, timeTakeToStartup);
ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class);
verify(this.log).info(captor.capture());
assertThat(captor.getValue().toString()).matches("Started " + getClass().getSimpleName()

View File

@@ -74,7 +74,7 @@ class EventPublishingRunListenerTests {
context.refresh();
this.runListener.started(context, null);
checkApplicationEvents(ApplicationStartedEvent.class, AvailabilityChangeEvent.class);
this.runListener.running(context, null);
this.runListener.ready(context, null);
checkApplicationEvents(ApplicationReadyEvent.class, AvailabilityChangeEvent.class);
}