Master 10 Essential Spring Extension Points for Robust Backend Development
This article walks through the ten most useful Spring extension points—including global exception handling, custom interceptors, bean access, @Import usage, startup runners, BeanDefinition modification, initialization callbacks, BeanPostProcessor hooks, destroy callbacks, and custom scopes—providing clear explanations and ready‑to‑use code examples for each.
1. Global Exception Handling
When an exception occurs in a controller, exposing raw stack traces is a poor user experience. By using @RestControllerAdvice with an @ExceptionHandler method, you can return friendly error messages such as "params error" for arithmetic exceptions.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
if (e instanceof ArithmeticException) {
return "params error";
}
if (e instanceof Exception) {
return "Internal server exception";
}
return null;
}
}2. Custom Interceptor
Spring MVC interceptors allow you to intercept requests and obtain HttpServletRequest and HttpServletResponse. Implement HandlerInterceptor (or extend HandlerInterceptorAdapter) and override preHandle, postHandle, and afterCompletion as needed.
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestUrl = request.getRequestURI();
if (checkAuth(requestUrl)) {
return true;
}
return false;
}
private boolean checkAuth(String requestUrl) {
System.out.println("===Authority Verification===");
return true;
}
}Register the interceptor in a @Configuration class:
@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {
@Bean
public AuthInterceptor getAuthInterceptor() {
return new AuthInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor());
}
}3. Access Spring Container Objects
You can obtain beans from the container by implementing BeanFactoryAware or ApplicationContextAware and overriding the respective setter methods.
@Service
public class StudentService 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");
}
} @Service
public class StudentService2 implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void add() {
Student student = (Student) applicationContext.getBean("student");
}
}4. Import Configuration
Use @Import to bring additional classes or configuration classes into the Spring context. Four import styles are demonstrated: importing a plain class, importing a @Configuration class, using ImportSelector, and using ImportBeanDefinitionRegistrar.
@Import(A.class)
@Configuration
public class TestConfiguration {} public class AImportSelector implements ImportSelector {
private static final String CLASS_NAME = "com.demo.cache.service.A";
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{CLASS_NAME};
}
}
@Import(AImportSelector.class)
@Configuration
public class TestConfiguration {} 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 {}5. Additional Logic at Application Startup
Implement ApplicationRunner or CommandLineRunner to execute code when the Spring Boot application starts, such as loading system parameters or initializing caches.
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("Project startup: load system parameters...");
Properties properties = new Properties();
try (InputStream inputStream = new FileInputStream("application.properties")) {
properties.load(inputStream);
String systemParam = properties.getProperty("system.param");
System.out.println("Loaded system param: " + systemParam);
} catch (IOException e) {
e.printStackTrace();
}
}
}6. Modify BeanDefinition
Implement BeanFactoryPostProcessor to manipulate BeanDefinition objects before bean instantiation, e.g., registering a new bean definition programmatically.
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(User.class);
bdb.addPropertyValue("id", 123);
bdb.addPropertyValue("name", "Dylan Smith");
dlbf.registerBeanDefinition("user", bdb.getBeanDefinition());
}
}7. Initialization Methods
Use @PostConstruct or implement InitializingBean to run custom initialization logic after bean properties are set.
@Service
public class AService {
@PostConstruct
public void init() {
System.out.println("===Initializing===");
}
} @Service
public class BService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("===Initializing===");
}
}8. BeanPostProcessor Hooks
Implement BeanPostProcessor to add logic before and after bean initialization, such as setting a default user name.
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof User) {
((User) bean).setUserName("Dylan Smith");
}
return bean;
}
}9. Logic Before Container Shutdown
Implement DisposableBean (or use @PreDestroy) to execute cleanup code when the Spring context is closed.
@Service
public class DService implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet");
}
}10. Custom Scope
Define a custom scope by implementing Scope (e.g., a thread‑local scope) and register it via a BeanFactoryPostProcessor. Then annotate beans with @Scope("threadLocalScope").
public class ThreadLocalScope implements Scope {
private static final ThreadLocal<Object> THREAD_LOCAL_SCOPE = new ThreadLocal<>();
@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 CService {
public void add() {}
}These extension points empower developers to build flexible, maintainable, and robust Spring applications.
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.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
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.
