How to Accurately Measure Spring Boot Bean Creation Time for Performance Optimization
This article demonstrates a non‑intrusive, high‑precision method to track each Spring Boot bean's creation time by customizing ApplicationContextFactory, Environment, and BeanFactory, allowing package‑level exclusion and clear console reporting to pinpoint slow‑loading beans during startup.
In Spring Boot startup optimization, bean creation time can be a critical bottleneck. Traditional monitoring cannot precisely measure each bean's instantiation and initialization, nor separate the time spent on dependent beans. The article presents a non‑intrusive, high‑precision approach that customizes ApplicationContextFactory, Environment, and BeanFactory, supports package‑exclude configuration, and provides clear console output for slow‑loading beans.
Custom ApplicationContextFactory
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PackServletWebServerApplicationContextFactory implements ApplicationContextFactory {
@Override
public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : PackApplicationServletEnvironment.class;
}
@Override
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : new PackApplicationServletEnvironment();
}
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : createContext();
}
private ConfigurableApplicationContext createContext() {
PackListableBeanFactory beanFactory = new PackListableBeanFactory();
if (!AotDetector.useGeneratedArtifacts()) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(beanFactory);
beanFactory.setContext(context);
return context;
}
ServletWebServerApplicationContext context = new ServletWebServerApplicationContext(beanFactory);
beanFactory.setContext(context);
return context;
}
}Configure the factory in META-INF/spring.factories:
org.springframework.boot.ApplicationContextFactory=\
com.pack.PackServletWebServerApplicationContextFactoryCustom Environment
public class PackApplicationServletEnvironment extends StandardServletEnvironment {
@Override
protected String doGetActiveProfilesProperty() { return null; }
@Override
protected String doGetDefaultProfilesProperty() { return null; }
@Override
protected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {
return ConfigurationPropertySources.createPropertyResolver(propertySources);
}
}Custom BeanFactory
public class PackListableBeanFactory extends DefaultListableBeanFactory {
private static final Map<String, Long> beanCreationTimeMap = new ConcurrentHashMap<>();
private ApplicationContext context;
private volatile boolean initialized = false;
private List<String> exclude = new ArrayList<>();
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (!initialized) {
Binder.get(this.context.getEnvironment()).bind("pack.statistics.exclude", Bindable.ofInstance(exclude));
initialized = true;
}
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass == null) {
beanClass = mbd.getTargetType();
}
for (String pkg : exclude) {
if (beanClass.getPackageName().startsWith(pkg)) {
return super.createBean(beanName, mbd, args);
}
}
long start = System.nanoTime();
Object bean = super.createBean(beanName, mbd, args);
long end = System.nanoTime();
double durationMillis = (end - start) / 1_000_000.0;
beanCreationTimeMap.put(beanName, end - start);
System.out.printf("Bean [%s] created in %.2f ms%n", beanName, durationMillis);
return bean;
}
public void setContext(ApplicationContext context) { this.context = context; }
}Test Scenario
@Configuration
public class CustomConfig {
@Bean
Date aaaa() {
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
return new Date();
}
}
@Component
public class B {
@PostConstruct
public void init() {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
}
}After starting the container, the console prints the creation times (image below).
Adding Dependency Between Beans
@Component
public class B {
@Resource
private Date aaaa;
@PostConstruct
public void init() {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {}
}
}Running the modified setup produces a new console output (image below) showing the combined timing.
Further Precision
If you need to exclude the time spent on injecting other dependencies, you can override additional methods in the custom factory to isolate pure bean instantiation cost.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
