Spring boot启动原理及相关组件

作者:q***3851日期:2025/11/16

优质博文:IT-BLOG-CN

一、Spring Boot应用启动

一个Spring Boot应用的启动通常如下:

1@SpringBootApplication
2@Slf4j
3public class ApplicationMain {
4    public static void main(String[] args) {
5        ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args);
6    }
7}
8

执行如上代码,Spring Boot程序启动成功。事实上启动Spring Boot应用离不开SpringApplication。
所以,我们跟随SpringApplication的脚步,开始从源码角度分析Spring Boot的初始化过程。

btw,可参看例子一节,我对Spring Boot启动的拓展点都做了demo,可参照下面源码分析进行理解。

文档有一句话说了SpringApplication做了什么(目的):

1Create 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
2

二、SpringApplication构造函数

启动代码先创建SpringApplication示例,在执行run方法:

1public static ConfigurableApplicationContext run(Class<?>[] primarySources,
2        String[] args) {
3    return new SpringApplication(primarySources).run(args);
4}
5

如下是SpringApplication的构造函数代码分析。

1this.resourceLoader = resourceLoader;
2Assert.notNull(primarySources, "PrimarySources must not be null");
3this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
4//通过Classloader探测不同web应用核心类是否存在,进而设置web应用类型
5this.webApplicationType = WebApplicationType.deduceFromClasspath(); 
6//找出所有spring.factories中声明的ApplicationContextInitializer并设置,
7//ApplicationContextInitializer定义了回调接口,在refresh()前初始化调用(即在prepareContext的applyInitializers方法中调用)
8setInitializers((Collection) getSpringFactoriesInstances(
9ApplicationContextInitializer.class));
10//找出所有spring.factories中声明的ApplicationListener(细节往后再叙),ApplicationListener继承了
11//java.util.EventListener,实现了类似观察者模式的形式,通过实现ApplicationListener、SmartApplicationListener,能够监听Spring上下文的refresh、Prepared等事件或者是自定义事件
12setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
13//找出主启动类(有趣的是,是通过new一个runtime异常然后在异常栈里面找出来的)
14this.mainApplicationClass = deduceMainApplicationClass();
15

在构造期间,主要做了:
1、判定应用类型,为后面创建不同类型的spring context做准备。
2、初始化ApplicationContextInitializer和ApplicationListener。
3、找出启动类。

三、run()源码解析

介绍run()方法前,先说说贯穿run方法的ApplicationRunListener,它有助于理解整个run()的运行周期。
写在这里:Spring Application事件机制

run()方法分析如下:

1//java.awt.headless,是J2SE的一种模式,用于在缺失显示屏、鼠标或者键盘时的系统配置。
2configureHeadlessProperty();
3//将spring.factories中的SpringApplicationRunListener接口实现类拖出来,塞到SpringApplicationRunListeners(一个集合)中,统一批量执行
4SpringApplicationRunListeners listeners = getRunListeners(args);
5//触发runlistener的starting
6listeners.starting();
7try {
8   ApplicationArguments applicationArguments = new DefaultApplicationArguments(
9         args);
10   ConfigurableEnvironment environment = prepareEnvironment(listeners,
11         applicationArguments);
12   //spring.beaninfo.ignore如果没有设置值,则把它设为true,具体情况具体设置,
13   //如果没用的话,把它设为true可以ignore掉classloader对于不存在的BeanInfo的扫描,提高性能。
14   configureIgnoreBeanInfo(environment);
15   //banner打印。自定义banner挺好玩的
16   Banner printedBanner = printBanner(environment);
17   //根据webApplicationType(一开始推断的应用类型)去新建applicationContext
18   context = createApplicationContext();
19   //获取SpringBootExceptionReporter,回调接口类,提供启动时的异常报告
20   exceptionReporters = getSpringFactoriesInstances(
21         SpringBootExceptionReporter.class,
22         new Class[] { ConfigurableApplicationContext.class }, context);
23   //下面会说
24   prepareContext(context, environment, listeners, applicationArguments,
25         printedBanner);
26   refreshContext(context);
27   //do nothing
28   afterRefresh(context, applicationArguments);
29   //计时停止
30   stopWatch.stop();
31   //打日志
32   if (this.logStartupInfo) {
33      new StartupInfoLogger(this.mainApplicationClass)
34            .logStarted(getApplicationLog(), stopWatch);
35   }
36   //启动
37   listeners.started(context);
38   //找出context的ApplicationRunner和CommandLineRunner,用AnnotationAwareOrderComparator排序,并执行
39   callRunners(context, applicationArguments);
40

下面再分别说说两个方法(prepareEnvironment、refreshContext)的代码。

四、prepareEnvironment

1private ConfigurableEnvironment prepareEnvironment(
2      SpringApplicationRunListeners listeners,
3      ApplicationArguments applicationArguments) {
4   // Create and configure the environment
5   ConfigurableEnvironment environment = getOrCreateEnvironment();
6   configureEnvironment(environment, applicationArguments.getSourceArgs());
7   //发布environment prepared事件
8   listeners.environmentPrepared(environment);
9   //将获取到的environment中的spring.main配置绑定到SpringApplication中,
10   //使用的是Binder这个spring boot2.0开始有的类
11   bindToSpringApplication(environment);
12   if (!this.isCustomEnvironment) {
13      environment = new EnvironmentConverter(getClassLoader())
14            .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
15   }
16   //附加的解析器将动态跟踪底层 Environment 属性源的任何添加或删除,
17   //关于ConfigurationPropertySourcesPropertySource和MutablePropertiySource
18   //将在Environment中作进一步讲解
19   ConfigurationPropertySources.attach(environment);
20   return environment;
21}
22

五、prepareContext

1private void prepareContext(ConfigurableApplicationContext context,
2      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
3      ApplicationArguments applicationArguments, Banner printedBanner) {
4   //为上下文设置environment(配置、profile)
5   context.setEnvironment(environment);
6   //对application做一些处理,设置一些组件,
7   //比如BeanNameGenerator,ApplicationConversionService(包含一些默认的Converter和formatter)
8   postProcessApplicationContext(context);
9   // 加载并运行ApplicationContextInitializer
10   applyInitializers(context);
11   listeners.contextPrepared(context);
12   if (this.logStartupInfo) {
13      logStartupInfo(context.getParent() == null);
14      logStartupProfileInfo(context);
15   }
16   // Add boot specific singleton beans
17   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
18   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
19   if (printedBanner != null) {
20      beanFactory.registerSingleton("springBootBanner", printedBanner);
21   }
22   if (beanFactory instanceof DefaultListableBeanFactory) {
23      ((DefaultListableBeanFactory) beanFactory)
24            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
25   }
26   // Load the sources
27   Set<Object> sources = getAllSources();
28   Assert.notEmpty(sources, "Sources must not be empty");
29   //Load beans(其实是由sources构建beanDefinition) into the application context. 
30   //构建BeanDefinitionLoader并执行BeanDefinitionLoader.load()
31   load(context, sources.toArray(new Object[0]));
32   //执行contextLoaded事件
33   listeners.contextLoaded(context);
34}
35

六、容器refresh(refreshContext)

1private void refreshContext(ConfigurableApplicationContext context) {
2    refresh(context);
3    if (this.registerShutdownHook) {
4        try {
5            context.registerShutdownHook();
6        }
7        catch (AccessControlException ex) {
8            // Not allowed in some environments.
9        }
10    }
11}
12

refreshContext会做两件事,
1、应用上下文刷新
2、注册shutdown钩子

我们来看看ServletWebServer的刷新。

1// ServletWebServerApplicationContext 
2public final void refresh() throws BeansException, IllegalStateException {
3   try {
4      super.refresh();
5   }
6   catch (RuntimeException ex) {
7      //停止webserver
8      stopAndReleaseWebServer();
9      throw ex;
10   }
11}
12
13
14org.springframework.context.support.AbstractApplicationContext refresh()
15public void refresh() throws BeansException, IllegalStateException {
16   // 单线程执行
17   synchronized (this.startupShutdownMonitor) {
18      // Prepare this context for refreshing.
19      // 1、设置Spring容器的启动时间,撤销关闭状态,开启活跃状态。2、初始化属性源信息(Property)3、验证环境信息里一些必须存在的属性
20      prepareRefresh();
21      // Tell the subclass to refresh the internal bean factory.
22      // 如果是RefreshtableApplicationContext会做了很多事情:
23      // 1、让子类刷新内部beanFactory ,创建IoC容器(DefaultListableBeanFactory--ConfigurableListableBeanFactory 的实现类)
24      // 2、加载解析XML文件(最终存储到Document对象中)
25      // 3、读取Document对象,并完成BeanDefinition的加载和注册工作
26      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
27      // Prepare the bean factory for use in this context.
28      //从Spring容器获取BeanFactory(Spring Bean容器)并进行相关的设置为后续的使用做准备:
29      //1、设置classloader(用于加载bean),设置表达式解析器(解析bean定义中的一些表达式),添加属性编辑注册器(注册属性编辑器)
30      //2、添加ApplicationContextAwareProcessor这个BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入。因为ApplicationContextAwareProcessor把这5个接口的实现工作做了
31      //3、设置特殊的类型对应的bean。BeanFactory对应刚刚获取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器
32      //4、注入一些其它信息的bean,比如environment、systemProperties等
33      prepareBeanFactory(beanFactory);
34      try {
35         // Allows post-processing of the bean factory in context subclasses.
36         postProcessBeanFactory(beanFactory);
37         // Invoke factory processors registered as beans in the context. 执行beanfactoryPostProcessor
38         invokeBeanFactoryPostProcessors(beanFactory);
39         // Register bean processors that intercept bean creation. 
40         // 注册beanPostProcessor
41         registerBeanPostProcessors(beanFactory);
42         // Initialize message source for this context. 初始化messageSource
43         initMessageSource();
44         // Initialize event multicaster for this context.
45         initApplicationEventMulticaster();
46         // Initialize other special beans in specific context subclasses.
47         onRefresh();
48         // Check for listener beans and register them.
49         registerListeners();
50         // Instantiate all remaining (non-lazy-init) singletons.
51         finishBeanFactoryInitialization(beanFactory);
52         // Last step: publish corresponding event.
53         finishRefresh();
54      } catch (BeansException ex) {
55         if (logger.isWarnEnabled()) {
56            logger.warn("Exception encountered during context initialization - " +
57                  "cancelling refresh attempt: " + ex);
58         }
59         // Destroy already created singletons to avoid dangling resources.
60         destroyBeans();
61         // Reset 'active' flag.
62         cancelRefresh(ex);
63         // Propagate exception to caller.
64         throw ex;
65      }
66
67      finally {
68         // Reset common introspection caches in Spring's core, since we
69         // might not ever need metadata for singleton beans anymore...
70         resetCommonCaches();
71      }
72   }
73}
74

七、postProcessBeanFactory()

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

不同的Context会进行不同的操作。 比如,AnnotationConfigServletWebServerApplicationContext

1protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
2    // 父类实现,会注册web应用特有的factory scope, 
3    super.postProcessBeanFactory(beanFactory);
4    //查看basePackages属性,如果设置了会使用ClassPathBeanDefinitionScanner去扫描basePackages包下的bean并注
5    if (this.basePackages != null && this.basePackages.length > 0) {
6        this.scanner.scan(this.basePackages);
7    }
8    // 查看annotatedClasses属性,如果设置了会使用AnnotatedBeanDefinitionReader去注册这些bean
9    if (!this.annotatedClasses.isEmpty()) {
10        this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
11    }
12}
13

八、invokeBeanFactoryPostProcessors()

1/**
2 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
3 * respecting explicit order if given.
4 * <p>Must be called before singleton instantiation.
5 */
6protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
7    //执行AbstractContext持有的BeanFactory后置处理器
8    //这些处理器是之前ContextInitializer
9    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
10
11    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
12    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
13    // 如果通过-javaagent参数设置了LTW的织入器类包,那么增加LTW的BeanProcessor。
14    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
15        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
16        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
17    }
18}
19

从容器中找出BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor(二者的区别是,一个使用BeanDefinitionRegistry作处理,一个使用BeanFactory做处理), 并按一定的规则顺序执行。

ConfigurationClassPostProcessor的优先级为最高,它会对项目中的@Configuration注解修饰的类(@Component、@ComponentScan、@Import、@ImportResource修饰的类也会被处理)进行解析,解析完成之后把这些bean注册到BeanFactory中。 需要注意的是这个时候注册进来的bean还没有实例化。

ConfigurationClassPostProcessor的流程之后会独立进行分析。

九、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);
}

1从Spring容器中按一定顺序(PriorityOrdered、Ordered、非PriorityOrdered非Ordered)找出实现了BeanPostProcessor接口的bean,并设置到BeanFactory的属性中。之后bean被实例化的时候会调用这个BeanPostProcessor。
2
3## 十、initMessageSource()
4初始化一些国际化相关的属性。
5
6Spring boot的国际化配置可阅读MessageSourceAutoConfiguration。 默认情况会设置一个DelegatingMessageSource,是一个空实现,因为ApplicationContext接口拓展了MessageSource接口,所以Spring容器都有getMessage方法, 可是,在实现上又允许空MessageSource,所以,通过一个DelegatingMessageSource去适配。
7
8## 十一、initApplicationEventMulticaster()
9Initialize event multicaster for this context. 初始化事件广播器。默认实现是SimpleApplicationEventMulticaster。
10
11onRefresh()
12模板方法,给不同的Spring应用容器去实例化一些特殊的类。
13
14比如,AnnotationConfigServletWebServerApplicationContext、AnnotationConfigReactiveWebServerApplicationContext会去创建web server(createWebServer())。 spring boot的mvc内置支持有tomcat、Undertow、jetty三种server,而reactive web server则内置支持tomcat、jetty、netty三种。
15java
16// Unlike Jetty, all Tomcat threads are daemon threads. We create a
17// blocking non-daemon to stop immediate shutdown
18startDaemonAwaitThread();
19

btw,如果是tomcat server的话,spring boot会启动多一个线程防止退出。

十二、registerListeners()

把BeanFactory的ApplicationListener拿出来塞到事件广播器里。

如果ApplicationContext的earlyApplicationEvents属性有值,则广播该属性持有的early事件。

十三、finishBeanFactoryInitialization(beanFactory)

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

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

十四、finishRefresh()

1// ReactiveWebServerApplicationContext
2    @Override
3    protected void finishRefresh() {
4        super.finishRefresh();
5        WebServer webServer = startReactiveWebServer();
6        if (webServer != null) {
7            publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));
8        }
9    }
10
11// AbstractApplicationContext
12    /**
13     * Finish the refresh of this context, invoking the LifecycleProcessor's
14     * onRefresh() method and publishing the
15     * {@link org.springframework.context.event.ContextRefreshedEvent}.
16     */
17    protected void finishRefresh() {
18        // Clear context-level resource caches (such as ASM metadata from scanning).
19        // 容器完成刷新,清除资源缓存
20        clearResourceCaches();
21
22        // Initialize lifecycle processor for this context.
23        // 初始化lifeCycleProcessor, 默认实现是DefaultLifeCycleProcessor,实现了BeanFactoryAware接口,通过BeanFactory找出LifeCycle bean
24        // 可通过自定义实现LifeCycle接口的Bean,来监听容器的生命周期。
25        initLifecycleProcessor();
26
27        // Propagate refresh to lifecycle processor first.
28        //粗发生命周期处理器的onRefresh方法,顺带一说,在程序正常退出时,会粗发shutdownHook,那时会粗发生命周期处理器的onClose方法
29        getLifecycleProcessor().onRefresh();
30
31        // Publish the final event.
32        // 广播ContextRefreshed事件
33        publishEvent(new ContextRefreshedEvent(this));
34
35        // Participate in LiveBeansView MBean, if active.
36        // 将ApplicationContext注册到Spring tool suite里
37        LiveBeansView.registerApplicationContext(this);
38    }
39

十五、resetCommonCaches()

1// Reset common introspection caches in Spring's core, since we
2// might not ever need metadata for singleton beans anymore...
3resetCommonCaches();
4

最后会在finally执行resetCommonCaches(),清除一些Spring core、beans加载和解析的Bean信息缓存(因为对于singleton bean来说已经不需要了)。

十六、流程整理

最后,按照启动阶段整理一幅全景图。
在这里插入图片描述

十七、例子

在github里,我把Spring Boot应用启动的拓展组件(自定义的应用初始器、监听器、事件、ApplicationRunner)都写了例子,可参照阅读。 代码在这 | spring-boot-none-startup

日志如下:

12020-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]'}]}
2
3  .   ____          _            __ _ _
4 /\ / ___'_ __ _ _(_)_ __  __ _    
5

( ( )___ | '_ | '| | ’ / ` | / )| |)| | | | | || (| | ) ) ) ) ’ | | .__|| ||| |_ , | / / / / =========||==============| /=///_/
:: Spring Boot :: (v2.1.3.RELEASE)

12020-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
22020-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 )
32020-05-20 18:30:11.838  INFO 81568 --- [           main] n.t.d.s.b.startup.none.ApplicationMain   : The following profiles are active: dev
42020-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
52020-05-20 18:30:12.404  INFO 81568 --- [           main] .s.b.s.n.s.SimpleApplicationContextAware : SimpleApplicationContextAware and send SimpleAppEvent
62020-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
72020-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]
82020-05-20 18:30:12.484  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.l.LoggingLifeCycle       : In Life cycle bean start().
92020-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)
102020-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
112020-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
122020-05-20 18:30:12.497  INFO 81568 --- [           main] n.t.d.s.b.s.n.s.r.EchoCommandLineRunner  : EchoCommandLineRunner running
132020-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
142020-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().
15

Spring boot启动原理及相关组件》 是转载文章,点击查看原文


相关推荐


UDP服务端绑定INADDR_ANY后,客户端该用什么IP访问?
咸鱼_要_翻身2025/11/15

目录 一、问题 二、详细解释 1、INADDR_ANY 到底是什么? 2、客户端可以使用什么IP访问? 三、为什么要传IP? 1、网络层寻址的需要 2、操作系统协议栈的需要 3、服务端主机区分流量的需要 四、总结 一、问题         在UDP协议中,服务端使用INADDR_ANY了,然后客户端可以使用什么IP可以访问服务端?为什么要传IP?这是一个非常经典且重要的问题。我们来分步拆解和解答。 核心答案:当UDP服务端绑定到 INADDR_ANY (其值通常是


【Kubernetes】K8s 集群 Ingress 入口规则
陈陈CHENCHEN2025/11/14

Ingress 是管理从集群外部访问集群内部服务的入口规则(HTTP 和 HTTPS)的 API 对象,核心功能是将外部到达集群的 HTTP/HTTPS 请求,根据主机名(host)和路径(path)转发到不同的后端 Service,一起来看看如何使用吧! 1、概念 1.1、Ingress(资源) 在 K8s 中创建的 YAML 文件,只是一份规则声明,定义 “当访问 a.example.com 时,将流量转发给 service-a” 之类的规则 1.2、Ingress Control


11种方法解决iPhone上共享相册不显示的问题[2025]
Digitally2025/11/12

如果你想查看或下载朋友或家人通过iCloud链接邀请与你共享的照片,你可以利用iPhone上的共享相册功能。然而,有时这个看似无缝的过程会出现问题,让用户不禁想问:“为什么我没有收到共享相册邀请?”或“如何修复共享相册不显示”。好的,本页面致力于揭示背后的原因,并提供有效的解决方法,确保你的共享相册体验保持完美。但在那之前,让我们先简要了解一下“共享相册”。 第一部分:什么是共享相册?如何接受共享相册邀请? 什么是共享相册?“共享相册”是一个在线空间,多人可以在此添加、查看和互动照片和视频。


Java 17 密封类(Sealed Classes)实战:从类型安全到架构解耦的范式升级
听风吟丶2025/11/10

在 Java 17 之前,面向对象编程中的 “类继承” 一直存在一个痛点:一个公开类(public class)可以被任何其他类无限制继承,导致类型体系失控、代码维护成本升高。例如,定义一个 “支付方式” 基类后,开发者可能会随意继承出不符合业务逻辑的子类(如 “现金支付” 继承 “电子支付”),破坏类型体系的完整性。为解决这一问题,Java 17 正式将密封类(Sealed Classes) 纳入标准特性,通过 “显式指定子类范围”“限制继承权限”“强制类型覆盖”,从语言层面实现了类型体系的可


网络协议之传统DNS存在的问题以及httpdns
小董亮出你的8块腹肌吧!2025/11/8

写在前面 传统的DNS虽然使用的非常广泛,但是也是存在一些问题的,本文就来一起来看下,以及其解决方案http dns。 1:传统DNS存在的问题 1.1:域名缓存问题 用户某次访问时,可能已经有了距离用户更近的服务IP,但是因为还是使用缓存的那个服务IP(注意并不是不能用了),所以就无法拿到最优的那个IP了。 1.2:域名转发问题 存在这个问题的原因是,有些运营商,自己并不会取获取DNS信息,而是将这个工作转发到其他的运营商来做,而DNS服务器来返回IP地址时会根据当前请求者所在的运


Python编程实战 · 基础入门篇 | 集合(set)
程序员爱钓鱼2025/11/6

在 Python 的基础数据结构中,除了列表(list)、元组(tuple)、字典(dict),还有一种特殊但非常实用的数据类型——集合(set)。 集合的最大特点是:不重复、无序、可进行集合运算。 它常用于去重、数据筛选、关系判断等场景,是数据处理中的高效利器。 一 什么是集合(set) 集合(Set)是一种 无序、可变 的数据容器, 用于存放 不重复的元素。 在 Python 中,可以通过 {} 或 set() 创建集合。 fruits = {"apple", "banana", "ch


(3)100天python从入门到拿捏
硅基工坊2025/11/1

Python3 基本数据类型 Python 中的变量不需要声明,它没有类型,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建 文章目录 Python3 基本数据类型一、数据类型介绍1. 数值类型(Numeric Types)1.1 整数(int)1.2 浮点数(float)1.3 复数(complex) 2. 字符串类型(String)3. 布尔类型(Boolean)4. 列表类型(List)5. 元组类型(Tuple)6. 字典类型(Dictionary)


数据库连接详解:从基础到实践
培风图南以星河揽胜2025/10/30

数据库连接详解:从基础到实践 在现代软件开发中,数据库是存储和管理数据的核心组件。无论是Web应用、移动应用还是数据分析系统,都离不开与数据库的交互。而这一切的基础,就是数据库连接。本文将深入浅出地介绍数据库连接的基本概念、原理、实现方式以及最佳实践,帮助你构建高效、稳定的数据库访问层。 1. 什么是数据库连接? 简单来说,数据库连接(Database Connection) 是应用程序与数据库服务器之间建立的一个通信通道。通过这个通道,应用程序可以发送SQL查询、接收查询结果、执行事


【案例实战】智能出行导航助手HarmonyOS 开发全流程复盘
cooldream20092025/10/27

目录 前言1. 项目概述与目标设定1.1 项目背景1.2 技术选型与总体方案 2. 架构设计:分布式与模块化融合2.1 设计思路2.2 模块化的实践价值 3. HarmonyOS 开放能力集成实战3.1 云开发(Cloud Development)3.2 性能监控与调优(APMS)3.3 分布式软总线:多端协同核心 4. 性能优化体系建设4.1 启动优化分层策略4.2 内存与功耗控制4.3 云函数响应优化 5. 经验复盘与开发心得5.1 架构先行,分布式思维贯穿始终5.2 善用


vue.js 视频截取为 gif - 2(将截取到的gif 转换为base64 、file)
改了一个昵称2025/10/25

demo.vue <template> <div> <div> <video ref="videoRef" :src="theUrl" controls autoplay muted crossOrigin="anonymous"></video> <!-- <div class="controls"> <button :disabled="isRecording" @click="startCapture"> 开始录制

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0