美文网首页js css html
Springboot/Spring总结

Springboot/Spring总结

作者: 王侦 | 来源:发表于2023-02-13 11:21 被阅读0次

    1.构造SpringApplication对象

      1. 推测web应用类型this.webApplicationType(NONE、REACTIVE、SERVLET)
    • 2.从spring.factories中获取BootstrapRegistryInitializer对象this.bootstrapRegistryInitializers
    • 3.从spring.factories中获取ApplicationContextInitializer对象this.initializers
    • 4.从spring.factories中获取ApplicationListener对象this.listeners
    • 5.推测出Main类(main()方法所在的类)this.mainApplicationClass

    2.SpringApplication#run:发布ApplicationStartingEvent

    • 6.从spring.factories中获取SpringApplicationRunListener对象。默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件

    • 7.listeners.starting(bootstrapContext, this.mainApplicationClass);发布ApplicationStartingEvent

    3.SpringApplication#run:获取各种配置参数

    • 8.将run()的参数封装为DefaultApplicationArguments对象

      1. ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);准备Environment:包括操作系统,JVM、ServletContext、properties、yaml等等配置,会发布一个ApplicationEnvironmentPreparedEvent

    4.SpringApplication#run:根据应用类型创建Spring容器

    • 10.context = createApplicationContext();

    AnnotationConfigServletWebServerApplicationContext

        public AnnotationConfigServletWebServerApplicationContext() {
            this.reader = new AnnotatedBeanDefinitionReader(this);
            this.scanner = new ClassPathBeanDefinitionScanner(this);
        }
    

    完整构造过程:

    • 调用父类GenericApplicationContext的无参构造方法,会构造一个BeanFactory,为DefaultListableBeanFactory
    • 构造AnnotatedBeanDefinitionReader(主要作用添加一些基础的PostProcessor,同时可以通过reader进行BeanDefinition的注册),同时对BeanFactory进行设置和添加PostProcessor(后置处理器)
       1)this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
       2)设置dependencyComparator:AnnotationAwareOrderComparator,它是一个Comparator,是用来进行排序的,会获取某个对象上的Order注解或者通过实现Ordered接口所定义的值进行排序,在日常开发中可以利用这个类来进行排序。
       3)设置autowireCandidateResolver:ContextAnnotationAutowireCandidateResolver,用来解析某个Bean能不能进行自动注入,比如某个Bean的autowireCandidate属性是否等于true
       4)向BeanFactory中添加ConfigurationClassPostProcessor对应的BeanDefinition,它是一个BeanDefinitionRegistryPostProcessor,并且实现了PriorityOrdered接口
       5)向BeanFactory中添加AutowiredAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessorAdapter,MergedBeanDefinitionPostProcessor
       6)向BeanFactory中添加CommonAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessor,InitDestroyAnnotationBeanPostProcessor
       7)向BeanFactory中添加EventListenerMethodProcessor对应的BeanDefinition,它是一个BeanFactoryPostProcessor,SmartInitializingSingleton
       8)向BeanFactory中添加DefaultEventListenerFactory对应的BeanDefinition,它是一个EventListenerFactory
    • 构造ClassPathBeanDefinitionScanner(主要作用可以用来扫描得到并注册BeanDefinition),同时进行设置:
       1)设置this.includeFilters = AnnotationTypeFilter(Component.class)
       2)设置environment
       3)设置resourceLoader

    5.SpringApplication#run:prepareContext

    • 11.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      1)利用ApplicationContextInitializer初始化Spring容器
      2)发布ApplicationContextInitializedEvent
      3)关闭DefaultBootstrapContext
      4)注册primarySources类(BeanDefinition),就是run方法存入进来的配置类
      AnnotatedBeanDefinitionReader#register
      5)发布ApplicationPreparedEvent事件

    6.SpringApplication#run:refreshContext

    • 12.refreshContext(context);刷新Spring容器,会解析配置类、扫描、启动WebServer、创建单例非懒加载的Bean

    6.1 prepareRefresh();

    • initPropertySources();可以允许子容器设置一些内容到Environment中:AbstractRefreshableWebApplicationContext#initPropertySources重写了initPropertySources(),把web.xml中的配置也加载到Environment中。
      -> GenericWebApplicationContext#initPropertySources
      -> StandardServletEnvironment#initPropertySources这里并没有添加。
    • getEnvironment().validateRequiredProperties();校验必须有的环境变量

    6.2 beanFactory = obtainFreshBeanFactory();

    • refreshBeanFactory();
    • getBeanFactory();
      这里返回的是之前创建的DefaultListableBeanFactory

    进行BeanFactory的refresh,在这里会去调用子类的refreshBeanFactory方法,具体子类是怎么刷新的得看子类,然后再调用子类的getBeanFactory方法,重新得到一个BeanFactory(AbstractRefreshableApplicationContext#refreshBeanFactory支持重复refresh(),GenericApplicationContext#refreshBeanFactory不支持重复refresh())

    6.3 prepareBeanFactory(beanFactory);

    • 给当前beanFactory设置一个类加载器,用于加载bd的class信息。
    • 设置Spring EL 表达式解析器StandardBeanExpressionResolver
    • 为propertyEditorRegistrars添加ResourceEditorRegistrar。PropertyEditor类型转化器注册器,用来注册一些默认的PropertyEditor。BeanWrapper 本身就是属性编辑器注册中心,属性编辑器作用于beanWrapper内部管理的真实bean注入字段值时,当某个字段对应的类型 在 BeanWrapper内 有对应的属性编辑器,那么对应类型的字段值 就由该属性编辑器 代理写入。
    • 添加BeanPostProcessor实例:ApplicationContextAwareProcessor,用来执行EnvironmentAware、ApplicationEventPublisherAware等回调方法
    • 忽略指定类型的依赖:EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware,统一放在上面的ApplicationContextAwareProcessor里面处理(注意,这个功能仅限于xml的autowire,@Autowired注解是忽略这个属性的,@Autowired依然会注入)
    • 添加resolvableDependencies:在byType进行依赖注入时,会先从这个属性中根据类型找bean
       1)BeanFactory.class:当前BeanFactory对象
       2)ResourceLoader.class:当前ApplicationContext对象
       3)ApplicationEventPublisher.class:当前ApplicationContext对象
       4)ApplicationContext.class:当前ApplicationContext对象
    • 添加BeanPostProcessor实例:ApplicationListenerDetector(初始化后: 判断当前创建出来的bean实例 是否是 实现了 ApplicationListener 接口的 实例,如果是,当前bean就是一个 事件监听器 对象,需要把监听者注册到容器中。)
    • 注册单例bean到单例池:
       1) "environment":StandardEnvironment对象
       2) "systemProperties":System.getProperties()返回的Map对象
       3) "systemEnvironment":System.getenv()返回的Map对象

    6.4 postProcessBeanFactory(beanFactory);

    留给子类实现

    6.5 invokeBeanFactoryPostProcessors(beanFactory);

    • 1)执行通过ApplicationContext添加进来的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
    • 2)执行BeanFactory中实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法。
      重点:ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry():
       2-1)拿到beanFactory容器里面注册的BeanDefinitions,这里只能拿到传入的配置类(这是重点)以及系统自己注册的那些BeanDefinition(ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等)
       2-2)判断这些BD,哪些是配置类。
      Full配置类:@Configuration注解。
      Lite配置类:@Configuration注解,其proxyBeanMethods为false;有注解@Component、@ComponentScan、@Import、@ImportResource;类里面有加了@Bean注解的方法。
       2-3)对配置类进行排序
       2-4)do ... while(!candidates.isEmpty()) 进行循环解析
       2-4-1)parser.parse(candidates),parser是ConfigurationClassParser
         A)for循环解析candidates
         A-1)this.conditionEvaluator.shouldSkip()处理@Conditional条件注解
         A-2)do...while(sourceClass != null)处理doProcessConfigurationClass,这里循环处理父类
            a)@Component注解,处理内部类
            b)@PropertySource注解,属性配置文件
            c)@ComponentScan、@ComponentScans会构造扫描器进行扫描,遍历扫描得到的BD是否是配置类并进行处理
            d)@Import(三种情况:ImportSelector、ImportBeanDefinitionRegistrar、普通的类(直接当作配置类)),其中DeferredImportSelector,this.deferredImportSelectorHandler.handle()这里只是存了一下。
            e)@ImportResource,导入一个xml配置文件
            f)@Bean方法
            g)处理实现的接口里面的@Bean的default方法
         A-3)this.configurationClasses.put(configClass, configClass)
         B)this.deferredImportSelectorHandler.process();在本轮配置类解析完之后再执行延迟的DeferredImportSelector。
       2-4-2)this.reader.loadBeanDefinitions(configClasses); (生成很多BeanDefinition)将被导入的类@Import以及@Component内部类生成BD注册到Spring容器中;处理@Bean生成的BD;处理@ImportResource导入的xml配置文件;处理@Import导入的ImportBeanDefinitionRegistrar
       2-4-3)筛选出新增的BeanDefinition,并筛选出没有被解析的配置类继续进行处理。@Bean引入的类,其className为null,会判定其不是配置类。

    Mybatis-Spring 1.3.2版本通过@MapperScan导入了MapperScannerRegistrar类,MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,其registerBeanDefinitions()定义了ClassPathMapperScanner,用来扫描Mapper接口生成BeanDefinition(把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byTyp)。
    Mybatis-Spring 2.0.6版本MapperScannerRegistrar#registerBeanDefinitions只是注册了一个新的BD:MapperScannerConfigurer,扫描逻辑放到了这个类中,因为这个类是一个BeanDefinitionRegistryPostProcessor。

    • 3.)执行BeanFactory中实现了Ordered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
    • 4)执行BeanFactory中其他的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
    • 5)执行上面所有的BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法
    • 6)执行通过ApplicationContext添加进来的BeanFactoryPostProcessor的postProcessBeanFactory()方法
    • 7)执行BeanFactory中实现了PriorityOrdered接口的BeanFactoryPostProcessor的postProcessBeanFactory()方法
    • 8)执行BeanFactory中实现了Ordered接口的BeanFactoryPostProcessor的postProcessBeanFactory()方法
    • 9)执行BeanFactory中其他的BeanFactoryPostProcessor的postProcessBeanFactory()方法

    6.6 registerBeanPostProcessors(beanFactory);

    • 1.注册实现PriorityOrdered接口的BeanPostProcessors
    • 2.注册实现Ordered接口的BeanPostProcessors
    • 3.注册普通的BeanPostProcessors
    • 4.重新注册所有MergedBeanDefinitionPostProcessor
    • 5.最后再注册ApplicationListenerDetector

    6.7 initMessageSource();

    如果BeanFactory中存在一个叫做"messageSource"的BeanDefinition,那么就会把这个Bean对象创建出来并赋值给ApplicationContext的messageSource属性,让ApplicationContext拥有国际化的功能

    6.8 initApplicationEventMulticaster();

    • this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); 初始化事件发布器

    6.9 onRefresh();

    ServletWebServerApplicationContext#onRefresh

        protected void onRefresh() {
            super.onRefresh();
            try {
                createWebServer();
            }
            catch (Throwable ex) {
                throw new ApplicationContextException("Unable to start web server", ex);
            }
        }
    

    ServletWebServerApplicationContext#createWebServer

    • 1)创建webServer
    • 2)注册WebServerGracefulShutdownLifecycle
    • 3)注册WebServerStartStopLifecycle,在finishRefresh()中调用WebServerStartStopLifecycle发布WebServerInitializedEvent事件,此时nacos客户端会监听该事件,进行服务实例的注册。
    • 4)initPropertySources()属性源
        private void createWebServer() {
            WebServer webServer = this.webServer;
            ServletContext servletContext = getServletContext();
            if (webServer == null && servletContext == null) {
                ServletWebServerFactory factory = getWebServerFactory();
                this.webServer = factory.getWebServer(getSelfInitializer());
                getBeanFactory().registerSingleton("webServerGracefulShutdown",
                        new WebServerGracefulShutdownLifecycle(this.webServer));
                getBeanFactory().registerSingleton("webServerStartStop",
                        new WebServerStartStopLifecycle(this, this.webServer));
            }
            else if (servletContext != null) {
                try {
                    getSelfInitializer().onStartup(servletContext);
                }
                catch (ServletException ex) {
                    throw new ApplicationContextException("Cannot initialize servlet context", ex);
                }
            }
            initPropertySources();
        }
    

    6.10 registerListeners();

    将ApplicationListener加入到事件发布器里面。

    从BeanFactory中获取ApplicationListener类型的beanName,然后添加到ApplicationContext中的事件广播器applicationEventMulticaster中去,到这一步因为FactoryBean还没有调用getObject()方法生成Bean对象,所以这里要在根据类型找一下ApplicationListener,记录一下对应的beanName

    6.11 finishBeanFactoryInitialization(beanFactory);

    • 实例化非抽象、非懒加载的单例Bean:getBean(beanName)
    • 如果是FactoryBean,则先获取FactoryBean实例本身getBean(& + beanName);
      如果FactoryBean是SmartFactoryBean ,并且isEagerInit为true,则提前初始化FactoryBean管理的Bean:getBean(beanName)。

    AbstractBeanFactory#getBean(beanName)
    -> AbstractBeanFactory#doGetBean核心流程:

    • 1)获取beanName(别名或者&打头->去掉&)
    • 2)到缓存中获取共享单实例,单参数getSingleton,主要就是从三级缓存中获取。
      2-1)先从一级缓存查找singletonObjects;
      2-2)一级缓存没有,且发生循环依赖,则查找二级缓存earlySingletonObjects;
      2-3)二级缓存没有,则加锁(先一级、二级查找)从三级缓存查找singletonFactories。
      SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(),AOP的AnnotationAwareAspectJAutoProxyCreator和事务的InfrastructureAdvisorAutoProxyCreator都是个SmartInstantiationAwareBeanPostProcessor,其getEarlyBeanReference()会调用wrapIfNecessary()创建动态代理。
    • 3)缓存中有对应的数据,此时缓存数据可能是普通单实例 也可能是 FactoryBean,所以需要根据name来进行判断,并且返回数据。
      3-1)如果想拿FactoryBean本身对象,则返回该实例。
      3-2)当前bean实例就是普通单实例,则直接返回该实例。
      3-3)当前bean实例是FactoryBean接口实现类,但是本次请求要拿的是FactoryBean实现类内部管理的实例。调用FactoryBean#getObject()。这些对象是没有经过Bean完整的生命周期的,只是在这里调用了一下初始化后的方法BeanPostProcessor#postProcessAfterInitialization()。
    • 4)缓存中没有bean数据,需要创建了
      4-1)处理原型循环依赖
      4-2)父容器查找
      4-3)获取合并后的BeanDefinition,校验:抽象的BD不能创建实例
      4-4)处理depends-on(会先getBean(dep)依赖的bean),如果产生循环依赖,报错(依靠两个Map,一个map是 dependentBeanMap 另一个是 dependenciesForBeanMap)
      4-5)单例创建,重载getSingleton(beanName, ObjectFactory),ObjectFactory -> createBean,然后还调用getObjectForBeanInstance处理可能是FactoryBean的情况
       A)beforeSingletonCreation(beanName);将当前beanName放入到“正在创建中单实例集合”singletonsCurrentlyInCreation,放入成功,说明没有产生循环依赖,失败,则产生循环依赖,里面会抛异常。这里可以发现构造方法参数产生的循环依赖。
       B)singletonObject = singletonFactory.getObject();这里是创建核心,会调用AbstractAutowireCapableBeanFactory#createBean()。
        B-1)resolveBeanClass(),如果未加载则使用类加载器将该类加载到JVM中
        B-2)mbdToUse.prepareMethodOverrides();对XML标签中定义的lookUp属性进行预处理,如果只能根据名字找到一个就标记为非重载的,这样在后续就不需要去推断到底是哪个方法了,对于@LookUp注解标注的方法是不需要在这里处理的,AutowiredAnnotationBeanPostProcessor会处理这个注解
        B-3)resolveBeforeInstantiation()短路操作。InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation。如果不为null,经过BeanPostProcessor#postProcessAfterInitialization()后会直接返回。
        B-4)doCreateBean()核心方法:创建bean实例对象,并且生命周期的动作大部分都在这里。
       C)afterSingletonCreation(beanName);从singletonsCurrentlyInCreation移除
       D)addSingleton(beanName, singletonObject);将实例bean加入到一级缓存,从二级、三级缓存移除。
      4-6)原型创建
      4-7)其他情况实例创建
    • 5)创建的实例是否与requiredType类型相匹配
    • 6)返回实例

    着重看一下AbstractAutowireCapableBeanFactory#doCreateBean主流程:

    • 1)createBeanInstance(),创建出真实的bean实例,并且将其包装到BeanWrapper实例中。
       1-1)InstanceSupplier创建实例
       1-2)instantiateUsingFactoryMethod(),工厂方法创建实例,分为静态工厂方法跟实例工厂方法
        step1.新建一个BeanWrapper,并对这个BeanWrapper做了初始化
        step2.明确了实例化当前这个Bean到底是静态工厂还是实例工厂(factoryBeanName 不为空)
        step3.从缓存(原型模式有用)、类中获取对应了方法以及参数(筛选出所有候选方法并排序: 优先级:public > 非public,参数多 > 参数少)
        step4.明确了方法需要的最小的参数数量并对配置文件中的标签属性进行了一次解析,然后从解析出来的参数中查找当前的这个候选方法需要的参数或者自动注入
        step5.计算类型差异(首先判断bd中是宽松模式还是严格模式,目前看来只有@Bean标注的方法解析得到的Bean会使用严格模式来计算类型差异,其余都是使用宽松模式),筛选出差异最小的方法
        step6.对推断出来的方法做验证(不能为null,方法不能有多个,返回值不能为void)
        step7.反射调用对应方法进行实例化
       1-3)构造方法以及解析过,如果构造方法需要自动注入参数,则调用autowireConstructor(beanName, mbd, null, null),否则无参构造方法处理instantiateBean(beanName, mbd)。
       1-4)ctors = determineConstructorsFromBeanPostProcessors(),典型的应用:@Autowired 注解打在了 构造器方法上。参考@Autowired AutowiredAnnotationBeanPostProcessor
       1-5)条件一:ctors不为null:说明存在1个或多个@Autowired标注的方法。条件二:autowiredMode为AUTOWIRE_CONSTRUCTOR 。条件三:配置文件中,bean信息配置了 构造参数信息。条件四:getBean时,args有参数。这四个条件任意一个成立,则调用autowireConstructor(beanName, mbd, ctors, args),选择一个差异值最小的,参数最长的构造函数
       1-6)有偏好选用的构造方法 mbd.getPreferredConstructors(),调用autowireConstructor(beanName, mbd, ctors, null);
       1-7)instantiateBean(beanName, mbd);未指定构造参数,未设定偏好...使用默认的 无参数的构造方法进行创建实例
    • 2)applyMergedBeanDefinitionPostProcessors(),MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition()。
       2-1)AutowiredAnnotationBeanPostProcessor会提取注入点:提取出来当前beanType类型整个继承体系内的 @Autowired @Value @Inject 信息 并且包装成一个InjectionMetadata的一个对象,存放到 AutowiredAnnotationBeanPostProcessor 它的缓存中了,key是 beanName。
    • 3)循环依赖的相关操作:addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
       getEarlyBeanReference()会调用SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference()
    • 4)populateBean(),依赖注入
       4-1)InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
       4-2)autowireMode为AUTOWIRE_BY_NAME时,autowireByName();autowireMode为AUTOWIRE_BY_TYPE时,autowireByType()。
       autowireByName(),首先找到需要进行依赖注入的属性名,有setter方法,不是简单类型(基本数据类型、枚举、日期等),然后直接调用getBean(propertyName)获取实例进行依赖注入。
       autowireByType(),首先找到需要进行依赖注入的属性名,有setter方法,不是简单类型(基本数据类型、枚举、日期等),然后调用resolveDependency()解析依赖。
        step1.通过依赖类型查询到所有的类型匹配的bean的名称
        step2.如果找到了多个的话,再根据依赖的名称匹配对应的Bean的名称
        step3.调用getBean得到这个需要被注入的Bean
        step4.最后反射调用字段的set方法完成属性注入
       4-3)InstantiationAwareBeanPostProcessor#postProcessProperties,属性注入,典型应用:@Autowired 注解的注入(AutowiredAnnotationBeanPostProcessor )。
       4-4)对需要进行依赖检查的属性进行依赖检查checkDependencies()
       4-5)将依赖注入合并后的pvs 应用到 真实的Bean实例中。applyPropertyValues(),属性来源:XML配置,byName自动注入的,byType自动注入的。
    • 5)initializeBean(),调用初始化方法
       5-1)invokeAwareMethods(beanName, bean),BeanNameAware、BeanClassLoaderAware和BeanFactoryAware处理。
       5-2)applyBeanPostProcessorsBeforeInitialization(),BeanPostProcessor#postProcessBeforeInitialization()
       5-3)invokeInitMethods(),首先调用InitializingBean#afterPropertiesSet(),如果没有实现接口,则可invokeCustomInitMethod()调用init-method定义的方法
       5-4)applyBeanPostProcessorsAfterInitialization(),BeanPostProcessor#postProcessAfterInitialization()
        AOP功能实现AnnotationAwareAspectJAutoProxyCreator(@EnableAspectJAutoProxy注解或者<aop:aspectj-autoproxy />引入)是个BeanPostProcessor。其postProcessAfterInitialization()会调用wrapIfNecessary()创建动态代理。
        事务功能实现InfrastructureAdvisorAutoProxyCreator(@EnableTransactionManagement注解或者<tx:annotation-driven>引入)是个BeanPostProcessor。其postProcessAfterInitialization()会调用wrapIfNecessary()创建动态代理。
    • 6)循环依赖相关处理
    • 7)注册销毁方法

    6.12 finishRefresh();

    • initLifecycleProcessor();设置this.lifecycleProcessor为DefaultLifecycleProcessor
    • getLifecycleProcessor().onRefresh();
      autoStartupOnly为true:表示只启动 SmartLifecycle 生命周期对象,为false表示全部启动,包括普通的Lifecycle。
      根据phase值,对SmartLifecycle 对象分组,值越小,越先启动。
      nacos 客户端jar包引入的自动配置类NacosServiceRegistryAutoConfiguration注入NacosAutoServiceRegistration,NacosAutoServiceRegistration是个ApplicationListener,监听WebServerInitializedEvent事件。WebServerStartStopLifecycle是个SmartLifecycle,在refresh()的最后会调用其start()方法,会发布ServletWebServerInitializedEvent事件。此时NacosAutoServiceRegistration#onApplicationEvent就会被调用,进行服务实例注册。
    • 发布ContextRefreshedEvent事件

    7.SpringApplication#run:后置处理

    • 13.发布ApplicationStartedEvent事件,表示Spring容器已经启动
    • 14.从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
    • 15.发布ApplicationReadyEvent事件,表示Spring容器已经准备好了

    相关文章

      网友评论

        本文标题:Springboot/Spring总结

        本文链接:https://www.haomeiwen.com/subject/kfvckdtx.html