Spring boot啟動(dòng)原理及相關(guān)組件整理
一、Spring Boot應(yīng)用啟動(dòng)
一個(gè)Spring Boot應(yīng)用的啟動(dòng)通常如下:
@SpringBootApplication @Slf4j public class ApplicationMain { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args); } }
執(zhí)行如上代碼,Spring Boot程序啟動(dòng)成功。事實(shí)上啟動(dòng)Spring Boot應(yīng)用離不開SpringApplication。
所以,我們跟隨SpringApplication的腳步,開始從源碼角度分析Spring Boot的初始化過(guò)程。
btw,可參看例子一節(jié),我對(duì)Spring Boot啟動(dòng)的拓展點(diǎn)都做了demo,可參照下面源碼分析進(jìn)行理解。
文檔有一句話說(shuō)了SpringApplication做了什么(目的):
Create an appropriate ApplicationContext instance (depending on your classpath) Register a CommandLinePropertySource to expose command line arguments as Spring properties Refresh the application context, loading all singleton beans Trigger any CommandLineRunner beans
二、SpringApplication構(gòu)造函數(shù)
啟動(dòng)代碼先創(chuàng)建SpringApplication示例,在執(zhí)行run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
如下是SpringApplication的構(gòu)造函數(shù)代碼分析。
this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //通過(guò)Classloader探測(cè)不同web應(yīng)用核心類是否存在,進(jìn)而設(shè)置web應(yīng)用類型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //找出所有spring.factories中聲明的ApplicationContextInitializer并設(shè)置, //ApplicationContextInitializer定義了回調(diào)接口,在refresh()前初始化調(diào)用(即在prepareContext的applyInitializers方法中調(diào)用) setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //找出所有spring.factories中聲明的ApplicationListener(細(xì)節(jié)往后再敘),ApplicationListener繼承了 //java.util.EventListener,實(shí)現(xiàn)了類似觀察者模式的形式,通過(guò)實(shí)現(xiàn)ApplicationListener、SmartApplicationListener,能夠監(jiān)聽Spring上下文的refresh、Prepared等事件或者是自定義事件 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //找出主啟動(dòng)類(有趣的是,是通過(guò)new一個(gè)runtime異常然后在異常棧里面找出來(lái)的) this.mainApplicationClass = deduceMainApplicationClass();
在構(gòu)造期間,主要做了:
1、判定應(yīng)用類型,為后面創(chuàng)建不同類型的spring context做準(zhǔn)備。
2、初始化ApplicationContextInitializer和ApplicationListener。
3、找出啟動(dòng)類。
三、run()源碼解析
介紹run()方法前,先說(shuō)說(shuō)貫穿run方法的ApplicationRunListener,它有助于理解整個(gè)run()的運(yùn)行周期。
寫在這里:Spring Application事件機(jī)制
run()方法分析如下:
//java.awt.headless,是J2SE的一種模式,用于在缺失顯示屏、鼠標(biāo)或者鍵盤時(shí)的系統(tǒng)配置。 configureHeadlessProperty(); //將spring.factories中的SpringApplicationRunListener接口實(shí)現(xiàn)類拖出來(lái),塞到SpringApplicationRunListeners(一個(gè)集合)中,統(tǒng)一批量執(zhí)行 SpringApplicationRunListeners listeners = getRunListeners(args); //觸發(fā)runlistener的starting listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //spring.beaninfo.ignore如果沒(méi)有設(shè)置值,則把它設(shè)為true,具體情況具體設(shè)置, //如果沒(méi)用的話,把它設(shè)為true可以ignore掉classloader對(duì)于不存在的BeanInfo的掃描,提高性能。 configureIgnoreBeanInfo(environment); //banner打印。自定義banner挺好玩的 Banner printedBanner = printBanner(environment); //根據(jù)webApplicationType(一開始推斷的應(yīng)用類型)去新建applicationContext context = createApplicationContext(); //獲取SpringBootExceptionReporter,回調(diào)接口類,提供啟動(dòng)時(shí)的異常報(bào)告 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //下面會(huì)說(shuō) prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); //do nothing afterRefresh(context, applicationArguments); //計(jì)時(shí)停止 stopWatch.stop(); //打日志 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //啟動(dòng) listeners.started(context); //找出context的ApplicationRunner和CommandLineRunner,用AnnotationAwareOrderComparator排序,并執(zhí)行 callRunners(context, applicationArguments);
下面再分別說(shuō)說(shuō)兩個(gè)方法(prepareEnvironment、refreshContext)的代碼。
四、prepareEnvironment
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); //發(fā)布environment prepared事件 listeners.environmentPrepared(environment); //將獲取到的environment中的spring.main配置綁定到SpringApplication中, //使用的是Binder這個(gè)spring boot2.0開始有的類 bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } //附加的解析器將動(dòng)態(tài)跟蹤底層 Environment 屬性源的任何添加或刪除, //關(guān)于ConfigurationPropertySourcesPropertySource和MutablePropertiySource //將在Environment中作進(jìn)一步講解 ConfigurationPropertySources.attach(environment); return environment; }
五、prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //為上下文設(shè)置environment(配置、profile) context.setEnvironment(environment); //對(duì)application做一些處理,設(shè)置一些組件, //比如BeanNameGenerator,ApplicationConversionService(包含一些默認(rèn)的Converter和formatter) postProcessApplicationContext(context); // 加載并運(yùn)行ApplicationContextInitializer applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); //Load beans(其實(shí)是由sources構(gòu)建beanDefinition) into the application context. //構(gòu)建BeanDefinitionLoader并執(zhí)行BeanDefinitionLoader.load() load(context, sources.toArray(new Object[0])); //執(zhí)行contextLoaded事件 listeners.contextLoaded(context); }
六、容器refresh(refreshContext)
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
refreshContext會(huì)做兩件事,
1、應(yīng)用上下文刷新
2、注冊(cè)shutdown鉤子
我們來(lái)看看ServletWebServer的刷新。
// ServletWebServerApplicationContext public final void refresh() throws BeansException, IllegalStateException { try { super.refresh(); } catch (RuntimeException ex) { //停止webserver stopAndReleaseWebServer(); throw ex; } } org.springframework.context.support.AbstractApplicationContext refresh() public void refresh() throws BeansException, IllegalStateException { // 單線程執(zhí)行 synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 1、設(shè)置Spring容器的啟動(dòng)時(shí)間,撤銷關(guān)閉狀態(tài),開啟活躍狀態(tài)。2、初始化屬性源信息(Property)3、驗(yàn)證環(huán)境信息里一些必須存在的屬性 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 如果是RefreshtableApplicationContext會(huì)做了很多事情: // 1、讓子類刷新內(nèi)部beanFactory ,創(chuàng)建IoC容器(DefaultListableBeanFactory--ConfigurableListableBeanFactory 的實(shí)現(xiàn)類) // 2、加載解析XML文件(最終存儲(chǔ)到Document對(duì)象中) // 3、讀取Document對(duì)象,并完成BeanDefinition的加載和注冊(cè)工作 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //從Spring容器獲取BeanFactory(Spring Bean容器)并進(jìn)行相關(guān)的設(shè)置為后續(xù)的使用做準(zhǔn)備: //1、設(shè)置classloader(用于加載bean),設(shè)置表達(dá)式解析器(解析bean定義中的一些表達(dá)式),添加屬性編輯注冊(cè)器(注冊(cè)屬性編輯器) //2、添加ApplicationContextAwareProcessor這個(gè)BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware這5個(gè)接口的自動(dòng)注入。因?yàn)锳pplicationContextAwareProcessor把這5個(gè)接口的實(shí)現(xiàn)工作做了 //3、設(shè)置特殊的類型對(duì)應(yīng)的bean。BeanFactory對(duì)應(yīng)剛剛獲取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext這3個(gè)接口對(duì)應(yīng)的bean都設(shè)置為當(dāng)前的Spring容器 //4、注入一些其它信息的bean,比如environment、systemProperties等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. 執(zhí)行beanfactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注冊(cè)beanPostProcessor registerBeanPostProcessors(beanFactory); // Initialize message source for this context. 初始化messageSource initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
七、postProcessBeanFactory()
設(shè)置BeanFactory之后再進(jìn)行后續(xù)的一些BeanFactory操作。
不同的Context會(huì)進(jìn)行不同的操作。 比如,AnnotationConfigServletWebServerApplicationContext
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 父類實(shí)現(xiàn),會(huì)注冊(cè)web應(yīng)用特有的factory scope, super.postProcessBeanFactory(beanFactory); //查看basePackages屬性,如果設(shè)置了會(huì)使用ClassPathBeanDefinitionScanner去掃描basePackages包下的bean并注 if (this.basePackages != null && this.basePackages.length > 0) { this.scanner.scan(this.basePackages); } // 查看annotatedClasses屬性,如果設(shè)置了會(huì)使用AnnotatedBeanDefinitionReader去注冊(cè)這些bean if (!this.annotatedClasses.isEmpty()) { this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); } }
八、invokeBeanFactoryPostProcessors()
/** * Instantiate and invoke all registered BeanFactoryPostProcessor beans, * respecting explicit order if given. * <p>Must be called before singleton instantiation. */ protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { //執(zhí)行AbstractContext持有的BeanFactory后置處理器 //這些處理器是之前ContextInitializer PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) // 如果通過(guò)-javaagent參數(shù)設(shè)置了LTW的織入器類包,那么增加LTW的BeanProcessor。 if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
從容器中找出BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor(二者的區(qū)別是,一個(gè)使用BeanDefinitionRegistry作處理,一個(gè)使用BeanFactory做處理), 并按一定的規(guī)則順序執(zhí)行。
ConfigurationClassPostProcessor的優(yōu)先級(jí)為最高,它會(huì)對(duì)項(xiàng)目中的@Configuration注解修飾的類(@Component、@ComponentScan、@Import、@ImportResource修飾的類也會(huì)被處理)進(jìn)行解析,解析完成之后把這些bean注冊(cè)到BeanFactory中。 需要注意的是這個(gè)時(shí)候注冊(cè)進(jìn)來(lái)的bean還沒(méi)有實(shí)例化。
ConfigurationClassPostProcessor的流程之后會(huì)獨(dú)立進(jìn)行分析。
九、registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)方法
···java /** Instantiate and invoke all registered BeanPostProcessor beans,respecting explicit order if given. Must be called before any instantiation of application beans. */ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { //委派PostProcessorRegistrationDelegate去做 PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this); }
從Spring容器中按一定順序(PriorityOrdered、Ordered、非PriorityOrdered非Ordered)找出實(shí)現(xiàn)了BeanPostProcessor接口的bean,并設(shè)置到BeanFactory的屬性中。之后bean被實(shí)例化的時(shí)候會(huì)調(diào)用這個(gè)BeanPostProcessor。 ## 十、initMessageSource() 初始化一些國(guó)際化相關(guān)的屬性。 Spring boot的國(guó)際化配置可閱讀MessageSourceAutoConfiguration。 默認(rèn)情況會(huì)設(shè)置一個(gè)DelegatingMessageSource,是一個(gè)空實(shí)現(xiàn),因?yàn)锳pplicationContext接口拓展了MessageSource接口,所以Spring容器都有g(shù)etMessage方法, 可是,在實(shí)現(xiàn)上又允許空MessageSource,所以,通過(guò)一個(gè)DelegatingMessageSource去適配。 ## 十一、initApplicationEventMulticaster() Initialize event multicaster for this context. 初始化事件廣播器。默認(rèn)實(shí)現(xiàn)是SimpleApplicationEventMulticaster。 onRefresh() 模板方法,給不同的Spring應(yīng)用容器去實(shí)例化一些特殊的類。 比如,AnnotationConfigServletWebServerApplicationContext、AnnotationConfigReactiveWebServerApplicationContext會(huì)去創(chuàng)建web server(createWebServer())。 spring boot的mvc內(nèi)置支持有tomcat、Undertow、jetty三種server,而reactive web server則內(nèi)置支持tomcat、jetty、netty三種。 java // Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown startDaemonAwaitThread();
btw,如果是tomcat server的話,spring boot會(huì)啟動(dòng)多一個(gè)線程防止退出。
十二、registerListeners()
把BeanFactory的ApplicationListener拿出來(lái)塞到事件廣播器里。
如果ApplicationContext的earlyApplicationEvents屬性有值,則廣播該屬性持有的early事件。
十三、finishBeanFactoryInitialization(beanFactory)
實(shí)例化BeanFactory中已經(jīng)被注冊(cè)但是未實(shí)例化的所有實(shí)例(懶加載的不需要實(shí)例化)。
比如invokeBeanFactoryPostProcessors方法中根據(jù)各種注解解析出來(lái)的類,在這個(gè)時(shí)候都會(huì)被初始化。
十四、finishRefresh()
// ReactiveWebServerApplicationContext @Override protected void finishRefresh() { super.finishRefresh(); WebServer webServer = startReactiveWebServer(); if (webServer != null) { publishEvent(new ReactiveWebServerInitializedEvent(webServer, this)); } } // AbstractApplicationContext /** * Finish the refresh of this context, invoking the LifecycleProcessor's * onRefresh() method and publishing the * {@link org.springframework.context.event.ContextRefreshedEvent}. */ protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). // 容器完成刷新,清除資源緩存 clearResourceCaches(); // Initialize lifecycle processor for this context. // 初始化lifeCycleProcessor, 默認(rèn)實(shí)現(xiàn)是DefaultLifeCycleProcessor,實(shí)現(xiàn)了BeanFactoryAware接口,通過(guò)BeanFactory找出LifeCycle bean // 可通過(guò)自定義實(shí)現(xiàn)LifeCycle接口的Bean,來(lái)監(jiān)聽容器的生命周期。 initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. //粗發(fā)生命周期處理器的onRefresh方法,順帶一說(shuō),在程序正常退出時(shí),會(huì)粗發(fā)shutdownHook,那時(shí)會(huì)粗發(fā)生命周期處理器的onClose方法 getLifecycleProcessor().onRefresh(); // Publish the final event. // 廣播ContextRefreshed事件 publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. // 將ApplicationContext注冊(cè)到Spring tool suite里 LiveBeansView.registerApplicationContext(this); }
十五、resetCommonCaches()
// Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches();
最后會(huì)在finally執(zhí)行resetCommonCaches(),清除一些Spring core、beans加載和解析的Bean信息緩存(因?yàn)閷?duì)于singleton bean來(lái)說(shuō)已經(jīng)不需要了)。
十六、流程整理
最后,按照啟動(dòng)階段整理一幅全景圖。
十七、例子
在github里,我把Spring Boot應(yīng)用啟動(dòng)的拓展組件(自定義的應(yīng)用初始器、監(jiān)聽器、事件、ApplicationRunner)都寫了例子,可參照閱讀。 代碼在這 | spring-boot-none-startup
日志如下:
2020-05-20 18:30:11.625 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : environmentPrepared, env:StandardEnvironment {activeProfiles=[dev], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application-dev.yml]'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}]}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.3.RELEASE)2020-05-20 18:30:11.832 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : contextPrepared, ctx:org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970
2020-05-20 18:30:11.838 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : Starting ApplicationMain on DESKTOP-OLDGHC1 with PID 81568 ( started by teash in )
2020-05-20 18:30:11.838 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : The following profiles are active: dev
2020-05-20 18:30:11.894 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : contextLoaded, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970
2020-05-20 18:30:12.404 INFO 81568 --- [ main] .s.b.s.n.s.SimpleApplicationContextAware : SimpleApplicationContextAware and send SimpleAppEvent
2020-05-20 18:30:12.441 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.e.SimpleEventListener : event: net.teaho.demo.spring.boot.startup.none.spring.event.SimpleAppEvent[source=event source], source: event source
2020-05-20 18:30:12.444 INFO 81568 --- [ main] n.t.d.s.b.s.n.config.BeanConfiguration : [net.teaho.demo.spring.boot.startup.none.spring.spi.DemoSpringLoaderImpl@c96a4ea]
2020-05-20 18:30:12.484 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.l.LoggingLifeCycle : In Life cycle bean start().
2020-05-20 18:30:12.496 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : Started ApplicationMain in 1.573 seconds (JVM running for 3.195)
2020-05-20 18:30:12.496 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : started, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020
2020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.EchoApplicationRunner : EchoApplicationRunner running, args:org.springframework.boot.DefaultApplicationArguments@45673f68
2020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.EchoCommandLineRunner : EchoCommandLineRunner running
2020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : running, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020
2020-05-20 18:30:12.500 INFO 81568 --- [ Thread-3] n.t.d.s.b.s.n.s.l.LoggingLifeCycle : In Life cycle bean stop().
到此這篇關(guān)于Spring boot啟動(dòng)原理及相關(guān)組件的文章就介紹到這了,更多相關(guān)Spring boot啟動(dòng)原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot項(xiàng)目啟動(dòng)錯(cuò)誤:找不到或無(wú)法加載主類的三種解決方法
- spring boot啟動(dòng)出現(xiàn)Unable to start ServletWebServerApplicationContext due to missing ServletWebServer錯(cuò)誤解決
- SpringBoot 啟動(dòng)報(bào)錯(cuò)Unable to connect to Redis server: 127.0.0.1/127.0.0.1:6379問(wèn)題的解決方案
- 查看本地啟動(dòng)SpringBoot的本地端口號(hào)的幾種方式
- SpringBoot中@Scheduled實(shí)現(xiàn)服務(wù)啟動(dòng)時(shí)執(zhí)行一次
- SpringBoot應(yīng)用啟動(dòng)慢的原因分析及優(yōu)化方法
相關(guān)文章
SpringBoot之controller參數(shù)校驗(yàn)詳解
介紹了Java中使用@Validated和@Valid進(jìn)行參數(shù)校驗(yàn)的方法,包括不同標(biāo)簽的使用場(chǎng)景、基本屬性和一些常用的注解類型,同時(shí),還討論了如何在控制器中使用這些校驗(yàn)標(biāo)簽,以及如何處理校驗(yàn)結(jié)果和自定義錯(cuò)誤消息,最后,還介紹了如何實(shí)現(xiàn)分組校驗(yàn)和嵌套校驗(yàn),并提供了一些示例代碼2024-11-11Mybatis執(zhí)行插入語(yǔ)句后并返回主鍵ID問(wèn)題
這篇文章主要介紹了Mybatis執(zhí)行插入語(yǔ)句后并返回主鍵ID問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03springboot @ConfigurationProperties和@PropertySource的區(qū)別
這篇文章主要介紹了springboot @ConfigurationProperties和@PropertySource的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06SpringBoot中Filter沒(méi)有生效原因及解決方案
Servlet 三大組件 Servlet、Filter、Listener 在傳統(tǒng)項(xiàng)目中需要在 web.xml 中進(jìn)行相應(yīng)的配置,這篇文章主要介紹了SpringBoot中Filter沒(méi)有生效原因及解決方案,需要的朋友可以參考下2024-04-04使用AOP攔截Controller獲取@PathVariable注解傳入的參數(shù)
這篇文章主要介紹了使用AOP攔截Controller獲取@PathVariable注解傳入的參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08java實(shí)現(xiàn)科研信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java科研信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02兼容Spring Boot 1.x和2.x配置類參數(shù)綁定的工具類SpringBootBindUtil
今天小編就為大家分享一篇關(guān)于兼容Spring Boot 1.x和2.x配置類參數(shù)綁定的工具類SpringBootBindUtil,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12springboot中通過(guò)jwt令牌校驗(yàn)及前端token請(qǐng)求頭進(jìn)行登錄攔截實(shí)戰(zhàn)記錄
這篇文章主要給大家介紹了關(guān)于springboot中如何通過(guò)jwt令牌校驗(yàn)及前端token請(qǐng)求頭進(jìn)行登錄攔截的相關(guān)資料,需要的朋友可以參考下2024-08-08