Backend Development 16 min read

Unlock Spring’s Power: 11 Essential Extension Points You Must Master

This article walks through the eleven most commonly used Spring extension points—including custom interceptors, bean retrieval methods, global exception handling, type converters, import configurations, startup runners, bean definition tweaks, post‑processing, initialization and destruction callbacks, and custom scopes—providing clear explanations and code samples for each.

macrozheng
macrozheng
macrozheng
Unlock Spring’s Power: 11 Essential Extension Points You Must Master

Introduction

Spring’s core strengths lie in its IOC and AOP mechanisms, but its real power comes from its extensibility, allowing third‑party integrations such as RocketMQ, MyBatis, and Redis.

1. Custom Interceptor

Spring MVC interceptors can access

HttpServletRequest

and

HttpServletResponse

. The top‑level interface is

HandlerInterceptor

with three methods:

preHandle

,

postHandle

, and

afterCompletion

. Usually developers extend

HandlerInterceptorAdapter

to create an interceptor, register it as a bean, and add it via

addInterceptors

.

<code>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;
    }
}
</code>
<code>@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {
    @Bean
    public AuthInterceptor getAuthInterceptor() {
        return new AuthInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor());
    }
}
</code>

2. Accessing the Spring Container

You can obtain beans from the container via three approaches:

2.1 BeanFactoryAware

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

2.2 ApplicationContextAware

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

2.3 ApplicationListener

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

3. Global Exception Handling

Instead of catching exceptions in every controller, define a

@RestControllerAdvice

with an

@ExceptionHandler

method.

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

4. Type Converters

Spring supports three converter types:

Converter&lt;S,T&gt;

,

ConverterFactory&lt;S,R&gt;

, and

GenericConverter

. Example of converting a date string to

Date

:

<code>@Data
public class User {
    private Long id;
    private String name;
    private Date registerDate;
}
</code>
<code>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;
    }
}
</code>
<code>@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new DateConverter());
    }
}
</code>
<code>@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping("/save")
    public String save(@RequestBody User user) {
        return "success";
    }
}
</code>

5. Import Configuration

Use

@Import

to bring additional classes into the Spring context. Four typical usages:

Importing a plain class – the class becomes a bean automatically.

Importing a @Configuration class – its @Bean definitions, @Import, @ImportResource, @PropertySource, etc., are processed recursively.

Importing via

ImportSelector

– returns an array of class names to import.

Importing via

ImportBeanDefinitionRegistrar

– manually registers bean definitions.

<code>public class A {}

@Import(A.class)
@Configuration
public class TestConfiguration {}
</code>
<code>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 {}
</code>
<code>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 {}
</code>

6. Project Startup

Implement

CommandLineRunner

or

ApplicationRunner

to execute code after the application starts.

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

Multiple runners can be ordered with

@Order

or

@Priority

.

7. Modifying BeanDefinition

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

8. Bean Post Processor

<code>@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;
    }
}
</code>

Standard annotations like @Autowired, @Value, @Resource, and @PostConstruct are implemented via internal post‑processors.

9. Initialization Methods

Two common ways:

Annotate a method with

@PostConstruct

.

Implement

InitializingBean

and override

afterPropertiesSet

.

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

10. Before Container Shutdown

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

11. Custom Scope

Define a new scope (e.g., thread‑local) by implementing

Scope

and registering it.

<code>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; }
}
</code>
<code>@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
    }
}
</code>
<code>@Scope("threadLocalScope")
@Service
public class CService {
    public void add() {}
}
</code>
backendJavaIoCSpringExtension Points
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.