Master Spring Boot Bean Lifecycle: Init, Destroy, Scopes & Advanced Tricks
This article explains how to control Spring bean initialization and destruction using interfaces and annotations, inject prototype beans into singleton controllers, implement various BeanPostProcessor extensions, dynamically register beans, use @Import, Runner interfaces, global exception handling, custom type conversion, BeanFactoryAware, web interceptors, and default AOP proxy creation.
1. Bean Initialization and Destruction
To execute custom logic during bean creation, implement InitializingBean or annotate a method with @PostConstruct . To run cleanup code when the container shuts down, implement DisposableBean or use @PreDestroy .
<code>@Component
public class InitDataToRedis implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// initialization logic
}
}
@Component
public class PersonService {
@PostConstruct
public void init() {
// initialization logic
}
}
public class Person implements DisposableBean {
@Override
public void destroy() {
// destruction logic
}
}
@Component
public class PersonService {
@PreDestroy
public void destroy() {
// destruction logic
}
}</code>2. Injecting Prototype Bean into a Singleton Bean
Define a prototype-scoped bean and inject it into a singleton controller using one of three methods.
<code>// Prototype bean
@Component
@Scope("prototype")
public class PersonService {}
// Singleton controller
@Controller
public class PersonController {}
</code>Method 1: Use @Lazy on the field.
<code>@Lazy
private PersonService personService;
</code>Method 2: Define the prototype bean with a scoped proxy.
<code>@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PersonService {}
</code>Method 3: Retrieve the bean from ApplicationContext manually.
<code>public class PersonController {
@Resource
private ApplicationContext context;
public Object index() {
PersonService ps = this.context.getBean(PersonService.class);
}
}
</code>3. BeanPostProcessor Interface
Implement BeanPostProcessor to run logic before and after bean initialization.
<code>public class PackBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// logic before init
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// logic after init
return bean;
}
}
</code>4. BeanFactoryPostProcessor Interface
Implement this interface to modify BeanDefinition before bean instances are created.
<code>public class PackBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition definition = beanFactory.getBeanDefinition("person");
// change scope to prototype
definition.setScope("prototype");
}
}
</code>5. Dynamic Bean Registration
Register beans at runtime, for example by scanning packages and adding definitions programmatically.
<code>public class RepositoryBeanDefinitionRegister implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// Dynamically register Person bean
registry.registerBeanDefinition(
"person",
BeanDefinitionBuilder.genericBeanDefinition(Person.class).getBeanDefinition()
);
}
}
</code>6. Importing Configuration Classes
Refer to the linked article for three common uses of the @Import annotation to import configuration classes.
7. *Runner Interfaces
Execute code after the application has started by implementing ApplicationRunner or CommandLineRunner .
<code>public class App implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// task execution
}
}
// or
public class App implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// task execution
}
}
</code>8. Global Exception Handling
Register a global exception handler to unify error responses.
<code>@RestControllerAdvice
public class GlobalControllerAdvice {
@ExceptionHandler({Exception.class})
public Object handle(Exception e) {
// other handling
return R.failure(R.ResultCode.FAILURE, e.getMessage());
}
}
</code>10. Global Type Conversion
Define a custom converter to transform strings into domain objects.
<code>public class CustomGenericConverter implements GenericConverter {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> pairs = new HashSet<>();
pairs.add(new ConvertiblePair(String.class, Teacher.class));
pairs.add(new ConvertiblePair(String.class, Student.class));
return pairs;
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
String str = (String) source;
if (targetType.getObjectType() == Teacher.class) {
String[] t = str.split("\\|");
return new Teacher(t[0], Integer.valueOf(t[1]));
}
if (targetType.getObjectType() == Student.class) {
String[] t = str.split("\\|");
return new Student(t[0], t[1]);
}
return null;
}
}
@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new CustomGenericConverter());
}
}
</code>11. Accessing BeanFactory
Implement BeanFactoryAware to obtain the BeanFactory inside a bean.
<code>@Service
public class PersonService implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
</code>Other *Aware interfaces include ApplicationContextAware , ServletContextAware , BeanNameAware , EnvironmentAware , etc.
12. Web Interceptors
Register custom interceptors for request pre‑handling, post‑handling, and after‑completion logic.
<code>@Component
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle method invoke...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle method invoke...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion method invoke...");
}
}).addPathPatterns("/**");
}
}
</code>13. Default AOP Proxy Creation
Use BeanNameAutoProxyCreator to automatically create proxies for beans whose names match a pattern.
<code>@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
creator.setBeanNames("*Service");
creator.setInterceptorNames("tokenInterceptor");
return creator;
}
</code>The above proxy creator will apply the tokenInterceptor to all beans ending with Service .
These are common techniques used in Spring Boot projects.
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.