Master the 11 Most Powerful Spring Extension Points for Advanced Backend Development

This article explores Spring's eleven most commonly used extension points—including custom interceptors, bean factory access, global exception handling, type converters, configuration imports, startup runners, bean definition tweaks, bean post‑processing, initialization methods, shutdown hooks, and custom scopes—providing code examples and practical guidance for Java backend developers.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Master the 11 Most Powerful Spring Extension Points for Advanced Backend Development

When we mention Spring, IOC (Inversion of Control) and AOP (Aspect‑Oriented Programming) are the first things that come to mind, yet its true power lies in the extensive extension capabilities that let third‑party libraries such as RocketMQ, MyBatis and Redis integrate seamlessly.

1. Custom Interceptor

Spring MVC interceptors can access HttpServletRequest and HttpServletResponse. The top‑level interface is HandlerInterceptor with methods preHandle, postHandle and afterCompletion. Usually we extend HandlerInterceptorAdapter to create a concrete interceptor.

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("===权限校验===");
        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());
    }
}

2. Obtaining the Spring Container

Three ways to get the container object:

2.1 BeanFactoryAware

@Service
public class PersonService implements BeanFactoryAware {
    private BeanFactory beanFactory;
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    public void add() {
        Person person = (Person) beanFactory.getBean("person");
    }
}

2.2 ApplicationContextAware

@Service
public class PersonService2 implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    public void add() {
        Person person = (Person) applicationContext.getBean("person");
    }
}

2.3 ApplicationListener

@Service
public class PersonService3 implements ApplicationListener<ContextRefreshedEvent> {
    private ApplicationContext applicationContext;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        applicationContext = event.getApplicationContext();
    }
    public void add() {
        Person person = (Person) applicationContext.getBean("person");
    }
}

3. Global Exception Handling

Instead of catching exceptions in every controller, define a @RestControllerAdvice:

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {
        if (e instanceof ArithmeticException) {
            return "数据异常";
        }
        if (e instanceof Exception) {
            return "服务器内部异常";
        }
        return null;
    }
}

4. Type Converters

Spring supports three converter types. Example using Converter<String, Date> to convert a date string to a Date object.

@Data
public class User {
    private Long id;
    private String name;
    private Date registerDate;
}
public class DateConverter implements Converter<String, Date> {
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Override
    public Date convert(String source) {
        if (source != null && !"".equals(source)) {
            try {
                return simpleDateFormat.parse(source);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConverter());
    }
}

5. Importing Configuration

The @Import annotation can bring in ordinary classes, other @Configuration classes, ImportSelector implementations, or ImportBeanDefinitionRegistrar implementations.

Import Types Diagram
Import Types Diagram

5.1 Ordinary Class

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

5.2 Configuration Class

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

5.3 ImportSelector

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

5.4 ImportBeanDefinitionRegistrar

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

6. Code Execution at Application Startup

Implement ApplicationRunner (or CommandLineRunner) to run custom logic after the Spring context is ready.

@Component
public class TestRunner implements ApplicationRunner {
    @Autowired
    private LoadDataService loadDataService;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        loadDataService.load();
    }
}

Use @Order or @Priority to control execution order when multiple runners exist.

7. Modifying BeanDefinition

Implement BeanFactoryPostProcessor to change bean definitions before instantiation.

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        builder.addPropertyValue("id", 123);
        builder.addPropertyValue("name", "苏三说技术");
        ((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("user", builder.getBeanDefinition());
    }
}

8. Bean Post‑Processing

Implement BeanPostProcessor to run 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).setUserName("苏三说技术");
        }
        return bean;
    }
}

9. Initialization Methods

Two common ways:

Annotate a method with @PostConstruct.

Implement InitializingBean and override afterPropertiesSet.

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

10. Actions Before Container Shutdown

Implement DisposableBean (or use @PreDestroy) to release resources.

@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"); }
}

11. Custom Scope

Define a new scope (e.g., thread‑local) by implementing Scope and registering it via a BeanFactoryPostProcessor.

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 obj = objectFactory.getObject();
        THREAD_LOCAL_SCOPE.set(obj);
        return obj;
    }
    @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());
    }
}

Use the custom scope on a bean:

@Scope("threadLocalScope")
@Service
public class CService { public void add() { } }
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.

JavaBackend DevelopmentspringSpring MVCExtension Points
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.