# SpringBoot 常用扩展点介绍、容器启动源码分析

SpringBoot 常用扩展点介绍、容器启动源码分析

SpringApplication.run()

实例化一个SpringApplication

  • 创建一个SpringApplication实例
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}
  • SpringApplication构造方法:属性赋值
// 收集所有的容器初始化组件对象:解析 spring.factories 文件获取容器自定义组件
private List<ApplicationContextInitializer<?>> initializers;

// 收集所有的监听器:扩展方式和上面的初始化组件方式差不多
private List<ApplicationListener<?>> listeners;

# SpringBoot 常用扩展点介绍、容器启动源码分析_第1张图片

总结

  • SpringApplication的构建都是为了run()方法启动做铺垫,构造方法中总共就有几行代码,最重要的部分就是设置应用类型、设置初始化器、设置监听器。

spring-boot自定义容器初始化组件 Demo

定义组件
  • 实现ApplicationContextInitializer
public class CustomInitializer implements ApplicationContextInitializer {

    private static final Logger logger = LoggerFactory.getLogger(CustomInitializer.class);

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        logger.info("====================CustomInitializer start======================");
        logger.info("CustomInitializer initialize方法被执行...");
        logger.info("ApplicationName: {}", applicationContext.getApplicationName());
        logger.info("isActive: {}", applicationContext.isActive());
        logger.info("====================CustomInitializer end======================");
    }
}
配置容器组件
  • SpringBootSPI扩展 META-INF下面 spring.factories配置自定义组件的位置
org.springframework.context.ApplicationContextInitializer=com.li.springbootproject.spring.initializer.CustomInitializer
  • add方式添加
@SpringBootApplication
public class MySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MySpringBootApplication.class);
        application.addInitializers(new MyApplicationContextInitializer());
        application.run(args);
    }
}

总结

  • ApplicationContextInitializerSpring 对外提供的扩展点之一,用于在 ApplicationContext 容器加载 Bean 之前对当前的上下文进行配置。
  • 重要的还是如何将这一组件和我们的具体业务相结合,实现我们具体的业务,这应该是需要思考的。

调用对象的 Run()

Run() 方法流程

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
    	// 获取运行监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
    	// 启动运行监听器
		listeners.starting();
		try 
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 环境构建这一步加载了系统环境配置、用户自定义配置并且广播了ApplicationEnvironmentPreparedEvent事件,触发监听器。
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
            // 打印 Banner
			Banner printedBanner = printBanner(environment);
            // 依据是否为 web 环境创建 web 容器或者普通的 IOC 容器(只是创建) 见下面截图
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            // IOC 容器的前置处理,为刷新容器之前做准备,关键操作(将启动类注入容器)
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 刷新容器,完成组件的扫描,创建,加载等
            refreshContext(context);
    		//  IOC容器的后置处理
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
  • 创建 BeanFactory
    # SpringBoot 常用扩展点介绍、容器启动源码分析_第2张图片
  • 初始化完成之后就进到了run方法,run方法完成了所有Spring的整个启动过程:准备Environment——发布事件——创建上下文、bean——刷新上下文——结束
    # SpringBoot 常用扩展点介绍、容器启动源码分析_第3张图片
  • 对比 ApplicationContext,ConfigurableApplicationContext提供了配置上下文的接口,如设置Environment、监听器、切面类、关闭上下文的钩子等。

容器启动过程中的 refreshContext()

源码refresh()

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        // 告诉子类去刷新bean工厂,这步完成后配置文件就解析成一个个bean定义,注册到BeanFactory(但是未被初始化,仅将信息写到了beanDefination的map中)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        try {
             // 处理自定义的BeanFactoryPostProcess:允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据)
            postProcessBeanFactory(beanFactory);
            //  调用BeanFactoryPostProcessor各个实现类的方法
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 注册 BeanPostProcessor 的实现类
            // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         	// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。
            registerBeanPostProcessors(beanFactory);
            
            // 国际化
            initMessageSource();
            
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            
            // 实例化所有剩余的非懒加载单例 bean
            finishBeanFactoryInitialization(beanFactory);
            
            finishRefresh();
        }catch(Exception e){
            // ...
        }
    }
}

SpringBoot 容器 启动流程

# SpringBoot 常用扩展点介绍、容器启动源码分析_第4张图片

常用技巧

初始化类的属性

@PostConstruct

初始化类变量
  • 首先这个注解是由Java提供的,它用来修饰一个非静态的void方法。它会在服务器加载Servlet的时候运行,并且只运行一次。

  • 它的作用在于声明一个Bean对象初始化完成后执行的方法。

  • Bean初始化中的执行顺序:

    Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

@Component
public class InitStaticVariableThree {

    private static final Logger logger = LoggerFactory.getLogger(InitStaticVariableThree.class);

    /**
     * 静态成员变量
     */
    public static RedissonClient redissonClientOne;

    /**
     * 注入 redissonClient
     */
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 使用 PostConstruct 给静态成员变量赋值
     */
    @PostConstruct
    public void init() {
        redissonClientOne = redissonClient;
        logger.info(String.valueOf(redissonClient));
        logger.info(String.valueOf(redissonClientOne));
    }

}

注册 Bean

  • 除了注解、Java配置和XML配置的方式来创建Bean,还有另外一种方式来创建我们的BeanDefinition。通过 BeanDefinitionRegistryPostProcessor可以创建一个特别的后置处理器,来将BeanDefinition添加到BeanDefinitionRegistry中。
  • BeanDefinition 是对 Bean 的定义,其保存了 Bean 的各种信息,如属性、构造方法参数、是否单例、是否延迟加载等。这里的注册 Bean 是指将 Bean 定义成 BeanDefinition,之后放入 容器中

BeanDefinitionRegistryPostProcessor

  • BeanPostProcessor不同,BeanPostProcessor只是在Bean初始化的时候有个钩子让我们加入一些自定义操作;而BeanDefinitionRegistryPostProcessor可以让我们在BeanDefinition中添加一些自定义操作。这就跟类与类实例之间的区别类似。
执行时机
  • Bean定义没有被加载,bean实例还没有被初始化时候。
使用
  • 注册PersonBean:MyBeanDefinitionRegistryPostProcessor.java
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private static final Logger logger = LoggerFactory.getLogger(MyBeanDefinitionRegistryPostProcessor.class);

    /**
     * 注册 Bean
     *
     * @param registry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "MyBeanDefinitionRegistryPostProcessor 中的 postProcessBeanDefinitionRegistry 方法");
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "bean 定义的数据量:" + registry.getBeanDefinitionCount());
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(PersonBean.class);
        registry.registerBeanDefinition("personbean", rootBeanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "MyBeanDefinitionRegisterPostProcessor中的postProcessBeanFactory方法");
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "bean 定义的数据量:" + beanFactory.getBeanDefinitionCount());
    }
}
  • 使用PersonBean:直接注入使用

实现 Bean 的初始化

实现InitializingBean

  • 定义 Bean 实现 InitializingBean
public class InitializingBeanExample implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(InitializingBeanExample.class);

    private int id;

    private String name;

    @Override
    public void afterPropertiesSet() {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + " 初始化前的值为:{}", this);
        logger.info(LogConst.LOG_SUCCESS_PREFIX + " 我要对 PeopleBean 进行初始化!");
        this.id = 100;
        this.name = "李四";
        logger.info(LogConst.LOG_SUCCESS_PREFIX + " 初始化后的值为:{}", this);
    }
}
  • 注册 Bean
@Configuration
public class InitializingBeanExampleTest {

    private static final Logger logger = LoggerFactory.getLogger(InitializingBeanExampleTest.class);

    @Bean
    public InitializingBeanExample initializingBeanExample() {
        InitializingBeanExample initializingBeanExample = new InitializingBeanExample();
        initializingBeanExample.setId(1);
        initializingBeanExample.setName("test1");
        logger.info(LogConst.LOG_SUCCESS_PREFIX + initializingBeanExample);
        return initializingBeanExample;
    }
}

使用initMethod方法

  • 定义 Bean
@Bean(initMethod = "initBean")
@Lazy
public InitMethodBean initMethodBean() {
    InitMethodBean initMethodBean = new InitMethodBean();
    logger.info(LogConst.LOG_SUCCESS_PREFIX + "实例化 InitMethodBean 信息为:" + initMethodBean);
    return initMethodBean;
}
  • 定义 Beaninti-method 方法
public class InitMethodBean {

    private static final Logger logger = LoggerFactory.getLogger(InitMethodBean.class);

    private UUID id;

    private String name;

    private void initBean() {
        this.id = UUID.randomUUID();
        this.name = "InitMethodBean";
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "执行 InitMethodBean 的 initBean():" + this);
    }
    
    // ...
}

@PostConstruct

  • 看前面的讲解

总结

  • 初始化 Bean 的顺序

Constructor > @PostConstruct > InitializingBean > init-method


获取 Bean 修改 Bean 信息

实现 BeanPostProcessor 接口

实现BeanPostProcessor 接口,重写postProcessBeforeInitialization()postProcessAfterInitialization(),在重写的方法中,可以获得Bean的属性,对Bean进行相关的操作。

@Component
public class UserBeanPostProcessor implements BeanPostProcessor {

    private static final Logger logger = Logger.getLogger(String.valueOf(UserBeanPostProcessor.class));

	// 在这里可以拿到bean的相关信息,进行相关操作
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        final String name = "myUser";
        if(name.equals(beanName)){
            User user = (User) bean;
            user.setName("李四");
            user.setDate(new Date());
            logger.info("=====> postProcessBeforeInitialization():"+ JSONObject.toJSONString(user));
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
执行时机
  • postProcessBeforeInitialization() 方法:在 Bean 实例化、属性注入后,初始化前调用。
  • postProcessAfterInitialization() 方法:在 Bean 实例化、属性注入、初始化都完成后调用。

SpringBoot 启动后执行操作

  • springboot给我们提供了两种方式:ApplicationRunnerCommandLineRunner

  • 这两种方法提供的目的是为了满足,在项目启动的时候立刻执行某些方法。我们可以通过实现ApplicationRunnerCommandLineRunner,来实现,他们都是在SpringApplication 执行之后开始执行的。

Order 注解使用

  • Spring容器启动后可以加载一些资源或者做一些业务操作
  • 实现 CommandLineRunner
@Component
@Order(1)
public class OrderTestOne implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(OrderTestOne.class);

    /**
     * 执行
     *
     * @param args 参数
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        logger.info("OrderTestOne...");
    }
}
  • order的值越小,优先级越高
  • order如果不标注数字,默认最低优先级,因为其默认值是int最大值

实现 ApplicationRunner、Ordered 接口

@Component
public class OrderTestTwo implements ApplicationRunner, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(OrderTestTwo.class);

    /**
     * 执行
     *
     * @param args 参数
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("OrderTestTwo...");
    }

    /**
     * 定义执行的顺序
     *
     * @return int
     */
    @Override
    public int getOrder() {
        return 2;
    }
}

获取 ApplicationContext 容器

直接注入

  • 直接在使用的地方注入使用
@Autowired
private ApplicationContext applicationContextBean;

实现ApplicationContextAware接口

  • 实例代码SpringContextUtils,定义全局的获取上下文的工具类
@Component
public class SpringContextUtils implements ApplicationContextAware {
    
    private static final Logger logger = LoggerFactory.getLogger(SpringContextUtils.class);

    /**
     * 上下文对象实例
     */
    private static ApplicationContext applicationContext = null;


    private SpringContextUtils() {
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (Objects.isNull(SpringContextUtils.applicationContext)) {
            logger.info(LogConst.LOG_SUCCESS_PREFIX + "ApplicationUtils初始化...");
            SpringContextUtils.applicationContext = applicationContext;
        }
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "ApplicationUtils初始化成功!");
    }

    /**
     * 获得当前的ApplicationContext
     *
     * @return ApplicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 根据名称拿到 Bean
     *
     * @param name Bean 的名称
     * @return Object
     */
    @SuppressWarnings("all")
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 从ApplicationContext中获得Bean并且转型
     *
     * @param tClass 类
     * @param     T
     * @return T
     */
    public static <T> T getBean(Class<T> tClass) {
        return getApplicationContext().getBean(tClass);
    }
}

作为参数获取 ApplicationContext Bean

  • Spring在初始化AutoConfiguration时会自动传入ApplicationContext,这时我们就可以使用下面的方式来获取ApplicationContext
@Configuration
public class TestConfig {

    private static final Logger logger = LoggerFactory.getLogger(TestConfig.class);

    /**
     * 作为参数获取 Bean
     *
     * @param applicationContext applicationContext
     */
    public TestConfig(ApplicationContext applicationContext) {
        logger.info(String.valueOf(applicationContext));
    }
}

通过WebApplicationContextUtils获取

  • Spring提供了一个工具类用于获取ApplicationContext对象:
@Autowired
private ServletContext servletContext;

@Override
public String testSix() {
    WebApplicationContext context1 = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
    WebApplicationContext context2 = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    return "测试成功!";
}

获取BeanFactory

Spring中的Aware

Aware这个类主要是辅助Spring进行访问容器中的数据,在实现这个类重写的方法中,可以拿到相关组件的信息,从而可以进行相应的扩展。

常用的Aware实现类
  • BeanFactoryAware:获取BeanFactory容器
  • BeanNameAware:获取Bean的名称
  • ApplicationContextAware:获取ApplicationContext

得到 BeanFactory

实现BeanFactoryAware,在setXXX()中获得相关的Spring组件。

@Component
public class GetBeanFactory implements BeanFactoryAware {

    private static final Logger logger = LoggerFactory.getLogger(GetBeanFactory.class);

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        MyBeans bean = beanFactory.getBean(MyBeans.class);
        logger.info("-----> 获得当前的BeanFactory "+ beanFactory);
    }

}

FactoryBean、BeanFactoryPostProcessor 扩展

  • MybatisMapper

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHUfeeFt-1647340618646)(images/image-20220313111913059.png)]

  • BeanFactoryPostProcessor是实现spring容器功能扩展的重要接口,例如修改bean属性值,实现bean动态代理等。

  • FactoryBean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。

实现汽车装配引擎的过程

  • BeanProduct
@Configuration
public class BeanProduct {

    /**
     * 定义引擎 Bean
     *
     * @return SpecialBeanForEngine
     */
    @Bean
    public SpecialBeanForEngine specialBeanForEngine() {
        return new SpecialBeanForEngine();
    }

    /**
     * 定义汽车 Bean 依赖引擎 Bean : 实现 Car 装配 引擎的过程
     *
     * @param carEngine 汽车引擎
     * @return Car
     */
    @Bean(initMethod = "start")
    public Car car(CarEngine carEngine) {
        Car car = new Car();
        car.setEngine(carEngine);
        return car;
    }

}
  • 注册 EngineFactoryBeanDefine
public class SpecialBeanForEngine implements BeanFactoryPostProcessor, BeanNameAware {

    public String name;

    @Override
    public void setBeanName(String name) {
        this.name = name;
    }

    public class SpecialBeanForEngine implements BeanFactoryPostProcessor, BeanNameAware {

    public String name;

    @Override
    public void setBeanName(String name) {
        this.name = name;
    }

    /**
     * 注册 EngineFactory 的 BeanDefine
     *
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(EngineFactory.class);
        genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        genericBeanDefinition.setAutowireCandidate(true);
        beanDefinitionRegistry.registerBeanDefinition("engine007", genericBeanDefinition);
    }
}
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(EngineFactory.class);
        genericBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        genericBeanDefinition.setAutowireCandidate(true);
        beanDefinitionRegistry.registerBeanDefinition("engine007",genericBeanDefinition);
    }
}
  • 汽车 Bean 的配置
public class Car implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(Car.class);

    private String code = String.valueOf(UUID.randomUUID());

    /**
     * 引擎
     */
    private CarEngine engine;

    public Car() {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "Car Constructor...");
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "Car engine not setting");
    }

    private void start() {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "Car start...");
        engine.fire();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        logger.info("Car initializingBean after propertieSet");
        if (engine == null) {
            logger.info("Car's engine not setting, in initializingBean ");
        } else {
            logger.info("Car's engine installed, in initializingBean");
            // 使用接口方法
            engine.fire();
        }
    }

    @PostConstruct
    public void postConstruct() {
        logger.info("Car postConstruct");
        if (engine == null) {
            logger.info("Car engine not setting, in postConstruct");
        } else {
            logger.info("Car engine installed, in postConstruct");
        }
    }
}
  • 需要代理的接口
public interface CarEngine {

    void fire();

    void close();
}

  • 工厂Bean EngineFactory
public class EngineFactory implements FactoryBean<CarEngine>, BeanNameAware, InvocationHandler {

    private static final Logger logger = LoggerFactory.getLogger(EngineFactory.class);

    private String name;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("fire".equalsIgnoreCase(method.getName())) {
            logger.info(LogConst.LOG_SUCCESS_PREFIX + "here is invoke  engine:" + method.getName());
        }
        if ("close".equalsIgnoreCase(method.getName())) {
            logger.info(LogConst.LOG_SUCCESS_PREFIX + "here is invoke  engine:" + method.getName());
        }
        return null;
    }

    @Override
    public void setBeanName(String name) {
        this.name = name;
    }

    /**
     * 获取引擎 CarEngine 的代理对象
     *
     * @return
     */
    @Override
    public CarEngine getObject() {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "EngineFactory  to build Engine01 , EngineFactory :" + name);
        CarEngine carEngine = (CarEngine) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{CarEngine.class}, this);
        return carEngine;
    }

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

    /**
     * 是否是单例 Bean
     *
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}
  • 测试代码

@Autowired
private CarEngine carEngine;

carEngine.fire();
carEngine.close();
  • 执行结果
2022-03-12 20:04:25.508  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car Constructor...
2022-03-12 20:04:25.508  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car engine not setting
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car postConstruct
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car engine installed, in postConstruct
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car initializingBean after propertieSet
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car engine installed, in initializingBean
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.b.EngineFactory                  : ==========> here is invoke  engine:fire
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.beanfactorypostprocessor.Car     : ==========> Car start...
2022-03-12 20:04:25.509  INFO 12836 --- [           main] c.l.s.s.b.EngineFactory                  : ==========> here is invoke  engine:fire

SpringBoot Event事件同步、异步处理

  • 场景:按照一定的顺序做一些事情,例如向A表插入数据事物提交之后,向B表中插入历史记录,最后向C表插入。

Spring 事件

Spring 中,初始化容器时会调用 org.springframework.context.ConfigurableApplicationContext 接口中的 reFresh() 方法进行 Bean的加载,该方法会进行事件的监听注册。

示例代码

  • 定义事件
public class UserEvent<T> extends ApplicationEvent {

    private T data;

    public UserEvent(T source) {
        super(source);
        this.data =source;
    }

    public T getData() {
        return this.data;
    }

    public void setData(final T data) {
        this.data = data;
    }
}
  • 发布事件
@Autowired
private ApplicationEventPublisher publisher;

/**
  * 发布插入user表的事件
  */
private void sendInsertUser() {
    User user = new User();
    user.setId(20210317);
    user.setUsername("测试张三");
    UserEvent<User> userEvent = new UserEvent<>(user);
    // 发布事件
    publisher.publishEvent(userEvent);

}
  • 监听用户插入的事件
/**
  * 监听插入User的事件 : 在上个事务提交之后在执行
  */
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,fallbackExecution = true)
public void insertUserLinster(UserEvent<User> userEvent) {
    User data = userEvent.getData();
    String message = String.format("get user message: %s", JSON.toJSONString(data));
    log.info(message);
    // 后续操作;继续处理
}

SpringBoot 关机函数

  • 实现 ExitCodeGenerator 接口,生成自己的退出代码
@Component
public class ExitCodeApplication implements ExitCodeGenerator {

    private static final Logger logger = LoggerFactory.getLogger(ExitCodeApplication.class);

    @Override
    public int getExitCode() {
        logger.info(LogConst.LOG_SUCCESS_PREFIX + "容器已经关机...");
        return 999;
    }
}
  • 调用
@Autowired
private ApplicationContext applicationContext;

@RequestMapping("/close")
public void testFive(HttpServletRequest request, HttpServletResponse response) {
    SpringApplication.exit(applicationContext);
}

SpringBoot starter机制

  • SpringBoot 在启动时会去依赖的 starter 包中寻找 /META-INF/spring.factories 文件,然后根据文件中配置的 Jar 包去扫描项目所依赖的 Jar 包,这类似于 JavaSPI 机制。

  • SpringBoot中的starter是一种非常重要的机制(自动化配置),能够抛弃以前繁杂的配置,将其统一集成进starter

# SpringBoot 常用扩展点介绍、容器启动源码分析_第5张图片

  • 生成配置文件
@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {

    @Autowired
    private StarterServiceProperties properties;

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "example.service", value = "enabled", havingValue = "true")
    public StarterService starterService() {
        return new StarterService(properties.getConfig());
    }
}
  • resourcesMETA-INFspring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.li.StarterAutoConfigure
  • 引入依赖
<dependency>
    <groupId>com.ligroupId>
    <artifactId>springboot-starter-demoartifactId>
    <version>1.0-SNAPSHOTversion>
dependency>

代码地址

  • https://gitee.com/Marlon_Brando/back/tree/master/springboot-project

你可能感兴趣的:(SpingBoot,spring,boot,SpringBoot,容器启动,SpringBoot,扩展点,容器启动)