Spring常用扩展点

这篇具有很好参考价值的文章主要介绍了Spring常用扩展点。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言介绍

1、常用扩展点

我们知道,IOC(控制反转) 和 AOP(面向切面编程)是spring的基石。除此之外,spring的扩展能力非常强。由于这个优势的存在,让spring拥有强大的包容能力,让很多第三方应用能够轻松投入spring的怀抱。比如:rocketmq、mybatis、redis等。

Spring中最常用的11个扩展点

  • 自定义拦截器

  • Spring容器对象获取

  • 全局异常处理

  • 类型转换器

  • 导入配置

  • 项目启动配置

  • BeanDefinition的修改

  • Bean前后初始化

  • 初始化方法

  • 容器关闭

  • 自定义作用域

2、Spring所有扩展接口

Spring的核心思想就是容器,内部想要把自动装配玩的转,就必须要了解spring对于bean的构造生命周期以及各个扩展接口。当然业务代码也能合理利用这些扩展点写出更加漂亮的代码。这里总结了几乎Spring & Springboot所有的扩展接口,以及各个扩展点的使用场景。下面是一个bean在spring内部从被加载到最后初始化完成所有可扩展点的顺序调用图

Spring常用扩展点

二、常用扩展点详解

1、自定义拦截器

1.1 介绍

spring mvc拦截器与spring拦截器相比,它里面能够获取HttpServletRequestHttpServletResponse等web对象实例。
可以参考:springBoot整合JWT实现前后端Token验证

spring mvc拦截器的顶层接口是:HandlerInterceptor,包含三个方法:

  • preHandle目标方法执行前执行

  • postHandle 目标方法执行后执行

  • afterCompletion请求完成时执行

一般情况会用HandlerInterceptor接口的实现类HandlerInterceptorAdapter类。假如有权限认证、日志、统计的场景,可以使用该拦截器。

1.2 代码详情

首先继承HandlerInterceptorAdapter类定义拦截器

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;
    }
}

然后将该拦截器注册到spring容器

@Configuration
public class WebAuthConfig extends WebMvcConfigurerAdapter {
 
    @Bean
    public AuthInterceptor getAuthInterceptor() {
        return new AuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor());
    }
}

最后在请求接口时spring mvc通过该拦截器,能够自动拦截该接口,并且校验权限。

2、获取Spring容器对象

借鉴参考:Spring事件监听

在我们日常开发中,除了通过@Autowired注解获取Bean外,有时候我们会经常需要从Spring容器中手动获取Bean,下面是几种手动获取Bean的方法

2.1 BeanFactoryAware接口

实现BeanFactoryAware接口,然后重写setBeanFactory方法,就能从该方法中获取到spring容器对象

@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接口

实现ApplicationContextAware接口,然后重写setApplicationContext方法,也能从该方法中获取到spring容器对象

@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、全局异常处理

参考:Spring Boot后端接口规范

我们在开发接口时,如果出现异常,为了给用户一个更友好的提示,必须在每一个接口处捕获异常,但是随着接口数量增加,代码量和可维护性也随之增加,因此全局异常捕获就派上用场了:RestControllerAdvice。只需在handleException方法中处理异常情况,业务接口中可以放心使用,不再需要捕获异常(程序统一处理了)

@RestControllerAdvice
public class GlobalExceptionHandler {

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

4、类型转换器

4.1 介绍

spring目前支持3中类型转换器

  • Converter<S,T>:将 S 类型对象转为 T 类型对象

  • ConverterFactory<S, R>:将 S 类型对象转为 R 类型及子类对象

  • GenericConverter:它支持多个source和目标类型的转化,同时还提供了source和目标类型的上下文,这个上下文能让你实现基于属性上的注解或信息来进行类型转换

常用的日期类转换可以参考:Java8 日期时间类整理

4.2 简单实战

通过自定义Converter,可以转换简单的参数,如下,有一个 UserDto 类,表示用户信息

public class UserDto {
    //用户名
    private String name;
    //年龄
    private Integer age;
    
    //省略getter、setter方法
}

要求后台所有接口接受UserDto数据时,参数的值格式为:name,age

@RequestMapping("/convert/test1")
public UserDto test1(@RequestParam("user") UserDto user) {
    System.out.println("name:" + user.getName());
    System.out.println("age:" + user.getAge());
    return user;
}

SpringMVC 中提供了一个接口org.springframework.core.convert.converter.Converter,这个接口用来将一种类型转换为另一种类型,调用后端接口的时候,http 传递的参数都是字符串类型的,但是后端却可以使用 Integer、Double 等其他类型来接收,这就是Converter实现的。Spring 内部也提供了很多默认的实现,用于各种类型转换

@FunctionalInterface
public interface Converter<S, T> {

 /**
  * 将source转换为目标T类型
  */
 @Nullable
 T convert(S source);
}
  • 代码如下,添加一个配置类,实现WebMvcConfigurer接口

  • 重写addFormatters方法,在这个方法中添加一个自定义的Converter,实现其 convert 方法,将name,age格式的字符串转换为 UserDto 对象返回

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String, UserDto>() {
            @Override
            public UserDto convert(String source) {
                if (source == null) {
                    return null;
                }
                String[] split = source.split(",");
                String name = split[0];
                Integer age = Integer.valueOf(split[1]);
                return new UserDto(name, age);
            }
        });
    }
}

最后访问/convert/test1?user=ready,1即可成功获取数据

5、配置的导入

5.1 介绍

参考:Spring容器加入bean的几种方式

有时我们需要在某个配置类中引入另外一些类,被引入的类也加到spring容器中。这时可以使用@Import注解完成这个功能。通过源码会发现,引入的类支持三种不同类型,将普通类和@Configuration注解的配置类分开讲解,列了四种不同类型

  • 普通类

  • @Configuration注解

  • ImportSelector接口类

  • ImportBeanDefinitionRegistrar接口类

5.2 普通类

这种引入方式是最简单的,被引入的类会被实例化bean对象

public class A {
}

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

通过@Import注解引入A类,spring就能自动实例化A对象,然后在需要使用的地方通过@Autowired注解注入即可,不用加@Bean注解也能实例化bean

5.3 配置类

这种引入方式是最复杂的,因为@Configuration注解还支持多种组合注解,比如:

  • @Import

  • @ImportResource

  • @PropertySource等

通过@Import注解引入@Configuration注解的配置类,会把该配置类相关@Import@ImportResource@PropertySource等注解引入的类进行递归,一次性全部引入。

public class A {
}

public class B {
}

@Import(B.class)
@Configuration
public class AConfiguration {

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

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

5.4 ImportSelector

这种引入方式需要实现ImportSelector接口,好处是selectImports方法返回的是数组,意味着可以同时引入多个类,还是非常方便的

public class AImportSelector implements ImportSelector {

private static final String CLASS_NAME = "com.sue.cache.service.test13.A";
    
 public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{CLASS_NAME};
    }
}

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

5.5 ImportBeanDefinitionRegistrar

种引入方式需要实现ImportBeanDefinitionRegistrar接口,这种方式是最灵活的,能在registerBeanDefinitions方法中获取到BeanDefinitionRegistry容器注册对象,可以手动控制BeanDefinition的创建和注册。

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 {
}

6、项目启动时

有时候我们需要在项目启动时定制化一些附加功能,比如:加载一些系统参数、完成初始化、预热本地缓存等,该怎么办呢?好消息是springboot提供了:

  • CommandLineRunner

  • ApplicationRunner

这两个接口帮助我们实现以上需求。它们的用法很简单的,以ApplicationRunner接口为例:

@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    private LoadDataService loadDataService;

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

实现ApplicationRunner接口,重写run方法,在该方法中实现自己定制化需求。如果项目中有多个类实现了ApplicationRunner接口,他们的执行顺序要怎么指定呢?答案是使用@Order(n)注解,n的值越小越先执行。当然也可以通过@Priority注解指定顺序。

7、修改BeanDefinition

Spring IOC在实例化Bean对象之前,需要先读取Bean的相关属性,保存到BeanDefinition对象中,然后通过BeanDefinition对象,实例化Bean对象。如果想修改BeanDefinition对象中的属性,该怎么办呢?我们可以实现BeanFactoryPostProcessor接口

postProcessBeanFactory方法中,可以获取BeanDefinition的相关对象,并且修改该对象的属性

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("id", 123);
        beanDefinitionBuilder.addPropertyValue("name", "shawn");
        defaultListableBeanFactory.registerBeanDefinition("user", beanDefinitionBuilder.getBeanDefinition());
    }
}

8、初始化Bean前后

有时候需要在初始化Bean前后,实现一些自己的逻辑。这时可以实现BeanPostProcessor接口,该接口目前有两个方法:

  • postProcessBeforeInitialization 该在初始化方法之前调用。

  • postProcessAfterInitialization 该方法再初始化方法之后调用。

如果spring中存在User对象,则将它的userName设置成:shawn666。其实,我们经常使用的注解,比如:@Autowired@Value@Resource@PostConstruct等,是通过AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor实现的。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

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

9、初始化方法

9.1 介绍

参考:Spring Bean初始化

spring中使用比较多的初始化bean的方法有:

  • 使用@PostConstruct注解

  • 实现InitializingBean接口

9.2 使用@PostConstruct注解

在需要初始化的方法上增加@PostConstruct注解,这样就有初始化的能力

@Service
public class AService {
    @PostConstruct
    public void init() {
        System.out.println("===初始化===");
    }
}

9.3 实现InitializingBean接口

实现InitializingBean接口,重写afterPropertiesSet方法,该方法中可以完成初始化功能

@Service
public class BService implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("===初始化===");
    }
}

10、关闭容器前

有时候,我们需要在关闭spring容器前,做一些额外的工作,比如:关闭资源文件等。这时可以实现DisposableBean接口,并且重写它的destroy方法:

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

这样spring容器销毁前,会调用该destroy方法,做一些额外的工作。通常情况下,我们会同时实现InitializingBeanDisposableBean接口,重写初始化方法和销毁方法。

11、自定义作用域

我们都知道spring默认支持的Scope只有两种:

  • singleton 单例,每次从spring容器中获取到的bean都是同一个对象。

  • prototype 多例,每次从spring容器中获取到的bean都是不同的对象。

spring web又对Scope进行了扩展,增加了:

  • **RequestScope **同一次请求从spring容器中获取到的bean都是同一个对象。

  • **SessionScope **同一个会话从spring容器中获取到的bean都是同一个对象。

即便如此,有些场景还是无法满足我们的要求。比如,我们想在同一个线程中从spring容器获取到的bean都是同一个对象,该怎么办?这就需要自定义Scope了。

第一步实现Scope接口

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 = 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;
    }
}

第二步将新定义的Scope注入到spring容器中

@Component
public class ThreadLocalBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("threadLocalScope", new ThreadLocalScope());
    }
}

第三步使用新定义的Scope

@Scope("threadLocalScope")
@Service
public class CService {
    public void add() {
    }
}

三、Spring所有扩展接口详解

1、ApplicationContextInitializer

整个spring容器在刷新之前初始化ConfigurableApplicationContext的回调接口,简单来说,就是在容器刷新之前调用此类的initialize方法。这个点允许被用户自己扩展。用户可以在整个spring容器还没被初始化之前做一些事情。可以想到的场景可能为,在最开始激活一些配置,或者利用这时候class还没被类加载器加载的时机,进行动态字节码注入等操作。

public class TestApplicationContextInitializer implements ApplicationContextInitializer {      
    @Override      
    public void initialize(ConfigurableApplicationContext applicationContext) {      
        System.out.println("[ApplicationContextInitializer]");      
    }      
}      

这时候spring容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:

  • 在启动类中用springApplication.addInitializers(new TestApplicationContextInitializer())语句加入

    @SpringBootApplication
    public class SpringextendApplication {
    
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(SpringextendApplication.class);
            springApplication.addInitializers(new TestApplicationContextInitializer());
            springApplication.run(args);
        }
    }
    
  • 配置文件配置context.initializer.classes=com.example.demo.TestApplicationContextInitializer

  • Spring SPI扩展,在spring.factories中加入org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer

2、BeanDefinitionRegistryPostProcessor

这个接口在读取项目中的beanDefinition之后执行,提供一个补充的扩展点。使用场景:可以在这里动态注册自己的beanDefinition,可以加载classpath之外的bean

@Component
public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanDefinitionRegistry");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory");
    }
}

3、BeanFactoryPostProcessor

这个接口是beanFactory的扩展接口,调用时机在spring在读取beanDefinition信息之后,实例化bean之前。在这个时机,用户可以通过实现这个扩展接口来自行处理一些东西,比如修改已经注册的beanDefinition的元信息。

@Component
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanFactoryPostProcessor]");
    }
}

4、InstantiationAwareBeanPostProcessor

该接口继承了BeanPostProcess接口,区别如下:BeanPostProcess接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。

该类主要的扩展点有以下5个方法,主要在bean生命周期的两大阶段:实例化阶段初始化阶段,,按调用顺序为:

  • postProcessBeforeInstantiation:实例化bean之前,相当于new这个bean之前

  • postProcessAfterInstantiation:实例化bean之后,相当于new这个bean之后

  • postProcessPropertyValues:bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现

  • postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前

  • postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后

使用场景:,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。

@Component
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessProperties " + beanName);
        return pvs;
    }

}

5、SmartInstantiationAwareBeanPostProcessor

该扩展接口有3个触发点方法:

  • predictBeanType:该触发点发生在postProcessBeforeInstantiation之前(在图上并没有标明,因为一般不太需要扩展这个点),这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。

  • determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。

  • getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。

@Component
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
        return beanClass;
    }

    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
        return null;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
        return bean;
    }
}

6、BeanFactoryAware

这个类只有一个触发点,发生在bean的实例化之后,注入属性之前,也就是Setter之前。这个类的扩展点方法为setBeanFactory,可以拿到BeanFactory这个属性。使用场景:你可以在bean实例化之后,但还未初始化之前,拿到 BeanFactory,在这个时候,可以对每个bean作特殊化的定制。也或者可以把BeanFactory拿到进行缓存,日后使用

@Component
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanFactoryPostProcessor] " + beanFactory.toString());
        // 下面这个有循环依赖问题
        // System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
    }
}

7、ApplicationContextAwareProcessor

该类本身并没有扩展点,但是该类内部却有6个扩展点可供实现 ,这些类触发的时机在bean实例化之后,初始化之前。该类用于执行各种驱动接口,在bean实例化之后,属性填充之后,通过执行扩展接口,来获取对应容器的变量。所以这里应该来说是有6个扩展点

  • EnvironmentAware:用于获取EnviromentAware的一个扩展类,这个变量非常有用, 可以获得系统内的所有参数。当然个人认为这个Aware没必要去扩展,因为spring内部都可以通过注入的方式来直接获得。

  • EmbeddedValueResolverAware:用于获取StringValueResolver的一个扩展类, StringValueResolver用于获取基于String类型的properties的变量,一般我们都用@Value的方式去获取,如果实现了这个Aware接口,把StringValueResolver缓存起来,通过这个类去获取String类型的变量,效果是一样的。

  • ResourceLoaderAware:用于获取ResourceLoader的一个扩展类,ResourceLoader可以用于获取classpath内所有的资源对象,可以扩展此类来拿到ResourceLoader对象。

  • ApplicationEventPublisherAware:用于获取ApplicationEventPublisher的一个扩展类,ApplicationEventPublisher可以用来发布事件,结合ApplicationListener来共同使用,下文在介绍ApplicationListener时会详细提到。这个对象也可以通过spring注入的方式来获得。

  • MessageSourceAware:用于获取MessageSource的一个扩展类,MessageSource主要用来做国际化。

  • ApplicationContextAware:用来获取ApplicationContext的一个扩展类,ApplicationContext应该是很多人非常熟悉的一个类了,就是spring上下文管理器,可以手动的获取任何在spring上下文注册的bean,我们经常扩展这个接口来缓存spring上下文,包装成静态方法。同时ApplicationContext也实现了BeanFactoryMessageSourceApplicationEventPublisher等接口,也可以用来做相关接口的事情。

8、BeanNameAware

这个类也是Aware扩展的一种,触发点在bean的初始化之前,也就是postProcessBeforeInitialization之前,这个类的触发点方法只有一个:setBeanName。使用场景:用户可以扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值

@Component
public class NormalBeanA implements BeanNameAware {
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("[BeanNameAware] " + name);
    }
}

9、@PostConstruct

这个并不算一个扩展点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。使用场景:用户可以对某一方法进行标注,来进行初始化某一个属性

10、InitializingBean

这个类也是用来初始化bean的。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization之前。使用场景:用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。

11、FactoryBean

Spring通过反射机制利用bean的class属性指定支线类去实例化bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在bean中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式

使用场景:用户可以扩展这个类,来为要实例化的bean作一个代理,比如为该对象的所有的方法作一个拦截,在调用前后输出一行log,模仿ProxyFactoryBean的功能。

@Component
public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> {

    @Override
    public TestFactoryBean.TestFactoryInnerBean getObject() throws Exception {
        System.out.println("[FactoryBean] getObject");
        return new TestFactoryBean.TestFactoryInnerBean();
    }

    @Override
    public Class<?> getObjectType() {
        return TestFactoryBean.TestFactoryInnerBean.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public static class TestFactoryInnerBean{

    }
}

12、SmartInitializingSingleton

这个接口中只有一个方法afterSingletonsInstantiated,其作用是是 在spring容器管理的所有单例对象(非懒加载对象)初始化完成之后调用的回调接口。其触发时机为postProcessAfterInitialization之后。使用场景:用户可以扩展此接口在对所有单例对象初始化完毕后,做一些后置的业务处理。

@Component
public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("[TestSmartInitializingSingleton]");
    }
}

13、CommandLineRunner

这个接口也只有一个方法:run(String... args),触发时机为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以利用@Order来进行排序。使用场景:用户扩展此接口,进行启动项目之后一些业务的预处理。

@Component
public class TestCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("[TestCommandLineRunner]");
    }
}

14、DisposableBean

这个扩展点也只有一个方法:destroy(),其触发时机为当此对象销毁时,会自动执行这个方法。比如说运行applicationContext.registerShutdownHook时,就会触发这个方法。

15、ApplicationListener

准确的说,这个应该不算spring&springboot当中的一个扩展点,ApplicationListener可以监听某个事件的event,触发时机可以穿插在业务方法执行过程中,用户可以自定义某个业务事件。但是spring内部也有一些内置事件,这种事件,可以穿插在启动调用中。我们也可以利用这个特性,来自己做一些内置事件的监听器来达到和前面一些触发点大致相同的事情。

接下来罗列下spring主要的内置事件:

  • ContextRefreshedEvent

    ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在ConfigurableApplicationContext接口中使用 refresh()方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。

  • ContextStartedEvent

    当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。

  • ContextStoppedEvent

    当使用 ConfigurableApplicationContext接口中的 stop()停止ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作

  • ContextClosedEvent

    当使用 ConfigurableApplicationContext接口中的 close()方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启

  • RequestHandledEvent

    这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件


参考文章:

https://mp.weixin.qq.com/s/Bih1XRVLi6ywMtErG_YcgQ

https://www.jianshu.com/p/38d834db7413文章来源地址https://www.toymoban.com/news/detail-408751.html

到了这里,关于Spring常用扩展点的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cache的介绍以及使用方法、常用注解

    Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能. Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。 CacheManager是Spring提供的各种缓存技术抽象接口。 针对不同的缓存

    2024年02月16日
    浏览(37)
  • Rx.NET in Action 中文介绍 前言及序言

    目标 可选方式 Rx 处理器(Operator) 创建 Observable Creating Observables 直接创建 By explicit logic Create Defer 根据范围创建 By specification Range Repeat Generate Timer Interval Return 使用预设 Predefined primitives Throw Never Empty 从其他类型创建 From other types FromEventPattern FromEvent FromTask FromAsync 变换 Transform

    2024年02月13日
    浏览(48)
  • 为了做低代码平台,这些年我们对.NET的DataGridView做的那些扩展

    我们的低代码开发平台从一开始决定做的时候,就追求未来能够支持多种类型的客户端,目前支持Winform,Web,H5,FlutterAPP,当然了,未来也有可能会随着实际的需要淘汰掉一些客户端的。 为了系统更易于维护,减少其对第三方的依赖风险,我们总是坚持着“能自己实现的一

    2024年02月02日
    浏览(48)
  • 常用的缓存工具有ehcache、memcache和redis,这里介绍spring中ehcache的配置。

    常用的缓存工具有ehcache、memcache和redis,这里介绍spring中ehcache的配置。 1.在pom添加依赖: 2.在applicationContext.xml添加命名空间: 3.在applicationContext.xml中配置ehcache: 4.创建ehcache的配置文件ehcache-setting.xml: 这里我们配置了自定义缓存cacheTest,10秒过期。 cache元素的属性: name:缓

    2024年02月16日
    浏览(42)
  • 公链扩展路线之争:道阻且长的过程中Ardor 3.0能为我们带来什么?

    当我们讨论公链扩容的时候,肯定绕不过目前最火热的以太坊2.0路线,简单来讲以太坊2.0路线就是从POW共识转向POS共识的过程,但由于以太坊的设计架构过于复杂,目前从以太坊基金会的官网来看,他们抛弃Eth1和Eth2的命名,并正式提出改名为执行层和共识层,一方面可以帮

    2024年02月02日
    浏览(35)
  • 当我们谈论Spring的时候到底在谈什么

    你好,这里是codetrend专栏“Spring6全攻略”。欢迎点击关注查看往期文章。 Spring 对于不做程序开发的人来说字面意思就是春天,四季的开始。 对于程序员来说这个单词完全拥有另外一个含义, Spring 指的是一个开源项目,而这个项目非常厉害。 Spring 这个术语在不同的语境中

    2024年03月22日
    浏览(45)
  • 今天我们来说说常用的三种排序算法:选择排序、插入排序、快速排序

    原文链接:http://www.ibearzmblog.com/#/technology/info?id=8ac4902f82f525e1456624d5d7a545dc 选择排序、插入排序、快速排序这三种算法算是比较初级的排序算法,对它们的原理和技巧,可以方便我们对后面的算法理解。 温馨提示,因为动图不好弄,所以我在网上下载了AlgorithmMan来进行动图演示

    2024年02月16日
    浏览(39)
  • springboot常用扩展点

    当涉及到Spring Boot的扩展和自定义时,Spring Boot提供了一些扩展点,使开发人员可以根据自己的需求轻松地扩展和定制Spring Boot的行为。本篇博客将介绍几个常用的Spring Boot扩展点,并提供相应的代码示例。 1. 自定义Starter(面试常问) Spring Boot通过Starter来提供自动配置和依赖管理

    2024年01月19日
    浏览(31)
  • GNU C 语言的常用扩展

    本文摘自《奔跑吧 Linux 内核(第二版)》2.2 Linux 内核中常用的 C 语言技巧 读到这本书的这一小节时,感觉其中不少技巧在项目中有遇到过,有必要做个笔记,方便以后查阅。 GCC 的 C 编译器除了支持 ANSI C 标准之外,还对 C 语言进行了很多的扩充。这些扩充为代码优化、目标

    2024年02月05日
    浏览(25)
  • Unity常用函数扩展(一)—— GetOrAddComponent()

    实现获取Transform或者GameObject上的组件,若获取不到则自动添加该组件

    2024年02月14日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包