Springboot/Spring 扩展点(一): Bean生命

2023-07-15  本文已影响0人  雪飘千里

在Java开源框架中,尤其是基于Spring/Springboot的开源框架,需要通过Spring/Springboot的扩展点来整合,这篇文章就是整理一下那些Bean的生命周期内所有的扩展点以及在框架中的应用。

Spring bean生命周期

Spring 容器中Bean的生命周期内所有可扩展的点的调用顺序

image.png

spring容器启动可以简单的分为三个阶段:(1)容器初始化阶段、(2)Bean实例化(instantiation)阶段、(3)Bean初始化(initialization)阶段。

1、ApplicationContextInitializer

在SpringBoot启动过程,会回调这个类的实现initialize方法。

自定义实现见Spring Boot启动流程

2、BeanDefinitionRegistryPostProcessor

扩展方式为:

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

BeanFactoryPostProcessor是对BeanFactory的扩展接口,入参是Spring的容器,通过这个接口,就对容器进行为所欲为的操作。

扩展方式为:

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

4、InstantiationAwareBeanPostProcessor(BeanPostProcessor)

该接口继承了BeanPostProcess接口,区别如下:

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

实例化阶段

初始化阶段

postProcessBeforeInitialization 和 postProcessAfterInitialization 是BeanPostProcess的接口;

扩展方式为:

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 PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
        return pvs;
    }

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

4.1 BeanPostProcessor补充

中文名 Bean的后置处理器,在Bean创建的过程中起作用。
BeanPostProcessor是Bean在创建过程中一个非常重要的扩展点,因为每个Bean在创建的各个阶段,都会回调BeanPostProcessor及其子接口的方法,传入正在创建的Bean对象,这样如果想对Bean创建过程中某个阶段进行自定义扩展,那么就可以自定义BeanPostProcessor来完成。

说得简单点,BeanPostProcessor就是在Bean创建过程中留的口子,通过这个口子可以对正在创建的Bean进行扩展。只不过Bean创建的阶段比较多,然后BeanPostProcessor接口以及他的子接口InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor就提供了很多方法,可以使得在不同的阶段都可以拿到正在创建的Bean进行扩展。

现在需要实现一个这样的需求,如果Bean的类型是User,那么就设置这个对象的username属性为 ”三友的java日记“。
那么就可以这么写:

public class UserBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof User) {
            //如果当前的Bean的类型是 User ,就把这个对象 username 的属性赋值为 TestName
            ((User) bean).setUsername("TestName");
        }

        return bean;
    }

}

测试:

public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //将 UserBeanPostProcessor 和  User 注册到容器中
        applicationContext.register(UserBeanPostProcessor.class);
        applicationContext.register(User.class);
        applicationContext.refresh();

        User user = applicationContext.getBean(User.class);
        System.out.println("获取到的Bean为" + user + ",属性username值为:" + user.getUsername());
    }

}

测试结果:
获取到的Bean为com.sanyou.spring.extension.User,属性username值为:TestName

从结果可以看出,每个生成的Bean在执行到某个阶段的时候,都会回调UserBeanPostProcessor,然后UserBeanPostProcessor就会判断当前创建的Bean的类型,如果是User类型,那么就会将username的属性设置为 ”TestName“。

4.2 Spring内置的BeanPostProcessor

Spring Bean的很多注解的处理都是依靠BeanPostProcessor及其子类的实现来完成的,比如@Autowired、@PostConstruct、@PreDestroy注解是通过BeanPostProcessor,在Bean的不同阶段来调用对应的方法起作用的

5、Aware接口

Aware接口是指以Aware结尾的一些Spring提供的接口,当你的Bean实现了这些接口的话,在创建过程中会回调对应的set方法,并传入响应的对象。

image.png

红色部分发生在Bean的创建过程,灰色部分发生在Bean销毁的过程中,在容器关闭的时候,就会销毁Bean。

这里列举几个Aware接口以及它们的作用

5.1 BeanFactoryAware

扩展方式为:

public class TestBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
    }
}

5.2 ApplicationContextAwareProcessor

该类本身并没有扩展点,但是该类内部却有6个扩展点可供实现 ,这些类触发的时机在bean实例化之后,初始化之前

image.png

5.3 BeanNameAware

扩展方式为:

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

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

6、@PostConstruct

这个并不算一个扩展点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。

扩展方式为:

public class NormalBeanA {
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }

    @PostConstruct
    public void init(){
        System.out.println("[PostConstruct] NormalBeanA");
    }
}

7、InitializingBean

是用来初始化bean的。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

扩展方式为:

public class NormalBeanA implements InitializingBean{
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[InitializingBean] NormalBeanA");
    }
}

8、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>的形式;

FactoryBean就是一种Bean的类型。当往容器中注入class类型为FactoryBean的类型的时候,最终生成的Bean是通过FactoryBean的getObject获取的。

image.png

扩展方式为:

ublic class UserFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        User user = new User();
        System.out.println("调用 UserFactoryBean 的 getObject 方法生成 Bean:" + user);
        return user;
    }

    @Override
    public Class<?> getObjectType() {
        // 这个 FactoryBean 返回的Bean的类型
        return User.class;
    }

}

public class Application {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //将 UserFactoryBean 注册到容器中
        applicationContext.register(UserFactoryBean.class);
        applicationContext.refresh();

        System.out.println("获取到的Bean为" + applicationContext.getBean(User.class));
    }

}


调用 UserFactoryBean 的 getObject 方法生成 Bean:com.sanyou.spring.extension.User@396e2f39
获取到的Bean为com.sanyou.spring.extension.User@396e2f39

从结果可以看出,明明注册到Spring容器的是UserFactoryBean,但是却能从容器中获取到User类型的Bean,User这个Bean就是通过UserFactoryBean的getObject方法返回的。

FactoryBean在开源框架中的使用

1、 在Mybatis中的使用

Mybatis在整合Spring的时候,就是通过FactoryBean来实现的,这也就是为什么在Spring的Bean中可以注入Mybatis的Mapper接口的动态代理对象的原因。
代码如下,省略了不重要的代码。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  
  // mapper的接口类型
  private Class<T> mapperInterface;
 
  @Override
  public T getObject() throws Exception {
    // 通过SqlSession获取接口的动态搭理对象
    return getSqlSession().getMapper(this.mapperInterface);
  }
  
  @Override
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }
  
}

getObject方法的实现就是返回通过SqlSession获取到的Mapper接口的动态代理对象。
而@MapperScan注解的作用就是将每个接口对应的MapperFactoryBean注册到Spring容器的。

2、在OpenFeign中的使用

FeignClient接口的动态代理也是通过FactoryBean注入到Spring中的。

public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    
    // FeignClient接口类型
    private Class<?> type;
    
    @Override
   public Object getObject() throws Exception {
      return getTarget();
   }
    
    @Override
   public Class<?> getObjectType() {
      return type;
   }
}

getObject方法是调用getTarget方法来返回的动态代理。
@EnableFeignClients注解的作用就是将每个接口对应的FeignClientFactoryBean注入到Spring容器的。

一般来说,FactoryBean 比较适合那种复杂Bean的构建,在其他框架整合Spring的时候用的比较多。

9、SmartInitializingSingleton

扩展方式为:

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

10、CommandLineRunner

扩展方式见 Spring Boot启动流程

11、DisposableBean

这个扩展点只有一个方法:destroy()。

扩展方式为:

public class NormalBeanA implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("[DisposableBean] NormalBeanA");
    }
}
上一篇 下一篇

猜你喜欢

热点阅读