Backend Development 9 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot Bean Lifecycle: Init, Destroy, Scopes & Advanced Tricks

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.

JavaBackend DevelopmentSpring Bootdependency injectionbean lifecycle
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.