SpringBoot 原理解析
# 需求分析
我们有时候需要让项目在启动时提前加载相应的数据或者执行某个方法,比如初始化数据、加载数据字典到缓存当中、显示banner等等
# 1、Profile功能
为了方便多环境适配,springboot简化了profile功能。
# 1、application-profile功能
默认配置文件 application.yaml;任何时候都会加载
指定环境配置文件 application-{env}.yaml
激活指定环境
配置文件激活
命令行激活:java -jar xxx.jar --spring.profiles.active=prod --person.name=haha
修改配置文件的任意值,命令行优先
默认配置与环境配置同时生效
同名配置项,profile配置优先
# 2、@Profile条件装配功能
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
2
3
4
5
6
7
# 3、profile分组
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
使用:--spring.profiles.active=production 激活
2
3
4
# SpringBoot 原理解析
1.在命令行中传入的参数
2.SPRING_APPLICATION_JSON中的属性。SPRING_APPLICATION_JSON是以JSON格式配置在系统环境变量中。
3.java:comp/env 中的JNDI属性。
4.java的系统属性,可以通过
System.getProperties()
获得的内容。5.操作系统的环境变量
6.通过
random.*
配置的随机属性7.位于当前应用jar包之外,针对不同{profile}环境的配置文件内容,例如
application-{profile}.properties
或者是YAML定义的配置文件。8.位于当前应用jar包之内,针对不同{profile}环境的配置文件内容,例如application-{profile}.properties或者是YAML定义的配置文件。
9.位于当前应用jar包之外的
application.properties
和YAML配置内容。10.位于当前应用jar包之内的application.properties和YAML配置内容。
11.在@Configuration注解修改的类中,通过
@PropertySource
注解定义的属性内容。12.应用默认属性,使用
SpringApplication.setDefaultProperties
定义的内容。
优先级按上面的顺序由高到低,数字越小优先级越高。
参考文档:
# SpringBoot启动过程
创建 SpringApplication
保存一些信息。
判定当前应用的类型。ClassUtils。Servlet
bootstrappers**:初始启动引导器(List
):去spring.factories文件中找** org.springframework.boot.Bootstrapper 找 ApplicationContextInitializer;去spring.factories****找 ApplicationContextInitializer
List<ApplicationContextInitializer<?>> initializers
找 ApplicationListener ;应用监听器。去spring.factories****找 ApplicationListener
List<ApplicationListener<?>> listeners
运行 SpringApplication
StopWatch
记录应用的启动时间
**创建引导上下文(Context环境)**createBootstrapContext()
获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
让当前应用进入headless模式。java.awt.headless
获取所有 RunListener**(运行监听器)【为了方便所有Listener进行事件感知】**
getSpringFactoriesInstances 去spring.factories****找 SpringApplicationRunListener.
遍历 SpringApplicationRunListener 调用 starting 方法;
相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
保存命令行参数;ApplicationArguments
准备环境 prepareEnvironment();
返回或者创建基础环境信息对象。StandardServletEnvironment
配置环境信息对象。
读取所有的配置源的配置属性值。
绑定环境信息
监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
创建IOC容器(createApplicationContext())
根据项目类型(Servlet)创建容器,
当前会创建 AnnotationConfigServletWebServerApplicationContext
准备ApplicationContext IOC容器的基本信息 prepareContext()
保存环境信息
IOC容器的后置处理流程。
应用初始化器;applyInitializers;
遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器****contextPrepared
所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded;
**刷新IOC容器。**refreshContext
创建容器中的所有组件(Spring注解)
容器刷新完成后工作?afterRefresh
所有监听 器 调用 listeners.started(context); 通知所有的监听器 started
**调用所有runners;**callRunners()
获取容器中的 ApplicationRunner
获取容器中的 CommandLineRunner
合并所有runner并且按照@Order进行排序
遍历所有的runner。调用 run 方法
如果以上有异常,
调用Listener 的 failed
调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
**running如果有问题。继续通知 failed 。**调用所有 Listener 的 **failed;**通知所有的监听器 failed
# 2、Application Events and Listeners
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-application-events-and-listeners
ApplicationContextInitializer
ApplicationListener
SpringApplicationRunListener
# 3、ApplicationRunner 与 CommandLineRunner
# springboot实现方式
# 1.实现ServletContextAware接口
注意:该方法会在填充完普通Bean的属性,但是还没有进行Bean的初始化之前执行
Aware的意思是感知 ApplicationContextAware可以获得 ApplicationContext上下文资源 ServletContextAware可以获得ServletContext上下文资源 … 实现xxAware接口的子类,会覆盖对应的setXX()方法,从而可以获得Spring容器中的xx资源
/**
* spring工具类 方便在非spring管理环境中获取bean
* @author ruoyi
*/
@Component
public class SpringUtils implements ServletContextAware,ApplicationContextAware,BeanFactoryPostProcessor {
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置,无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles()
{
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile()
{
final String[] activeProfiles = getActiveProfiles();
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
/**
* 在填充普通bean属性之后但在初始化之前调用
* 类似于initializingbean的afterpropertiesset或自定义init方法的回调
*
*/
@Override
public void setServletContext(ServletContext servletContext) {
System.out.println("setServletContext方法");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# 2.实现ServletContextListener接口
/**
* 在初始化Web应用程序中的任何过滤器或servlet之前,将通知所有servletContextListener上下文初始化。
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
//ServletContext servletContext = sce.getServletContext();
System.out.println("执行contextInitialized方法");
}
2
3
4
5
6
7
8
# 3.@PostConstruct和static代码块
将要执行的方法所在的类交个spring容器扫描(
@Component
),并且在要执行的方法上添加@PostConstruct
注解或者静态static代码块执行被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Serclet的inti()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
@PreDestroy
说明被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy修饰的方法会在
destroy()
方法之后运行,在Servlet被彻底卸载之前。———————————————— 版权声明:本文为CSDN博主「这辈子_安静的努力着」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35868412/article/details/89085500
这是最简单的方式。但是会影响服务提供,比如这个方法要执行五分钟 这五分钟之内是无法提供服务的,这个方法是在服务初始化后之前运行, 所以 此方法运行不结束,服务就无法初始化, 在这过程路也无法提供服务。
@Component
public class Test2 {
//静态代码块会在依赖注入后自动执行,并优先执行
static{
System.out.println("---static--");
}
/**
* @Postcontruct’在依赖注入完成后自动调用
*/
@PostConstruct
public static void haha(){
System.out.println("@Postcontruct’在依赖注入完成后自动调用");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4.实现CommandLineRunner接口
实现CommandLineRunner接口,@Order注解里面的参数是类执行的顺序,由小到大顺序。
监听接口方式,启动服务,执行方式时仍然提供服务,服务初始化之后即表示所有的bean以及applicationCOntenxt已完成,执行方法。
@Component
//@Order(value=1)//有多个CommandLineRunner接口时可以指定执行顺序(小的先执行)
public class SettingConfig implements CommandLineRunner {
@Autowired
ConfigRepository configRepository;
@Autowired
CacheService cacheService;
/**
* 用于指示bean包含在SpringApplication中时应运行的接口。可以定义多个applicationrunner bean
* 在同一应用程序上下文中,可以使用有序接口或@order注释对其进行排序。
*/
@Override
public void run(String... strings) throws Exception {
System.err.println("执行数据初始化操作......");
//将config数据。。放置redis
List<Config> configs = configRepository.findAll();
if(configs != null && !configs.isEmpty()){
for(Config config : configs){
String key = Constant.CONFIG_PREFIX+config.getKey();
String val = config.getValue();
cacheService.set(key, val);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 5、springboot的启动类main方法体
# 6、在servletcontext设置系统属性
/**
* 在ServletContext里存放部分数据
* @return
*/
@Bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setAttribute("jsVer", "1.0");
}
};
}
2
3
4
5
6
7
8
9
10
11
12
13