首页 > 其他分享 >SpringBoot3.x 启动 refresh 过程解析

SpringBoot3.x 启动 refresh 过程解析

时间:2023-08-16 21:32:21浏览次数:41  
标签:容器 beanFactory Spring refresh bean SpringBoot3 Import 注解 解析

Spring容器创建后,会调用它的refresh方法,refresh的时候会做很多事情:如完成配置类解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等。

web程序对应Spring容器为AnnotationConfigServletWebServerApplicationContext。

ServletWebServerApplicationContext接受带注解的类作为输入,特别是 @Configuration 类,也接受使用普通 @Component 类和符合 JSR-330 的 javax.inject 类。允许逐个注册类(将类名指定为配置位置)以及类路径扫描(将基本包指定为配置位置)。 如有多个 @Configuration 类,以后 @Bean 的定义将覆盖在早期加载的文件中定义的定义。可以利用这一点,通过额外的配置类有意覆盖某些 Bean 定义。

其refresh方法调用父类

0 AbstractApplicationContext

1@Override2public void refresh() throws BeansException, IllegalStateException {3  // refresh过程只能一个线程处理,不允许并发执行4  synchronized (this.startupShutdownMonitor) {5    StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");67    // Prepare this context for refreshing.8    prepareRefresh();910    // Tell the subclass to refresh the internal bean factory.11    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();1213    // Prepare the bean factory for use in this context.14    prepareBeanFactory(beanFactory);1516    try {17      // Allows post-processing of the bean factory in context subclasses.18      postProcessBeanFactory(beanFactory);1920      StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");21      // Invoke factory processors registered as beans in the context.22      invokeBeanFactoryPostProcessors(beanFactory);2324      // Register bean processors that intercept bean creation.25      registerBeanPostProcessors(beanFactory);26      beanPostProcess.end();2728      // Initialize message source for this context.29      initMessageSource();3031      // Initialize event multicaster for this context.32      initApplicationEventMulticaster();3334      // Initialize other special beans in specific context subclasses.35      onRefresh();3637      // Check for listener beans and register them.38      registerListeners();3940      // Instantiate all remaining (non-lazy-init) singletons.41      finishBeanFactoryInitialization(beanFactory);4243      // Last step: publish corresponding event.44      finishRefresh();45    }4647    catch (BeansException ex) {48      if (logger.isWarnEnabled()) {49        logger.warn("Exception encountered during context initialization - " +50            "cancelling refresh attempt: " + ex);51      }5253      // Destroy already created singletons to avoid dangling resources.54      destroyBeans();5556      // Reset 'active' flag.57      cancelRefresh(ex);5859      // Propagate exception to caller.60      throw ex;61    }6263    finally {64      // Reset common introspection caches in Spring's core, since we65      // might not ever need metadata for singleton beans anymore...66      resetCommonCaches();67      contextRefresh.end();68    }69  }70}

1 prepareRefresh

真正refresh操作前需要准备做的事:

  1. 设置Spring容器的启动时间,撤销关闭状态,开启活跃状态
  2. 初始化属性源信息(Property)
  3. 验证环境信息里一些必须存在的属性

2 prepareBeanFactory

从Spring容器获取BeanFactory(Spring Bean容器)并进行相关的设置为后续的使用做准备:

1  protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {2    // Tell the internal bean factory to use the context's class loader etc.3    4    // 设置classloader(用于加载bean)5    beanFactory.setBeanClassLoader(getClassLoader());6    // 设置表达式解析器(解析bean定义中的一些表达式)7    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));8    // 添加属性编辑注册器(注册属性编辑器)9    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));1011    // Configure the bean factory with context callbacks.12    // 添加ApplicationContextAwareProcessor这个BeanPostProcessor13    // 取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入14    // 因为ApplicationContextAwareProcessor把这5个接口实现工作做了15    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));16    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);17    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);18    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);19    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);20    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);21    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);22    beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);2324    // BeanFactory interface not registered as resolvable type in a plain factory.25    // MessageSource registered (and found for autowiring) as a bean.26    27   // 设置特殊的类型对应的bean。BeanFactory对应刚刚获取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器28    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);29    beanFactory.registerResolvableDependency(ResourceLoader.class, this);30    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);31    beanFactory.registerResolvableDependency(ApplicationContext.class, this);3233    // Register early post-processor for detecting inner beans as ApplicationListeners.34    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));3536    // Detect a LoadTimeWeaver and prepare for weaving, if found.37    if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {38      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));39      // Set a temporary ClassLoader for type matching.40      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));41    }4243    // Register default environment beans.44    // 注入一些其它信息的bean45    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {46      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());47    }48    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {49      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());50    }51    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {52      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());53    }54    if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {55      beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());56    }57  }

3 postProcessBeanFactory

BeanFactory设置之后再进行后续的一些BeanFactory操作。

不同的Spring容器做不同的操作。比如GenericWebApplicationContext容器会在BeanFactory中添加ServletContextAwareProcessor用于处理ServletContextAware类型的bean初始化的时候调用setServletContext或者setServletConfig方法(ApplicationContextAwareProcessor原理)。

AnnotationConfigEmbeddedWebApplicationContext对应的postProcessBeanFactory方法:

1AnnotationConfigServletWebServerApplicationContext.java23@Override4protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {5  // 调用父类EmbeddedWebApplicationContext实现6  super.postProcessBeanFactory(beanFactory);7  // 查看basePackages属性,如设置了,使用ClassPathBeanDefinitionScanner扫描basePackages包下的bean并注册8  if (this.basePackages != null && this.basePackages.length > 0) {9    this.scanner.scan(this.basePackages);10  }11  // 查看annotatedClasses属性,如设置了,使用AnnotatedBeanDefinitionReader注册这些bean12  if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {13    this.reader.register(this.annotatedClasses);14  }15}

父类EmbeddedWebApplicationContext实现

ServletContextAwareProcessor的变体,用于 ConfigurableWebApplicationContext.可以在注册处理器时使用,可以在 或 ServletConfig 已经初始化之前ServletContext发生。

1public class WebApplicationContextServletContextAwareProcessor extends ServletContextAwareProcessor {23  private final ConfigurableWebApplicationContext webApplicationContext;45  public WebApplicationContextServletContextAwareProcessor(ConfigurableWebApplicationContext webApplicationContext) {6    Assert.notNull(webApplicationContext, "WebApplicationContext must not be null");7    this.webApplicationContext = webApplicationContext;8  }9  ...10}

4 invokeBeanFactoryPostProcessors方法

在Spring容器中找出实现了BeanFactoryPostProcessor#processor并执行。Spring容器会委托给PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors。

  1. BeanFactoryPostProcessor:修改Spring容器中已存在的bean的定义,使用ConfigurableListableBeanFactory对bean进行处理
  2. BeanDefinitionRegistryPostProcessor:继承BeanFactoryPostProcessor,作用跟BeanFactoryPostProcessor一样,只不过是使用BeanDefinitionRegistry对bean进行处理

基于web程序的Spring容器AnnotationConfigServletWebServerApplicationContext构造时,会初始化内部属性AnnotatedBeanDefinitionReader reader:

1public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext2    implements AnnotationConfigRegistry {34  private final AnnotatedBeanDefinitionReader reader;56  private final ClassPathBeanDefinitionScanner scanner;78  private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>();910  private String[] basePackages;1112  public AnnotationConfigServletWebServerApplicationContext() {13    this.reader = new AnnotatedBeanDefinitionReader(this);14    this.scanner = new ClassPathBeanDefinitionScanner(this);15  }

这reader构造的时候会在BeanFactory中注册一些post processor,包括BeanPostProcessor和BeanFactoryPostProcessor(如ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor):

1AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

invokeBeanFactoryPostProcessors处理BeanFactoryPostProcessor

从Spring容器中找出BeanDefinitionRegistryPostProcessor类型的bean。

这些processor是在容器刚创建的时候通过构造AnnotatedBeanDefinitionReader的时候注册到容器中的,然后按优先级执行。

优先级逻辑:

  1. 实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor先全部找出来,然后排序后依次执行
  2. 实现Ordered接口的BeanDefinitionRegistryPostProcessor找出来,然后排序后依次执行
  3. 没有实现PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor找出来执行并依次执行

接着,从Spring容器内查找BeanFactoryPostProcessor接口的实现类,然后执行(若processor已经执行过,则忽略),这里的查找规则跟上面查找BeanDefinitionRegistryPostProcessor一样,先找PriorityOrdered,然后是Ordered,最后是两者都没。

ConfigurationClassPostProcessor这个processor是优先级最高的被执行的processor(实现了PriorityOrdered接口)。这个ConfigurationClassPostProcessor会去BeanFactory中找出所有有@Configuration注解的bean,然后使用ConfigurationClassParser去解析这个类。ConfigurationClassParser内部有个Map<ConfigurationClass, ConfigurationClass>类型的configurationClasses属性用于保存解析的类,ConfigurationClass是一个对要解析的配置类的封装,内部存储了配置类的注解信息、被@Bean注解修饰的方法、@ImportResource注解修饰的信息、ImportBeanDefinitionRegistrar等都存储在这个封装类中。

这里ConfigurationClassPostProcessor最先被处理还有另外一个原因是如果程序中有自定义的BeanFactoryPostProcessor,那么这个PostProcessor首先得通过ConfigurationClassPostProcessor被解析出来,然后才能被Spring容器找到并执行。(ConfigurationClassPostProcessor不先执行的话,这个Processor是不会被解析的,不会被解析的话也就不会执行了)。

在我们的程序中,只有主类RefreshContextApplication有@Configuration注解(@SpringBootApplication注解带有@Configuration注解),所以这个配置类会被ConfigurationClassParser解析。解析过程如下:

  1. 处理@PropertySources注解:进行一些配置信息的解析
  2. 处理@ComponentScan注解:使用ComponentScanAnnotationParser扫描basePackage下的需要解析的类(@SpringBootApplication注解也包括了@ComponentScan注解,只不过basePackages是空的,空的话会去获取当前@Configuration修饰的类所在的包),并注册到BeanFactory中(这个时候bean并没有进行实例化,而是进行了注册。具体的实例化在finishBeanFactoryInitialization方法中执行)。对于扫描出来的类,递归解析
  3. 处理@Import注解:先递归找出所有的注解,然后再过滤出只有@Import注解的类,得到@Import注解的值。比如查找@SpringBootApplication注解的@Import注解数据的话,首先发现@SpringBootApplication不是一个@Import注解,然后递归调用修饰了@SpringBootApplication的注解,发现有个@EnableAutoConfiguration注解,再次递归发现被@Import(EnableAutoConfigurationImportSelector.class)修饰,还有@AutoConfigurationPackage注解修饰,再次递归@AutoConfigurationPackage注解,发现被@Import(AutoConfigurationPackages.Registrar.class)注解修饰,所以@SpringBootApplication注解对应的@Import注解有2个,分别是@Import(AutoConfigurationPackages.Registrar.class)和@Import(EnableAutoConfigurationImportSelector.class)。找出所有的@Import注解之后,开始处理逻辑:
  1. 遍历这些@Import注解内部的属性类集合
  2. 如果这个类是个ImportSelector接口的实现类,实例化这个ImportSelector,如果这个类也是DeferredImportSelector接口的实现类,那么加入ConfigurationClassParser的deferredImportSelectors属性中让第6步处理。否则调用ImportSelector的selectImports方法得到需要Import的类,然后对这些类递归做@Import注解的处理
  3. 如果这个类是ImportBeanDefinitionRegistrar接口的实现类,设置到配置类的importBeanDefinitionRegistrars属性中
  4. 其它情况下把这个类入队到ConfigurationClassParser的importStack(队列)属性中,然后把这个类当成是@Configuration注解修饰的类递归重头开始解析这个类
  1. 处理@ImportResource注解:获取@ImportResource注解的locations属性,得到资源文件的地址信息。然后遍历这些资源文件并把它们添加到配置类的importedResources属性中
  2. 处理@Bean注解:获取被@Bean注解修饰的方法,然后添加到配置类的beanMethods属性中
  3. 处理DeferredImportSelector:处理第3步@Import注解产生的DeferredImportSelector,进行selectImports方法的调用找出需要import的类,然后再调用第3步相同的处理逻辑处理

这里@SpringBootApplication注解被@EnableAutoConfiguration修饰,@EnableAutoConfiguration注解被@Import(EnableAutoConfigurationImportSelector.class)修饰,所以在第3步会找出这个@Import修饰的类EnableAutoConfigurationImportSelector,这个类刚好实现了DeferredImportSelector接口,接着就会在第6步被执行。第6步selectImport得到的类就是自动化配置类。

EnableAutoConfigurationImportSelector的selectImport方法会在spring.factories文件中找出key为EnableAutoConfiguration对应的值,有81个,这81个就是所谓的自动化配置类(XXXAutoConfiguration)。

ConfigurationClassParser解析完成之后,被解析出来的类会放到configurationClasses属性中。然后使用ConfigurationClassBeanDefinitionReader去解析这些类。

这个时候这些bean只是被加载到了Spring容器中。下面这段代码是ConfigurationClassBeanDefinitionReader的解析bean过程:

1public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {2  TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();3  for (ConfigurationClass configClass : configurationModel) {4    // 对每一个配置类,调用loadBeanDefinitionsForConfigurationClass方法5    loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);6  }7}89private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,10    TrackedConditionEvaluator trackedConditionEvaluator) {11  // 条件注解判断是否需跳过这个配置类12  if (trackedConditionEvaluator.shouldSkip(configClass)) {13    // 跳过配置类的话在Spring容器中移除bean的注册14    String beanName = configClass.getBeanName();15    if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {16      this.registry.removeBeanDefinition(beanName);17    }18    this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());19    return;20  }2122  if (configClass.isImported()) {23    // 如果自身是被@Import注释所import的,注册自己24    registerBeanDefinitionForImportedConfigurationClass(configClass);25  }26  // 注册方法中被@Bean注解修饰的bean27  for (BeanMethod beanMethod : configClass.getBeanMethods()) {28    loadBeanDefinitionsForBeanMethod(beanMethod);29  }30  // 注册@ImportResource注解注释的资源文件中的bean31  loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());32  // 注册@Import注解中的ImportBeanDefinitionRegistrar接口的registerBeanDefinitions33  loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());34}

invokeBeanFactoryPostProcessors方法总结来说就是从Spring容器中找出BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor接口的实现类并按照一定的规则顺序进行执行。 其中ConfigurationClassPostProcessor这个BeanDefinitionRegistryPostProcessor优先级最高,它会对项目中的@Configuration注解修饰的类(@Component、@ComponentScan、@Import、@ImportResource修饰的类也会被处理)进行解析,解析完成之后把这些bean注册到BeanFactory中。需要注意的是这个时候注册进来的bean还没有实例化。

ConfigurationClassPostProcessor后置器

5 registerBeanPostProcessors

从Spring容器中找出的BeanPostProcessor接口的bean,并设置到BeanFactory的属性。

之后bean被实例化时,会调用这个BeanPostProcessor。

该方法委托给PostProcessorRegistrationDelegate#registerBeanPostProcessors(类似invokeBeanFactoryPostProcessors):

  1. 先找出实现PriorityOrdered接口的BeanPostProcessor,添加到BeanFactory的BeanPostProcessor集合priorityOrderedPostProcessors
  2. 找出实现了Ordered接口的BeanPostProcessor添加到orderedPostProcessorNames集
  3. 两个接口都没实现的BeanPostProcessor加到nonOrderedPostProcessorNames集

这些BeanPostProcessor都由AnnotationConfigUtils#registerAnnotationConfigProcessors注册的。这些BeanPostProcessor包括有:

  • AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
  • RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法)
  • CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等。

如果是自定义的BeanPostProcessor,已经被ConfigurationClassPostProcessor注册到容器内。

这些BeanPostProcessor会在这个方法内被实例化(通过调用BeanFactory的getBean方法,如果没有找到实例化的类,就会去实例化)。

6 initMessageSource

在Spring容器中初始化一些国际化相关的属性。

7 initApplicationEventMulticaster

在Spring容器中初始化事件广播器,事件广播器用于事件的发布。

EventPublishingRunListener这个SpringApplicationRunListener会监听事件,其中发生contextPrepared事件的时候EventPublishingRunListener会把事件广播器注入到BeanFactory。

所以initApplicationEventMulticaster无需再次注册,只需拿出BeanFactory中的事件广播器,然后设置到Spring容器的属性。如未使用SpringBoot,Spring容器得需要自己初始化事件广播器。

8 onRefresh

可以重写以添加特定于上下文的刷新工作的模板方法,在实例化单例之前调用特殊 bean 的初始化。 默认实现为空。

1protected void onRefresh() throws BeansException {2  // For subclasses: do nothing by default.3}

模板方法,不同的Spring容器做不同事。如web程序的容器ServletWebServerApplicationContext调createWebServer创建内置的Servlet容器。

SpringBoot3.x 启动 refresh 过程解析_父类

SpringBoot内置Servlet容器:

SpringBoot3.x 启动 refresh 过程解析_父类_02

9 registerListeners

把Spring容器内的事件监听器和BeanFactory中的事件监听器都添加的事件广播器中。

如存在early event,广播出去。

10 finishBeanFactoryInitialization

实例化BeanFactory中已被注册但未实例化的所有实例(懒加载的无需实例化)。

比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化。

实例化的过程各种BeanPostProcessor开始起作用。

11finishRefresh

refresh做完之后需要做的其他事:

  1. 初始化生命周期处理器,并设置到Spring容器中(LifecycleProcessor)
  2. 调用生命周期处理器的onRefresh方法,这个方法会找出Spring容器中实现了SmartLifecycle接口的类并进行start方法的调用
  3. 发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
  4. 调用LiveBeansView的registerApplicationContext方法:如果设置了JMX相关的属性,则就调用该方法
  5. 发布EmbeddedServletContainerInitializedEvent事件告知对应ApplicationListener进行相应操作

标签:容器,beanFactory,Spring,refresh,bean,SpringBoot3,Import,注解,解析
From: https://blog.51cto.com/JavaEdge/7113623

相关文章

  • Streamlit 讲解专栏(七):解析数据元素
    文章目录1前言2解锁Streamlit中的st.dataframe功能3使用st.data_editor进行数据编辑4定制数据编辑器-使用st.column_config4.1使用st.column_config.Column定制数据编辑器的列4.2使用st.column_config.TextColumn定制数据编辑器的文本列4.3使用st.column_confi......
  • 深入解析Spring Boot自动配置原理
    大家好,我是你的技术达人小助手,在今天的技术博客中,我将带你深入解析SpringBoot自动配置的原理,揭示其神秘面纱,让你能更好地理解和利用这一强大的功能。准备好了吗?让我们一起开始这次关于SpringBoot自动配置的探索之旅吧!背景知识SpringBoot作为一款优秀的Java开发框架,以其方便的配......
  • vue-vuex中使用commit提交mutation来修改state的原因解析
    https://blog.csdn.net/a460550542/article/details/82620457 在vuex中,关于修改state的方式,需要commit提交mutation。官方文档中有这么一句话:更改Vuex的store中的状态的唯一方法是提交mutation。为了搞清楚其原因,查阅了很多资料,发现其它人在做vuex的源码解析的时候,并......
  • 干货满满:多人语音聊天室源码开发解析
    目前,一对一直播源码平台已经不能满足广大社交场景和人群了,而多人语音聊天室源码的开发属性,正好满足此需求,也让社交更加多样化、娱乐化,那么在技术上如何开发多人语音聊天室源码呢?开发语音聊天室的技术关键点如下:1.多人语音频繁麦位切换:抢麦、跳麦、麦位排序、抱麦、上麦、下麦等是典......
  • 华为认证考试每日刷题与解析 Part1
    1、如图所示,路由器RE分别存在去往三个SiteA、SiteB和Site_C三个网段的静态路由,管理员需要配置将Site_A和Site_B路由引入至0SPFDomain中,请将以下配置RE的命令补全。1:______2:______3:______?(英文,全小写)答案:permit,permit,if-match解析:常用命令2、针对MAC地址欺骗atta......
  • 技术中台:概念、重要性及实例解析
    一、概述在当今的数字化时代,企业运营和发展的关键在于如何有效利用技术资源,实现业务与技术的深度融合。在这样的背景下,技术中台作为一种高效的IT架构模式,越来越受到企业的关注和重视。技术中台旨在通过构建稳定、灵活的技术平台,支持业务端的快速创新和迭代,为企业提供可持续发展的动......
  • next.js 源码解析 - getStaticProps、getStaticPaths 篇
    ......
  • 注释解析
    文档注释文档注释是功能级注释,用来说明一个类,一个方法或一个常量的,因此只在上述三个地方使用。文档注释可以使用java自带的命令javadoc来对这个类生成手册。packageapi;​/***文档注释是功能级注释,用来说明一个类,一个方法或一个常量的,因此只在上述三个地方使用。*文档注释......
  • SpringBoot3 学习笔记 (整合Druid)
    一、Druid Github地址:https://github.com/alibaba/druid/二、配置数据源1、在https://mvnrepository.com/artifact/com.alibaba/druid上找最新的版本 2、在pom.xml中添加上Druid数据源依赖<!--https://mvnrepository.com/artifact/com.alibaba/druid--><dependency......
  • SpringBoot3 学习笔记 (整合Mybatis-plus)
    1、引入依赖,网址:https://mvnrepository.com/artifact/com.baomidou 找到mybatis-plus-boot-starter这里最新版本为3.5.3.2,点击进去2、在pom.xml中添加依赖,并确认依赖中已经有了mysql-connector-j的依赖<!--https://mvnrepository.com/artifact/com.baomidou/mybatis-pl......