SoFunction
Updated on 2025-03-08

Summary of the 11 most commonly used extension points in Spring, how many do you know

Preface

During the process of using spring, have we found that it has strong expansion capabilities? Due to the existence of this advantage, spring is highly inclusive, so many third-party applications or frameworks can be easily invested in the arms of spring. Today we will mainly learn about the 11 commonly used expansion points in Spring. How many have you used?

1. Type converter

If there is a field type Date in the entity object receiving parameters in the interface, but the actual passed parameter is the string type: 2022-12-15 10:20:15, how to deal with it?

Spring provides an extension point, type converterType Converter, divided into 3 categories:

  • Converter<S,T>: Convert an object of type S to an object of type T
  • ConverterFactory<S, R>: Convert an S-type object to an R-type or its subclass object
  • GenericConverter: It supports conversions of multiple source and target types, and also provides context for source and target types. This context allows you to perform type conversion based on comments or attribute information.

If you still don’t understand, let’s give an example.

  • Define a user object
@Data
public class User {
    private Long id;
    private String name;
    private Date registerDate;
}
  • accomplishConverterinterface
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 {
                (source);
            } catch (ParseException e) {
                ();
            }
        }
        return null;
    }
}
  • Inject a newly defined type converter into a Spring container
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        (new DateConverter());
    }
}
  • Call interface test
@RequestMapping("/user")
    @RestController
    public class UserController {
        @RequestMapping("/save")
        public String save(@RequestBody User user) {
            return "success";
        }
    }

When requesting an interface, the date string passed in by the front end will be automatically converted to Date type.

2. Get container beans

In our daily development, we often need to get beans from Spring containers, but do you know how to get Spring container objects?

2.1 BeanFactoryAware

@Service
public class PersonService implements BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
         = beanFactory;
    }

    public void add() {
        Person person = (Person) ("person");
    }
}

Implement the BeanFactoryAware interface, and then override the setBeanFactory method, you can get the spring container object from the method.

2.2 ApplicationContextAware

@Service
public class PersonService2 implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         = applicationContext;
    }

    public void add() {
        Person person = (Person) ("person");
    }
}

accomplishApplicationContextAwareInterface, then rewritesetApplicationContextMethod, you can also obtain spring container objects through this method.

2.3 ApplicationListener

@Service
public class PersonService3 implements ApplicationListener<ContextRefreshedEvent> {
    private ApplicationContext applicationContext;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        applicationContext = ();
    }

    public void add() {
        Person person = (Person) ("person");
    }
}

3. Global exception handling

In the past, when we were developing the interface, if there were exceptions, we should give users more friendly prompts, such as:

@RequestMapping("/test")
@RestController
public class TestController {

    @GetMapping("/add")
    public String add() {
        int a = 10 / 0;
        return "su";
    }
}

If you do not do any processing to the result of adding an interface to the request, an error will be reported directly:

Can users see the error message directly?

This kind of interaction brings a very poor experience to the user. To solve this problem, we usually catch exceptions in the interface:

@GetMapping("/add")
public String add() {
    String result = "success";
    try {
        int a = 10 / 0;
    } catch (Exception e) {
        result = "error";
    }
    return result;
}

After the interface is modified, when an exception occurs, it will prompt: "Data exception", which is more user-friendly.

It looks good, but there is a problem.

It would be fine if it was just one interface, but if there were hundreds or thousands of interfaces in the project, would it still have to add exception capture code?

The answer is no, this is where global exception handling comes in handy:RestControllerAdvice

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler()
    public String handleException(Exception e) {
        if (e instanceof ArithmeticException) {
            return "data error";
        }
        if (e instanceof Exception) {
            return "service error";
        }
        retur null;
    }
}

Only the exception is handled in the methodhandleException, it can be used with peace of mind in the business interface, and no longer needs to catch exceptions (unified and personal handling).

4. Custom interceptor

Spring MVC interceptor, which can be obtainedHttpServletRequestandHttpServletResponseetc. web object instance.

The top-level interface of Spring MVC interceptor isHandlerInterceptor, it contains three methods:

  • preHandleExecute before the target method is executed
  • Execute the target method after executingpostHandle
  • afterCompletionExecute when the request is completed

For convenience, we generally inheritHandlerInterceptorAdapter, it implementsHandlerInterceptor

If there are authorization authentication, logging, statistics and other scenarios, you can use this interceptor, let’s demonstrate it.

  • Write a class inheritanceHandlerInterceptorAdapter
public class AuthInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {
        String requestUrl = ();
        if (checkAuth(requestUrl)) {
            return true;
        }
        return false;
    }
    private boolean checkAuth(String requestUrl) {
        return true;
    }
}
  • Register the interceptor into the spring container
@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {

    @Bean
    public AuthInterceptor getAuthInterceptor() {
        return new AuthInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        (new AuthInterceptor());
    }
}
  • Spring MVC can automatically intercept the interface when requesting an interface and verify permissions through the interceptor.

5. Import configuration

Sometimes we need to introduce other classes into a configuration class, and the introduced classes are also added to the Spring container. You can use annotations at this time@ImportTo complete this function.

If you look at its source code, you will find that the imported class supports three different types.

But I think it's better to configure the normal class and@ConfigurationAnnotations are explained separately, so four different types are listed:

5.1 General Class

This method of introduction is the easiest, the introduced class will be instantiated into a bean object.

public class A {
}

@Import()
@Configuration
public class TestConfiguration {
    
}

pass@ImportAnnotation introduces class A, spring can automatically instantiate A objects, and then use annotations where they need to be used.@Autowiredinjection:

@Autowired
private A a;

5.2 Configuration class

This introduction method is the most complex, because @Configuration supports also support multiple combination annotations, such as:

  • @Import
  • @ImportResource
  • @PropertySource
public class A {
}

public class B {
}

@Import()
@Configuration
public class AConfiguration {

    @Bean
    public A a() {
        return new A();
    }
}

@Import()
@Configuration
public class TestConfiguration {
}

@ConfigurationThe annotated configuration class passes@ImportAnnotation import, configuration class@Import@ImportResourceClasses introduced by related annotations will be introduced recursively at once.@PropertySourceThe attributes that are located.

5.3 ImportSelector

This import method needs to be implementedImportSelectorinterface

public class AImportSelector implements ImportSelector {

    private static final String CLASS_NAME = ".";

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{CLASS_NAME};
    }
}

@Import()
@Configuration
public class TestConfiguration {
}

The benefit of this approach isselectImportsThe method returns an array, which means that multiple classes can be introduced at the same time, which is very convenient.

5.4 ImportBeanDefinitionRegistrar

This import method needs to be implementedImportBeanDefinitionRegistrarinterface:

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

@Import()
@Configuration
public class TestConfiguration {
}

This method is the most flexible. The container registration object can beregisterBeanDefinitionsGet it from the method, can be created manuallyBeanDefinitionRegister toBeanDefinitionRegistrykind.

6. When the project starts

Sometimes we need to customize some extra functions when the project is started, such as loading some system parameters, completing initialization, warming up the local cache, etc. What should we do?

The good news is that SpringBoot provides:

  • CommandLineRunner
  • ApplicationRunner

These two interfaces help us realize the above requirements.

Their usage is simple,ApplicationRunnerInterface as an example:

@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    private LoadDataService loadDataService;

    public void run(ApplicationArguments args) throws Exception {
        ();
    }
}

accomplishApplicationRunnerInterface, rewriterunMethod, implement your custom requirements in this method.

If there are multiple classes in the projectApplicationRunnerInterfaces, how to specify their execution order?

The answer is to use @Order(n) annotation, the smaller the value of n, the earlier it executes. Of course, the order can also be passed@PriorityAnnotation is specified.

7. Modify BeanDefinition

Before instantiating the Bean object,Spring IOCNeed to read the relevant properties of the bean and save them inBeanDefinitionIn the object, then passBeanDefinitionObject InstantiationBeanObject.

What if you want to modify the properties in the BeanDefinition object?

Answer: We can achieveBeanFactoryPostProcessorInterface.

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
        BeanDefinitionBuilder beanDefinitionBuilder = ();
        ("id", 123);
        ("name", "Tom");
        ("user", ());
    }
}

existpostProcessBeanFactoryIn the method, you can obtainBeanDefinitionrelated objects, modify the properties of the object.

8. Initialize Beans before and after

Sometimes you want to implement some of your own logic before and after bean initialization.

This is the case:BeanPostProcessorinterface.

There are currently two methods for this interface:

  • postProcessBeforeInitialization: It should be called before initializing the method.
  • postProcessAfterInitialization: This method is called after initializing the method.
@Component
    public class MyBeanPostProcessor implements BeanPostProcessor {

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof User) {
                ((User) bean).setUserName("Tom");
            }
            return bean;
        }
    }

We use it frequently@Autowired@Value@Resource@PostConstructAll the annotations are passedAutowiredAnnotationBeanPostProcessorandCommonAnnotationBeanPostProcessorTo achieve it.

9. Initialization method

Currently there are many ways to initialize beans in Spring:

  • use@PostConstructannotation
  • accomplishInitializingBeaninterface

9.1 Use@PostConstruct

@Service
public class AService {
    @PostConstruct
    public void init() {
        ("===init===");
    }
}

Add annotations to methods that need to be initialized@PostConstruct, make it execute when bean is initialized.

9.2 Implementing the initialization interfaceInitializingBean

@Service
public class BService implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        ("===init===");
    }
}

accomplishInitializingBeanInterface, rewriteafterPropertiesSetMethod, in which the initialization function can be completed.

10. Before closing Spring container

Sometimes, we need to do some extra work before closing the spring container, such as closing the resource file.

You can do it nowDisposableBeanInterface and rewrite itsdestroymethod.

@Service
public class DService implements InitializingBean, DisposableBean {

    @Override
    public void destroy() throws Exception {
        ("DisposableBean destroy");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ("InitializingBean afterPropertiesSet");
    }
}

This way, before the spring container is destroyed, it will be calleddestroyMethod to do some extra work.

Usually we will implement it at the same timeInitializingBeanandDisposableBeanInterface, override the initialization method and the destruction method.

11. Customize the scope of the bean

We all know that spring core only supports two types by defaultScope

  • SingletonSingleton, each bean obtained from the spring container is the same object.
  • prototypeMultiple instances, each time the beans obtained from the spring container are different objects.

Spring Web has expanded Scope again, adding

  • RequestScope: The beans retrieved from the spring container in the same request are the same object.
  • SessionScope: The beans retrieved from the spring container of the same session are the same object.

Despite this, some scenarios do not meet our requirements.

For example, we need to go fromspringGetted in the containerbeanWhat should I do if I am all the same object?

Answer: This requires a custom range.

  • accomplishScopeInterface
public class ThreadLocalScope implements Scope {
    private static final ThreadLocal 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 = ();
        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;
    }
}
  • Inject the newly defined Scope into the Spring container
@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ("threadLocalScope", new ThreadLocalScope());
    }
}
  • Use the newly defined Scope
@Scope("threadLocalScope")
@Service
public class CService {
    public void add() {
    }
}

Summarize

This article summarizes 11 commonly used extension points in Spring. You can inject your desired logic at each stage of bean creation, initialization and destruction. There are also extension points such as Spring MVC-related interceptors, which I hope will be helpful to everyone.

This is the end of this article about the 11 most commonly used extension points in Spring. For more relevant contents of Spring's commonly used extension points, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!