死磕Spring系列,SpringBoot启动流程
参考文章:SpringBoot启动流程系列讲解
参考视频:SpringBoot启动流程
吐血推荐视频:史上最完整的Spring启动流程
超级好文:SpringBoot执行原理
参考文章:SpringBoot资源接口ResourceLoader和Resource学习
参考文章:到底什么是上下文(Context)
参考文章:超级好文
参考文章:这个系列的文章,让我自愧不如,痛删了原来2W字的内容
文章目录
- Spring Boot启动流程
- 服务构建
- 环境准备
- 容器创建
- 填充容器
- 非常重要的函数
- getSpringFactoriesInstances()
- getClassLoader()
- deduceMainApplicationClass()
- getRunListeners(String[] args)
- SpringApplicationRunListeners.starting()
- prepareEnvironment(SpringApplicationRunListeners, applicationArguments)
- prepareContext()
- refreshContext()
- refreshContext().refresh().obtainFreshBeanFactory()
- refreshContext().refresh().finishBeanFactoryInitialization(beanFactory)
- 非常重要的类
- ResourceLoader
- 总结
- **4. 填充容器(就是容器创建的refreshContext)**
Spring Boot启动流程
服务构建
//SpringApplication类
//只列出几个重要的字段和方法
public class SpringApplication {//SpringApplication默认web容器类public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";//banner名称,默认为banner.txtpublic static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;//banner位置key,默认为spring.banner.locationpublic static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;//调用main函数的类,也就是YanggxApplication.classprivate Class<?> mainApplicationClass;//bean名称生成器,执行结果为nullprivate BeanNameGenerator beanNameGenerator;//spring的环境,我们使用的是ServletWeb环境private ConfigurableEnvironment environment;//web类型,执行结果为SERVLETprivate WebApplicationType webApplicationType;//Application初始化器,springboot启动过程中执行其initialize方法private List<ApplicationContextInitializer<?>> initializers;//Application监听器,springboot启动过程执行其onApplicationEvent方法private List<ApplicationListener<?>> listeners;/*** SpringApplication构造函数* @param resourceLoader 资源加载器的策略接口,传参null,* @param primarySources 传参YanggxApplication.class*/public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {//执行结果:nullthis.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");//Set去重:"primarySources":[com.yanggx.spring.YanggxApplication.class]this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 判断当前模块web类型:"webApplicationType":"SERVLET"this.webApplicationType = WebApplicationType.deduceFromClasspath();// 加载Application初始化器// 获取所有"META-INF/spring.factories"文件中维护的ApplicationContextInitializer子类列表// org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer// org.springframework.boot.context.ContextIdApplicationContextInitializer // org.springframework.boot.context.config.DelegatingApplicationContextInitializer // org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer // org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListenersetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 3.3 加载Application监听器// 获取所有"META-INF/spring.factories"文件中维护的ApplicationListener子类列表// org.springframework.boot.ClearCachesApplicationListener// org.springframework.boot.builder.ParentContextCloserApplicationListener// org.springframework.boot.context.FileEncodingApplicationListener// org.springframework.boot.context.config.AnsiOutputApplicationListener// org.springframework.boot.context.config.ConfigFileApplicationListener// org.springframework.boot.context.config.DelegatingApplicationListener// org.springframework.boot.context.logging.ClasspathLoggingApplicationListener// org.springframework.boot.context.logging.LoggingApplicationListener// org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener// org.springframework.boot.autoconfigure.BackgroundPreinitializer// 加载的这些类都是ApplicationListener的子类setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 3.4 找到启动类// 抛出一个RuntimeException,然后通过堆栈信息找到启动类//"mainApplicationClass": com.yanggx.spring.YanggxApplication.classthis.mainApplicationClass = deduceMainApplicationClass();}
}
环境准备
public class SpringApplication {public ConfigurableApplicationContext run(String... args) {//实例化一个StopWatch实例, 监控项目运行时间StopWatch stopWatch = new StopWatch();stopWatch.start();//初始化Spring上下文ConfigurableApplicationContext context = null;//初始化错误报告参数Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();//配置headless,在没有显示器,鼠标,键盘的情况下,仍然可以调用显示,输入输出的方法configureHeadlessProperty();//1. 发布Spring启动事件SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {/*2. 这一步的主要作用是处理启动类main函数的参数, 将其封装为一个 DefaultApplicationArguments对象, 为prepareEnvironment()提供参数*/ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//3.这一步的主要作用按顺序加载命令行参数, 系统参数和外部配置文件, 创建并配置Web环境ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment);//4. 打印Banner图Banner printedBanner = printBanner(environment);//....}
容器创建
public ConfigurableApplicationContext run(String... args) {//步骤1: 根据switch创建context,分别有:SERVLET、REACTIVE、NONE,并且注册了Bean后置处理器context = createApplicationContext();//步骤2: BeanFactory是在这里创建的context.setApplicationStartup(this.applicationStartup);//步骤2: prepareContext()准备应用上下文prepareContext(context, environment, listeners, applicationArguments, printedBanner);//步骤3: refreshContext()刷新应用上下文,BeanDefinition和BeanFactory都是在这里创建的refreshContext(context);//步骤4: 刷新完成,该方法是拓展接口,用户可以自定义操作逻辑afterRefresh(context, applicationArguments);//步骤6: 发布Application开始事件listeners.started(context);//步骤7: 执行Runners,用于调用项目中自定义的执行器xxxRunner类,//在项目启动完成后立即执行,这些操作只在服务启动时执行一次callRunners(context, applicationArguments);//步骤8: 发布Application准备事件listeners.running(context);return context;
}
填充容器
/*** 抽象父类ApplicationContext*/
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {//刷新前操作,例如:清空缓存、初始化占位符prepareRefresh();//获取并刷新beanFactory,创建BeanFactory和BeanDefinitionConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//设置beanFactory,配置各种beanFactory.xxx属性prepareBeanFactory(beanFactory);//beanFactory的后置处理器:注册与Servlet相关的特殊Bean,注册beanDefinitionpostProcessBeanFactory(beanFactory);/*BeanFactoryPostProcessor是一个接口, 处理beanFactory中所有的bean, 在所有的beanDefinition加载完成之后, BeanFactoryPostProcessor可以对beanDefinition进行属性的修改, 之后再进行bean实例化*/invokeBeanFactoryPostProcessors(beanFactory);//beanFactory注册后置处理器,对bean实例的增强registerBeanPostProcessors(beanFactory);//初始化messageSourceinitMessageSource();//初始化Application事件发布器initApplicationEventMulticaster();//初始化其他特殊的bean,例如实例化了TomcatWebServeronRefresh();//注册监听器registerListeners();//完成beanFactory初始化finishBeanFactoryInitialization(beanFactory);//完成刷新,发布完成事件,实例化了所有beanfinishRefresh(); }
}
非常重要的函数
getSpringFactoriesInstances()
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = this.getClassLoader();Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {//主要获取spring.factories中的key,key对应接口全名String factoryTypeName = factoryType.getName();//筛选Map中key为factoryTypeName对应放到list返回 return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}
//会把Spring.factories文件中所有键值对放到Map中,其实就是缓存//classLoader参数就是"META-INF/spring.factories"加载器private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {//如果缓存已经有Spring.factories,那就从缓存中拿MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}//如果缓存中没有Spring.factories,那就从重新加载到缓存Enumeration<URL> urls = (classLoader != null ?// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;}
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList<>(names.size());for (String name : names) {try {//通过反射机制创建实例Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);T instance = (T) BeanUtils.instantiateClass(constructor, args);instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;}
总结:
- loadSpringFactories(@Nullable ClassLoader classLoader):判断缓存是否有Spring.factories文件,如果有就提取整个spring.factories。如果没有就加载到缓存再提取。
- loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader):从spring.factories文件中获取指定factoryType
- createSpringFactoriesInstances(Class type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set names):将loadFactoryNames()返回的factoryType,通过反射机制实例化- getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args):获取createSpringFactoriesInstances()返回的实例instances,返回实例
getClassLoader()
- 我们都知道java程序写好以后是以.java(文本文件)的文件存在磁盘上,然后,我们通过(bin/javac.exe)编译命令把.java文件编译成.class文件(字节码文件),并存在磁盘上。
但是程序要运行,首先一定要把.class文件加载到JVM内存中才能使用的,我们所讲的classLoader,就是负责把磁盘上的.class文件加载到JVM内存中- 你可以认为每一个Class对象拥有磁盘上的那个.class字节码内容,每一个class对象都有一个getClassLoader()方法,得到是谁把我从.class文件加载到内存中变成Class对象的
deduceMainApplicationClass()
private Class<?> deduceMainApplicationClass() {try {//通过一个RuntimeException,获取器堆栈信息StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {//堆栈中包含main方法,实例化一个该类的对象return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) { }return null;
}
getRunListeners(String[] args)
//当前只能获取SpringApplicationRunListener子类列表EventPublishingRunListenerprivate SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}
SpringApplicationRunListeners.starting()
//该SpringApplicationRunListeners存在多个子类,在下面starting方法中,会调用对应子类的starting方法
class SpringApplicationRunListeners {//发布启动事件public void starting() {for (SpringApplicationRunListener listener : this.listeners) {//目前调用EventPublishingRunListener的starting方法listener.starting();}}//其他事件都是相同的代码
}
//不仅仅是ApplicationListeners存在很多子类,EventPublishingRunListener也有很多子类
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {//SpringApplication对象private final SpringApplication application;//命令函参数private final String[] args;//事件广播器private final SimpleApplicationEventMulticaster initialMulticaster;public EventPublishingRunListener(SpringApplication application, String[] args) {this.application = application;this.args = args;this.initialMulticaster = new SimpleApplicationEventMulticaster();// 通过application.getListeners(),获取到Listener列表// ConfigFileApplicationListener// AnsiOutputApplicationListener// LoggingApplicationListener// ClasspathLoggingApplicationListener// BackgroundPreinitializer// DelegatingApplicationListener// ParentContextCloserApplicationListener// ClearCachesApplicationListener// FileEncodingApplicationListener// LiquibaseServiceLocatorApplicationListenerfor (ApplicationListener<?> listener : application.getListeners()) {//将listener添加到事件广播器initialMulticasterthis.initialMulticaster.addApplicationListener(listener);}}@Overridepublic void starting() {// 广播器广播ApplicationStartingEvent事件this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));}//其他事件发布都是相同的代码//...
}
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));//调用父类getApplicationListeners方法//遍历所有支持ApplicationStartingEvent事件的监听器//LoggingApplicationListener//BackgroundPreinitializer//DelegatingApplicationListener//LiquibaseServiceLocatorApplicationListenerfor (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {//此时的executor为nullExecutor executor = getTaskExecutor();if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {//调用listenerinvokeListener(listener, event);}}}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = this.getErrorHandler();if (errorHandler != null) {try {this.doInvokeListener(listener, event);} catch (Throwable var5) {errorHandler.handleError(var5);}} else {this.doInvokeListener(listener, event);}}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {//调用listener的onApplicationEvent方法listener.onApplicationEvent(event);}
onApplicationEvent(event){到这里就不再深究了,这个方法有三十个实现类,操作基本上就是绑定环境,设置参数等等
}
总结:
- SpringApplicationRunListeners.starting()调用了EventPublishingRunListener.starting();
- EventPublishingRunListener.starting()调用了广播器initialMulticaster.multicastEvent()发布SpringApplication启动事件
- initialMulticaster.multicastEvent()分别调用了LoggingApplicationListener、BackgroundPreinitializer、DelegatingApplicationListener、LiquibaseServiceLocatorApplicationListener的invokeListener()方法
invokeListener()——doInvokeListener()——onApplicationEvent()——设置参数、绑定环境等等
prepareEnvironment(SpringApplicationRunListeners, applicationArguments)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {//获取或者创建环境,根据switch判断,选择SERVLET、REACTIVE、NONE类型ConfigurableEnvironment environment = getOrCreateEnvironment();//配置环境:为environment配置“共享的类型转换服务”,即:A数据类型变成B数据类型 //然后将defaultProperties和args分别添加到environment的propertySources中configureEnvironment(environment, applicationArguments.getSourceArgs());//发布环境准备事件listeners.environmentPrepared(environment);//如果指定了main函数,那么会将当前环境绑定到指定的SpringApplication中bindToSpringApplication(environment);if (!this.isCustomEnvironment) {//环境转换:如果environment.class和模块EnvironmentClass()不一致,就转换成一样的environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}//将环境依附到PropertySourcesConfigurationPropertySources.attach(environment);return environment;}
prepareContext()
private void prepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {//设置context环境:统一ApplicationContext与Application.environment一致context.setEnvironment(environment);//设置ApplicationContext.beanNameGenerator、resourceLoader、classLoader、类型转换服务postProcessApplicationContext(context);//获取6个初始化器并执行初始化方法,例如设置元数据、配置警告、获取应用名称applyInitializers(context);//发布contextPrepared事件listeners.contextPrepared(context);if (this.logStartupInfo) {//配置了info日志//打印启动和profile日志logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}//获取到DefaultListableBeanFactory实例ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();//注册名为springApplicationArguments,值为applicationArguments的单例beanbeanFactory.registerSingleton("springApplicationArguments", applicationArguments);//banner不为空,那么注册名为springBootBanner,值为printedBanner的单例beanif (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}if (beanFactory instanceof DefaultListableBeanFactory) {//allowBeanDefinitionOverriding默认为false((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// 获取sources列表,获取到我们的YanggxApplication.classSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");//初始化bean加载器,并加载bean到应用上下文load(context, sources.toArray(new Object[0]));//发布contextLoaded事件listeners.contextLoaded(context);}
refreshContext()
//刷新应用上下文,注册关闭应用钩子private void refreshContext(ConfigurableApplicationContext context) {refresh(context);if (this.registerShutdownHook) context.registerShutdownHook();}
/*** 抽象父类ApplicationContext*/
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {//清空缓存、清空监听器、判断必要属性是否被忽略、打印日志、初始化占位符、设置earlyApplicationEventsprepareRefresh();//获取并刷新beanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();/*配置classLoader为当前context的classLoader设置BeanExpressionResolver, 解析EL表达式设置属性编辑器添加BeanPostProcessor配置自动装配手工注册environment相关bean*/prepareBeanFactory(beanFactory);/*注册basepackages注册annotatedClasses注册了request和session两个scopes注册几个Autowired依赖类 */postProcessBeanFactory(beanFactory);/*BeanFactoryPostProcessor接口用于增强BeanFactory,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取bean 的定义,并可以修改它。例如:public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) */invokeBeanFactoryPostProcessors(beanFactory);//beanFactory注册后置处理器,对bean实例的增强registerBeanPostProcessors(beanFactory);//初始化messageSourceinitMessageSource();//初始化Application事件发布器initApplicationEventMulticaster();//初始化其他特殊的bean,例如实例化了TomcatWebServeronRefresh();//注册监听器registerListeners();//完成beanFactory初始化finishBeanFactoryInitialization(beanFactory);//完成刷新,发布完成事件,实例化了所有beanfinishRefresh();} }
}
public abstract class AbstractApplicationContext extends DefaultResourceLoaderimplements ConfigurableApplicationContext {protected void prepareRefresh() {//记录开始时间,调整active状态this.startupDate = System.currentTimeMillis();this.closed.set(false);this.active.set(true);//初始化占位符,例如:$,#,{}initPropertySources();//如果属性中缺少requiredProperties,那么抛出MissingRequiredPropertiesExceptiongetEnvironment().validateRequiredProperties();//清空监听器if (this.earlyApplicationListeners == null) {this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);}else {this.applicationListeners.clear();this.applicationListeners.addAll(this.earlyApplicationListeners);}//初始化earlyApplicationEventsthis.earlyApplicationEvents = new LinkedHashSet<>();}
}
refreshContext().refresh().obtainFreshBeanFactory()
/*refreshBeanFactory():创建beanFactory、指定序列化Id、定制beanFactory、加载bean定义getBeanFactory():返回beanFactory实例
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {//1.初始化beanFactory,并执行加载和解析配置操作refreshBeanFactory();//返回beanFactory实例ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {... @Overrideprotected final void refreshBeanFactory() throws BeansException {//判断是否存在beanFactoryif (hasBeanFactory()) {// 注销所有的单例destroyBeans();//重置beanFactorycloseBeanFactory();}try {//创建beanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();//指定序列化id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象beanFactory.setSerializationId(getId());//定制BeanFactorycustomizeBeanFactory(beanFactory);//下载BeanDefinitions,放到BeanDefinitionsMaploadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}...
}
refreshContext().refresh().finishBeanFactoryInitialization(beanFactory)
/* 根据loadBeanDefinitions加载的BeanDinition到BeanDefinitionMap,再从BeanDefinitionMap拿出来BeanDefinitionMap创建Bean
*/
createBean(){//1. createBeanInstance通过反射机制获取Bean的构造方法,然后创建Bean。当然,如果构造方法需要参数,就会到单例池中查找。//2. populateBean填充Bean属性//3. 初始化1. 初始化容器信息,通过invokeAwareMethods(),唤醒各种Aware接口,获取Bean在容器中的信息2. 初始化Bean成普通对象:通过invokeInitMethods(),执行Bean的初始化方法,这个方法可以通过实现InitialzingBean接口实现的afterPropertiesSet方法。3. AOP操作Bean成:初始化之前和之后处理各种Bean的后置处理器,即在invokeInitMethods()之前和之后分别执行applyBeanPostProcessorsBeforeInitialization()和applyBeanPostProcessorsAfterInitialization()//4. 注册销毁实现了销毁接口DisposableBean,在registerDisposableBean方法注册指定的Bean在销毁时可以直接执行destroy方法销毁Bean
}addSingleton(){将上面create出来的Bean放入单例池就可以获取和使用了
}
非常重要的类
ResourceLoader
//默认的资源加载器
public class DefaultResourceLoader implements ResourceLoader {@Nullableprivate ClassLoader classLoader;//自定义ProtocolResolver, 用于获取资源private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);//private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);//实例化ClassLoaderpublic DefaultResourceLoader() {this.classLoader = ClassUtils.getDefaultClassLoader();}//加载资源@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");//自定义资源加载方式for (ProtocolResolver protocolResolver : this.protocolResolvers) {//调用ProtocolResolver的resolve方法Resource resource = protocolResolver.resolve(location, this);if (resource != null) {//如果获取到资源,立即返回return resource;}}if (location.startsWith("/")) {//先判断是否是根目录return getResourceByPath(location);}else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//再判断是否是classpath下的资源return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}else {try {//先当做一个URL处理URL url = new URL(location);//先判断是否是一个file//不是file的话,再从URL中获取return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));}catch (MalformedURLException ex) {//获取不到资源的话//当做resource处理return getResourceByPath(location);}}}
}
总结
1. 服务构建
- 设置primarySources:YanggxApplication.class
- 设置webType:SERVLET
- 从Spring.factories获取初始化器
- 从Spring.factories获取监听器ApplicationListener
- 设置启动类:YanggxApplication.class
2. 环境准备
- 发布Spring启动事件listeners.starting()
- 封装args参数 new DefaultApplicationArguments(args);
- 配置环境并让环境生效prepareEnvironment() & configureIgnoreBeanInfo()
- 打印Banner图printBanner(environment)
3. 容器创建
- 创建容器createApplicationContext()
- 设置容器prepareContext()
- 刷新容器refreshContext(),这里也是填充容器,重点是 invokeBeanFactoryPostProcessors(beanFactory);和onRefresh()
- 执行Runners
4. 填充容器(就是容器创建的refreshContext)
相关文章:

死磕Spring系列,SpringBoot启动流程
参考文章:SpringBoot启动流程系列讲解 参考视频:SpringBoot启动流程 吐血推荐视频:史上最完整的Spring启动流程 超级好文:SpringBoot执行原理 参考文章:SpringBoot资源接口ResourceLoader和Resource学习 参考文章&…...

关于条件变量wait操作中锁的作用
condition_variable::wait的锁 在看C Concurrency in Action 6.2.3节的线程安全队列时,其对condition_variable的使用与常规用法有点不同,我对condition_variable::wait中锁的作用产生了疑惑:它究竟是保护的谁?于是找到了 C noti…...

JUC并发编程与源码分析笔记09-原子类操作之十八罗汉增强
基本类型原子类 AtomicInteger、AtomicBoolean、AtomicLong。 常用API: public final int get();// 获取当前的值 public final int getAndSet(int newValue);// 获取当前值,并设置新值 public final int getAndIncrement();// 获取当前的值࿰…...

含分布式电源的配电网日前两阶段优化调度模型(Matlab代码实现)
👨🎓个人主页:研学社的博客💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密…...

FreeRTOS的Delay函数
两个Delay函数有两个延时函数vTaskDelay:至少等待指定个数的Tick Interrupt才能变为就绪态xTaskDelayUtil:等待到指定的绝对时刻,才能变为就绪态个人感觉这两个延时函数就是,比如一个我等3个小时,一个是我等到下午3点的…...

HCIA-HarmonyOS Application Developer——题目集1
题目1 1、一位开发人员在设计应用程序时,添加了一个Text组件和Button组件,开发样图如下所示。该开发者不能选择哪种布局方式来放置组件? A、StackLayout B、DependentLayout C、DirectionalLayout D、TableLayout 解析:(A&#…...

高性能 Message ToJavaBean 工具 【easy.server.mapper】
easy.server.mapper 介绍 后端开发中,消息转换常见问题 Map 中的数据 转换成实体Bean数组 中的数据 转换成实体BeanServet 中的 param 转换成实体Bean 以上的三个问题是最常见的消息转换困扰。 以Map 举例 常见做法是 手动转换 Map<String,Object> da…...

Web前端学习:三 - 练习
三六:风筝效果 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style type"text/css">*{margin: 0;padding: 0;}.d1{width: 200px;height: 200px;background: yellow;position…...

面试题:Android 中 Intent 采用了什么设计模式?
答案是采用了原型模式。原型模式的好处在于方便地拷贝某个实例的属性进行使用、又不会对原实例造成影响,其逻辑在于对 Cloneable 接口的实现。 话不多说看下 Intent 的关键源码: // frameworks/base/core/java/android/content/Intent.java public cla…...

Java数据类型与变量
个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【JavaSE_primary】 文章目录字面常量数据类型变量整型变量字节型变量浮点数变量双精度浮点数单精度浮点数字符型变量布尔型变量空常量nu…...

Python为CANoe工程添加/删除DBC文件
前面文章我们对于通过COM来实现打开CANoe、导入CANoe配置工程、导入执行文件、启动CANoe软件和执行脚本;但是这只能完成最基本的功能调用,在实际得到使用过程中,特别是各家在推的CI/CD以及平台化,仅仅是实现这些功能是完全不够用的;比如dbc的添加和删除,这是我们非常必要…...

不同的产品经理特征和需要的能力
产品经理是一个管家,需要和各方沟通推动产品各个决策进展。 每天早上看看线上用户数据、看下今天要安排任务,接着就是和各方开会讨论推动产品实现。每天穿插于与 UI、用户以及完成自己的 todolist 中循环。如果公司体制完善,还要和运营、数据…...

webpack之处理样式资源
处理样式资源 本章节我们学习使用 Webpack 如何处理 Css、Less、Sass、Scss、Styl 样式资源 #介绍 Webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源 我们找 Loader 都应该去官方文档中找到对应的 Loader,然后…...

Golang 接口笔记
基本介绍接口是一个数据类型,可以定义一组方法,但都不需要实现。并且interface中不能包含任何变量。到某个自定义类型要使用的时候,再根据具体情况把这些方法实现出来语法type 接口名 interface {method1(参数列表) 返回值列表method2(参数列…...

[计算机网络(第八版)]第二章 物理层(章节测试/章节作业)
章节作业 带答案版 选择题 (单选题)双绞线是用两根绝缘导线绞合而成的,绞合的目的是( )。 A. 减少干扰 B. 提高传输速度 C. 增大传输距离 D. 增大抗拉强度(单选题)在电缆中采用屏蔽技术可以带来的好处主要是( )。 A…...

[iOS 理解] Swift Runtime (1) 类
Warm up 先看一段代码: import ObjectiveCclass Obj {var x: Double 0 }let v: NSObjectProtocol Obj() as! NSObjectProtocol let result v.isKind(of: Obj.self) let size class_getInstanceSize(Obj.self)我们有一个没有继承 NSObject、没有遵循 NSObjectP…...

ASEMI低压MOS管20N06参数,20N06体积,20N06大小
编辑-Z ASEMI低压MOS管20N06参数: 型号:20N06 漏极-源极电压(VDS):60V 栅源电压(VGS):20V 漏极电流(ID):20A 功耗(PD࿰…...

常见前端基础面试题(HTML,CSS,JS)(四)
作用域和作用域链的理解 作用域 (1)全局作用域 最外层函数和最外层函数外面定义的变量拥有全局作用域所有未定义直接赋值的变量自动声明为全局作用域所有window对象的属性拥有全局作用域全局作用域有很大的弊端,过多的全局作用域变量会污染…...

RabbitMQ发布确认模式
目录 一、发布确认原理 二、发布确认的策略 (一)开启发布确认的方法 (二)单个确认模式 (三)批量确认模式 (四)异步确认模式 (五)如何处理异步未确认消…...

零基础的人如何入门 Python ?看完这篇文章你就懂了
第一部分:编程环境准备 零基础入门Python的话我不建议用IDE,IDE叫集成开发环境,这东西一般是专业程序员用来实战开发用的,好处很多,比如:调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测…...

Atcoder abc257 E
E - Addition and Multiplication 2 题意: 给你一个数字n表示你现在拥有的金额 然后给你1~9每个经营额所需要的成本, 设总经营额为x, 当前使用的经营额为y, 则每一次使用经营额时都有x10*xy 问, 如何在使用不大于成本数量的金额下, 使得经营额最高 例如: 5 5 4 3 8 1 6 7 …...

模拟退火算法改进
import numpy as np import matplotlib.pyplot as plt import math import random from scipy.stats import norm from mpl_toolkits.mplot3d import Axes3D # 目标函数 def Function(x, y): return -20 * np.exp(-0.2*np.sqrt(0.5*(x*xy*y)))\ -np.exp(0.5*(n…...

SpringBoot+HttpClient+JsonPath提取A接口返回值作为参数调用B接口
前言 在做java接口自动化中,我们常常需要依赖多个接口,A接口依赖B,C,D接口的响应作为请求参数;或者URL中的参数是从其他接口中提取返回值作获取参数这是必不可少的。那么怎么实现呢?下面就来介绍多业务依赖…...

JUC 之 CompletableFuture
——CompletableFuture Future Future 接口(FutureTask 实现类) 定义了操作异步任务执行的一些方法,如获取异步的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕例如: 主线程让一个子线程去执行任务&…...

7-vue-1
谈谈你对MVVM的理解 为什么要有这些模式,目的:职责划分、分层(将Model层、View层进行分类)借鉴后端思想,对于前端而已,就是如何将数据同步到页面上 MVC模式 代表:Backbone underscore jquer…...

OpenAPI SDK组件介绍
背景 公司成立以来,积累了数以万计的可复用接口。上层的SaaS业务,原则上要复用这些接口开发自己的业务,为了屏蔽调用接口的复杂性,基础服务开发了apisdk组件,定义了一套声明OpenAPI的注解、注解解析器,实例…...

【Java】Synchronized锁原理和优化
一、synchronized介绍 synchronized中文意思是同步,也称之为”同步锁“。 synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。 synchronized是Java中解决并发问题的一种最常用的方法…...

西北工业大学2020-2021学年大物(I)下期末试题选填解析
2 位移电流。磁效应服从安培环路,热效应不服从焦耳-楞次定律。注意,它是变化的电场而非磁场产生。3 又考恒定磁场中安培环路定理。4感生电场5 麦克斯韦速率分布函数。6 相同的高温热源和低温热源之间的一切可逆热机的工作效率相等,无论工质如…...

PHP - ChatGpt API 接入 ,代码,亲测!(最简单!)
由于最近ChatGpt 大火,但是门槛来说是对于大家最头疼的环节, 我自己也先开发了一个个人小程序!大家可以访问使用下, 由此ChatGpt 有一个API 可以仅供大伙对接 让我来说下资质: 1:首先要搞得到一个 ChatGp…...

物联网MQTT协议简单介绍
物联网曾被认为是继计算机、互联网之后,信息技术行业的第三次浪潮。随着基础通讯设施的不断完善,尤其是 5G 的出现,进一步降低了万物互联的门槛和成本。物联网本身也是 AI 和区块链应用很好的落地场景之一,各大云服务商也在纷纷上…...