# SpringBoot 常用扩展点介绍容器启动源码分析
Posted MarlonBrando1998
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# 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;
总结
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======================");
配置容器组件
SpringBoot
的SPI
扩展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);
总结
ApplicationContextInitializer
是Spring
对外提供的扩展点之一,用于在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
- 初始化完成之后就进到了
run
方法,run
方法完成了所有Spring
的整个启动过程:准备Environment
——发布事件——创建上下文、bean
——刷新上下文——结束
- 对比
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 容器 启动流程
常用技巧
初始化类的属性
@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;
- 定义
Bean
的inti-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
给我们提供了两种方式:ApplicationRunner
和CommandLineRunner
。 -
这两种方法提供的目的是为了满足,在项目启动的时候立刻执行某些方法。我们可以通过实现
ApplicationRunner
和CommandLineRunner
,来实现,他们都是在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> 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);
/**
* 作为参数获取以上是关于# SpringBoot 常用扩展点介绍容器启动源码分析的主要内容,如果未能解决你的问题,请参考以下文章
掌握这些 Spring Boot 启动扩展点,已经超过 90% 的人了