10 Most Valuable Features in Spring Boot Practice

This article presents ten highly valuable Spring Boot features—including global exception handling, MVC interceptors, programmatic bean access, @Import usage, startup runners, BeanDefinition modification, bean lifecycle callbacks, and custom scopes—each illustrated with concrete code examples and explanations for building cleaner, more maintainable Java backend applications.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
10 Most Valuable Features in Spring Boot Practice

Environment: Spring Boot 3.5.0

1. Introduction

This article introduces ten highly valuable features for Spring Boot project development.

2.1 Global Exception Handling

When an exception is thrown, the default response shows a stack trace, which harms user experience. Using @RestControllerAdvice with @ExceptionHandler allows unified handling and structured error responses.

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    // If id not found, original exception is thrown
    return userRepository.findById(id).orElseThrow();
}
Error page example
Error page example
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        if (e instanceof NoSuchElementException) {
            return "数据不存在";
        }
        if (e instanceof Exception) {
            return "服务异常";
        }
        // ...
    }
}

2.2 Interceptor

Spring MVC interceptors can access HttpServletRequest and HttpServletResponse. The core interface HandlerInterceptor defines three methods: preHandle, postHandle, and afterCompletion. They are suitable for permission checks, logging, and request timing.

public class CustomInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // permission check, return true to continue
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {
        // modify response or add data to model
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {
        // log request time or clean resources
    }
}
public class RepeatWebMvcConfiguration implements WebMvcConfigurer {
    private final CustomInterceptor customerInterceptor;
    public RepeatWebMvcConfiguration(CustomInterceptor customerInterceptor) {
        this.customerInterceptor = customerInterceptor;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.customerInterceptor)
                .addPathPatterns(List.of("/api/**"));
    }
}

2.3 Accessing Container Objects

Beyond constructor or @Autowired injection, beans can be retrieved programmatically via BeanFactoryAware or ApplicationContextAware.

@Component
public class CommonComponent implements BeanFactoryAware {
    private BeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    public void add() {
        Student student = (Student) beanFactory.getBean("student");
    }
}
@Component
public class CommonComponent implements ApplicationContextAware {
    private ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }
    public void add() {
        Student student = (Student) context.getBean("student");
    }
}

Direct autowiring of ApplicationContext is a cleaner alternative.

@Component
public class CommonComponent {
    private final ApplicationContext context;
    public CommonComponent(ApplicationContext context) {
        this.context = context;
    }
}

2.4 Import Configuration

The @Import annotation can bring ordinary classes, configuration classes, or selector classes into the Spring container.

Importing a simple class:

public class A {}
@Import(A.class)
@Configuration
public class TestConfiguration {}

Importing a configuration class:

@Import(B.class)
@Configuration
public class AConfiguration {
    @Bean
    public A a() { return new A(); }
}
@Import(AConfiguration.class)
@Configuration
public class TestConfiguration {}

Using ImportSelector to return multiple class names:

public class AImportSelector implements ImportSelector {
    private static final String CLASS_NAME = "com.pack.service.A";
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{CLASS_NAME};
    }
}
@Import(AImportSelector.class)
@Configuration
public class TestConfiguration {}

Implementing ImportBeanDefinitionRegistrar allows manual bean definition registration.

public class AImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(A.class);
        registry.registerBeanDefinition("a", rootBeanDefinition);
    }
}
@Import(AImportBeanDefinitionRegistrar.class)
@Configuration
public class TestConfiguration {}

2.5 Application Startup Add‑ons

To execute code at startup, implement CommandLineRunner or ApplicationRunner. Example using CommandLineRunner to load cache.

@Component
public class CacheLoader implements CommandLineRunner {
    private final StringRedisTemplate stringRedisTemplate;
    public CacheLoader(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public void run(String... args) throws Exception {
        // load cache
    }
}

2.6 Modify BeanDefinition

Implement BeanFactoryPostProcessor to change bean properties before instantiation.

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
        BeanDefinition beanDefinition = dlbf.getBeanDefinition("commonComponent");
        beanDefinition.getPropertyValues().addPropertyValue("id", 123);
        beanDefinition.getPropertyValues().addPropertyValue("name", "pack_xg");
    }
}

2.7 Bean Initialization Methods

Common ways to run code after bean creation include @PostConstruct and implementing InitializingBean.

@Service
public class AService {
    @PostConstruct
    public void init() {
        System.out.println("init...");
    }
}
@Service
public class BService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("init...");
    }
}

2.8 BeanPostProcessor

Implement BeanPostProcessor to add logic before or after bean initialization.

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof User) {
            ((User) bean).setName("Pack_xg");
        }
        return bean;
    }
}

2.9 Bean Destruction Callback

Implement DisposableBean or use @PreDestroy to run code when the container shuts down.

@Service
public class DService implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean destroy");
    }
}
@PreDestroy
public void close() {
    // ...
}

2.10 Custom Scope

Spring Boot provides six built‑in scopes. To create a thread‑local scope, implement Scope, register it via a BeanFactoryPostProcessor, and annotate beans with @Scope("threadLocalScope").

public class ThreadLocalScope implements Scope {
    private static final ThreadLocal<Object> THREAD_LOCAL_SCOPE =
        ThreadLocal.withInitial(() -> null);
    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object value = THREAD_LOCAL_SCOPE.get();
        if (value != null) {
            return value;
        }
        Object object = objectFactory.getObject();
        THREAD_LOCAL_SCOPE.set(object);
        return object;
    }
    @Override
    public Object remove(String name) {
        THREAD_LOCAL_SCOPE.remove();
        return null;
    }
    @Override public void registerDestructionCallback(String name, Runnable callback) {}
    @Override public Object resolveContextualObject(String key) { return null; }
    @Override public String getConversationId() { return null; }
}
@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
    }
}
@Scope("threadLocalScope")
@Service
public class CommonService {}
Spring Boot built‑in scopes table
Spring Boot built‑in scopes table

These ten features help developers write cleaner, more maintainable Spring Boot applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Exception HandlingSpring BootInterceptorBeanFactoryCommandLineRunnerCustom Scope
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

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.