Spring Boot原理分析(一):项目启动流程、自动装配

spring bean的简介以及手动装配、自动装配、注解装配且包含实际demo (手动装配+xml): java中定义class,xml定义bean,class是逻辑,bean是注入数据,通过ref指定bean与bean之间的关联关系(自动装配+xml): 通过spring自动扫描代替手动ref指定bean和bean之间的关系,可以设定映射方式是byName还是byType(注解装配+xml): xml只配置支持注解和扫描的bean所在的包,bean的定义不在xml中。java中不仅定义class,还通过注解定义bean实例。因此class既有逻辑,也有数据。 阅读详情

本文章是Spring Boot原理分析系列博客的第一篇,将会介绍一些准备工作和Spring Boot的项目启动的原理和源码。

一、项目启动流程

  1. 加载Spring Boot的启动类: Spring Boot应用程序的入口点是一个带有@SpringBootApplication注解的Java类。该注解会触发Spring Boot的自动配置和组件扫描。

  2. 创建Spring应用程序上下文: 在启动类中,使用SpringApplication.run()方法创建一个Spring应用程序上下文。该方法会加载所有的配置、组件和依赖,并创建一个可用于管理应用程序的上下文对象。

  3. 执行自动装配: Spring Boot会根据类路径上的依赖和配置,自动配置应用程序的各个组件。它会根据约定和默认值,自动配置数据库连接、Web服务器、日志记录等功能。

  4. 启动内嵌的Web服务器: 如果应用程序是一个Web应用程序,Spring Boot会自动启动一个内嵌的Web服务器(如Tomcat、Jetty等),并将应用程序部署到该服务器上。

  5. 运行应用程序: 一旦所有的配置和组件都加载完毕,Spring Boot会调用应用程序的主要方法,开始执行应用程序的业务逻辑。

二、SpringBootApplication.java源码解析

在这里插入图片描述

1.准备工作

  • 环境:
jdk 1.8 + Maven 3.3.9或以上版本 + IDEA
SpringBoot 2.7.13
  • 启动类:
    创建项目后会自动创建一个启动类,运行main方法就可以启动项目,代码如下:
package tracy.springbootcode;

@SpringBootApplication
public class SpringBootCodeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootCodeApplication.class, args);
    }

}

可以说,@SpringBootApplication是其中最关键的东西。

2.源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};


	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};
	
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;
}

3.自定义注解

关于自定义注解的相关知识可以先看我这篇博客了解一下。

以下四个元注解用于定义@SpringBootApplication这个注解:

@Target(ElementType.TYPE)//注解的作用目标是类、接口(包括注解类型)或枚举声明。
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited//此注解可被继承

4.组合注解

@SpringBootApplication是一个组合注解,它等价于同时标注 @Configuration + @EnableAutoConfiguration + @ComponentScan 。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	...
}

5.注解@ComponentScan

可以指定包扫描的根路径,让 SpringFramework 来扫描指定包及子包下的组件;也可以不指定路径,默认扫描当前配置类所在包及子包里的所有组件,所以启动类才会放到所有类所在包的最外层。

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

过滤器

这段代码是使用Spring框架中的注解来指定Spring Boot自动配置时需要排除的类。

具体来说,这里使用了两个@Filter注解,分别指定了需要排除的类的类型以及具体的类名:

  • 第一个@Filter注解的type属性值为FilterType.CUSTOM,表示需要使用自定义的过滤器来进行过滤;
    classes属性指定了具体的自定义过滤器类,这里是TypeExcludeFilter.class。 它会从 BeanFactory (可以暂时理解成IOC容器)中获取所有类型为 TypeExcludeFilter 的组件,去执行自定义的过滤方法。由此可见,TypeExcludeFilter 的作用是做扩展的组件过滤。

  • 第二个@Filter注解同样是type属性值为FilterType.CUSTOM,表示需要使用自定义过滤器;
    classes属性指定了另一个自定义过滤器类,这里是AutoConfigurationExcludeFilter.class。会将不需要自动配置的类屏蔽掉。

这两个自定义过滤器的作用是根据一定的规则来排除不需要自动配置的类。例如,TypeExcludeFilter可以排除特定类型的类,AutoConfigurationExcludeFilter可以排除不需要自动配置的类。这样就可以在Spring Boot自动配置过程中,针对某些类进行自定义的过滤和排除,从而更加灵活地控制自动配置的行为。

6.注解@SpringBootConfiguration

  • @SpringBootConfiguration的定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@SpringBootConfiguration被 @Configuration 标注,说明它实际上是标注配置类的,而且是标注主启动类的。

也就是说,在启动类上使用这个注解表明该类是一个配置类,也是当前项目的主启动类。

@Configuration

被 @Configuration 标注的类,会被 Spring 的IOC容器认定为配置类。一个被 @Configuration 标注的类,相当于一个 applicationContext.xml 的配置文件。

7.注解@EnableAutoConfiguration

这一个注解是spring boot自动装配的核心注解。

(1)Spring手动装配

Spring Framework提供了多种手动装配的方式,其中比较常见的有以下几种:

使用XML配置文件

在XML配置文件中使用<bean>元素配置需要装配的Bean对象,然后使用ClassPathXmlApplicationContext或者XmlWebApplicationContext启动Spring容器。Spring容器会自动解析XML配置文件,并将其中配置的Bean对象注册为Spring容器中的Bean对象。

示例代码如下:

<beans>
    <bean id="myService" class="com.example.service.MyServiceImpl"/>
    <bean id="myController" class="com.example.controller.MyController">
        <property name="myService" ref="myService"/>
    </bean>
</beans>
使用Java注解

在Java类中使用@Configuration注解标记一个配置类,然后在该类中使用@Bean注解标记需要装配的Bean对象。然后使用AnnotationConfigApplicationContext启动Spring容器。Spring容器会自动扫描@Configuration注解的类,并将其中使用@Bean注解标记的方法的返回值注册为Spring容器中的Bean对象。

示例代码如下:

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

    @Bean
    public MyController myController() {
        MyController controller = new MyController();
        controller.setMyService(myService());
        return controller;
    }
}
使用Java类

直接在Java类中创建需要装配的Bean对象,并将其注册到Spring容器中。然后使用AnnotationConfigApplicationContext启动Spring容器。Spring容器会自动扫描Java类中注册的Bean对象,并将其注册为Spring容器中的Bean对象。

示例代码如下:

public class MyConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

    @Bean
    public MyController myController() {
        MyController controller = new MyController();
        controller.setMyService(myService());
        return controller;
    }
}

public class MyApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        MyController controller = context.getBean(MyController.class);
        // ...
    }
}

需要注意的是,Spring Framework的手动装配方式适用于需要对Bean进行复杂的配置或者需要根据特定条件进行Bean的选择和装配的场景。对于简单的Bean对象,使用自动装配方式更加方便快捷。

(2)Spring Boot自动装配

SpringBoot的自动配置完全由 @EnableAutoConfiguration 开启,这一注解是@SpringBootApplication注解中的组成之一。

从@EnableAutoConfiguration的定义可以看出,@AutoConfigurationPackage注解是它的组成之一。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

@AutoConfigurationPackage

@AutoConfigurationPackage注解的作用是将当前类所在的包及其子包中的所有类都注册到Spring Boot的自动配置中。这样,Spring Boot就可以扫描到这些类,并根据它们的特性和需求进行自动配置。

@Import(AutoConfigurationImportSelector.class)

@Import(AutoConfigurationImportSelector.class)注解的作用是让Spring Boot自动扫描classpath下的META-INF/spring.factories文件,从中获取所有的自动配置类,并将这些自动配置类导入到Spring Boot应用程序中。

spring.factories是一个标准的Java properties文件,它可以在classpath下的任何SpringFramework的jar包中找到。在这个文件中,可以通过键值对的方式指定自动配置类的全限定类名,例如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

这个键值对指定了两个自动配置类:DataSourceAutoConfiguration和HibernateJpaAutoConfiguration。在Spring Boot启动应用程序时,Spring Boot会自动扫描到这些自动配置类,并根据它们的特性和需求进行自动配置。

【SpringBoot】SpringBoot整合SpringSession SpringBoot整合SpringSession示例,存储分布式用户会话 阅读详情

相关推荐

Spring Boot自动装配原理

创建自定义自动配置类需要遵循特定的模式。定义配置类:使用(或SpringBoot 3.x中的)注解标记配置类,并添加适当的条件注解。@Bean注册配置类:在项目的文件中添加以下内容:或在SpringBoot 3.x中使用绑定配置属性如果配置类需要外部属性,可以定义一个属性类并使用注解:@Component//...@Autowired//...自定义条件注解对于复杂的条件判断,可以创建自定义条件注解:@Override// 自定义条件判断逻辑// 自定义条件参数。

初心不改 1492

springboot的自动配置加载和启动

所谓自动配置那么就应该分成两步;一、自动;二、配置 一、springboot如何实现自动 springboot其实就是对spring的二次开发,能够实现自动主要是因为注解@EnableAutoConfiguration;注解中使用了@Import({AutoConfigurationImportSelector.class});AutoConfigurationImportSelector中又实现了DeferredImportSelector接口;DeferredImportSelecto...

qq_16803227的博客 1539

Spring Boot源码解析 - 自动装配原理

作为自动装配核心类,在处理@Import逻辑中触发自动装配的过程,最终获取需要导入的自动装配类集合,实现Spring Boot的自动装配功能。注解,我们可以在一个配置类中引入其他的配置类,从而将它们的Bean定义和配置合并到当前配置中。,以key-value的形式进行本地缓存,后续触发自动装配时,从本地缓存中获取自动装配类集合。后置处理器会在扫描解析启动类当前包以及子包下的配置类,并将其注册为Spring管理的。,其他部分与自动装配无关。的自动装配类集合,然后去重过滤返回需要导入的自动装配类数组。

q1424966670的博客 1632

Spring框架之Bean的装配方式

Spring框架之Bean的装配

m0_56540237的博客 732

Spring Boot(四):Spring Boot启动原理分析

将配置类@SpringBootApplication标注的类所在包及下面所有子包里的所有组件扫描到spring容器。点击@SpringBootApplication,再点击@EnableAutoConfiguration进入类文件。这些自动配置类给我们当前项目的场景提供了一些组件和配置,有了自动配置就免除类手动编写配置文件,注入等等功能。这里管理着springboot中所有依赖的版本,是一个版本控制中心,导入依赖如果不写版本就用这里的版本。该jar包提供一系列的配置类,替换掉了我们的XML配置信息。

Lansonli(蓝深李)的博客 1万+

超详细网安学习路线(Day1-Day2)

渗透测试(Penetration Testing),就是一种通过模拟恶意攻击者的技术与方法,挫败目标系统安全控制措施,取得访问控制权,并发现具备业务影响后果安全隐患的一种安全测试与评估方法。简单来说,就是合法去入侵别人的系统,来帮助其找出漏洞,增强防护能力。

aobulaien001的博客 3386

Springboot - 包扫描原理解析

Springboot - 包扫描原理解析_有机后浪的博客-优快云博客_spring扫描包原理 Spring中的包扫描 在Spring中bean的注入有两种方式: 自定义bean:xml或配置类 注解,设置自动扫描 Spring中xml配置文件: **配置类:**在类上加注解@Configuration,将类设置为配置类代替xml配置文件 由程序员手动设置bean是比较繁琐的,Spring又提供了偷懒的方法 注解、自动扫描: 在springmvc.xml中<context:comp

yygr的博客 4625

电信光猫隐藏的VOIP功能揭秘:不用座机也能打电话(EasySip实战)

本文揭秘了电信光猫隐藏的VOIP功能,通过EasySip实战演示如何无需座机实现高清通话。详细解析了光猫的SIP协议支持、语音VLAN配置及网络架构优化,帮助用户利用现有设备提升通话质量并扩展设备兼容性,特别适合智能家居和企业级应用场景。

weixin_29268385的博客 754

Springboot启动流程核心知识点(一):Spring自动装配原理

依托Springboot启动流程,结合源码,完整了解Springboot自动装配原理。

bigbearxyz的博客 703

新建菜单Office被替换为腾讯文档 - 解决方案

右键新建菜单中的Office被替换为腾讯文档。原因是安装了腾讯文档软件,此软件安装时会自动将新建菜单中的Office替换为腾讯文档,下文介绍解决方案。

NSJim的博客 2926

破解拼多多网页字体加密的Python实现(静态)

爬取或者复制粘贴出来发现数字不是6372,而是这样的一个东西 ""从上图我们可以看出数字是一行的空白,后面被浏览器渲染后才展现的数字。我们可以用这个代码查看十六位进制对应的十进制,为什么要看十进制呢?​,而字体文件中的字符映射关系(cmap)也是以十进制存储的。我们搜索这个属性我们可以看到第一个这样的属性有一个URL,这个就是解密的字体包。然后我们构造一个这样的映射字典 key就是对应的十进制,后面的值就是对应的数字。然后我们需要封装一个函数,将加密文字和映射字典传入,返回解密后的数字。

weixin_64179111的博客 3538

Spring-Boot自动配置原理:kkFileView启动流程源码解析

在日常开发中,你是否遇到过需要集成各种文件在线预览功能的困扰?面对不同格式的文件,集成过程复杂且耗时。kkFileView作为一款基于Spring-Boot的通用文件在线预览项目,能够轻松解决这一问题。本文将深入解析kkFileView的启动流程,带你了解Spring-Boot自动配置在其中的应用。读完本文,你将清楚kkFileView如何从代码层面实现启动,并掌握其配置原理。 ## 项目启动入...

gitblog_00118的博客 397

facebook协议

支持同时登录多个Facebook账号,按照关键词采集小组地址,群发小组,批量添加推荐人,群发推 荐人消息,采集个人Facebook用户,群发用户私信,群发主页评论。通过客服台管理功能,和粉丝互动聊天,通过话术快捷回复,自动翻译聊天记录。通过FaceBook小组采集小组成员id,或者通过FaceBook个人主页,采集主页上粉丝批量操作已FaceBook账号添加推荐热,可以指定添加某个国家的推荐人,可以设置每个账号添加推荐人个数去重模式:所有的FaceBook账号不添加重复的推荐人。

2503_90751938的博客 951

Spring Boot到微服务:大厂Java面试核心考点与实战解析

Java后端开发中,框架的原理理解与工程实践能力往往决定技术成长的天花板。Spring Boot作为当前主流的微服务开发基础设施,其自动装配、循环依赖处理、事务失效等机制,不仅是日常开发中的常见难点,也是技术面试中考察候选人基本功的高频切入点。理解这些底层原理,能帮助开发者从“会用”走向“懂设计”,在面对复杂业务场景时做出更合理的架构决策。随着微服务架构的普及,服务拆分、注册中心选型、分布式事务及幂等设计等话题,逐渐成为中高级工程师必须掌握的技能体系。无论是准备面试还是梳理自身技术栈,从Spring Boo

weixin_30219613的博客 195

Ubuntu 24.04 下 NoMachine 极速安装与远程桌面配置全攻略

本文详细介绍了在Ubuntu 24.04系统下安装和配置NoMachine远程桌面的完整指南。从系统准备、分步安装到性能调优和常见问题解决,帮助用户快速实现低延迟的远程桌面访问。特别适合开发者和IT管理员在Linux环境下高效部署远程工作环境。

moon9的博客 945

银河麒麟V10系统下,手把手解决Ollama安装报错‘GLIBCXX_3.4.25 not found’

本文详细介绍了在银河麒麟V10系统下解决Ollama安装报错‘GLIBCXX_3.4.25 not found’的实战指南。通过隔离编译新版libstdc++、环境变量引导和服务化封装等安全方案,确保在不破坏系统稳定性的前提下顺利运行Ollama,适用于国产操作系统与AI工具的兼容性需求。

weixin_42556197的博客 482

SpringBoot启动流程原理+自动装配原理

SpringBoot 设计的目的是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。SpringBoot相对Spring的优点主要有两个: 1.起步依赖-会将很多jar包按照功能合并成stater整体进行版本管理和引用,解决Spring集成其他框架时jar版本管理问题 2.自动装配-引入相关的jar包后SpringBoot会自动注册一些比较关键的bean,并进行默认配置,不用我们进行特殊配置,解决Spring重量级XML配置问题。比如整合Mybatis时的SqlSessionFac

yangxiaofei_java的博客 7693

RK3588S之CPU--benchmark(一)

本章会基于RK3588S来介绍有关CPU常用的benchmark工具及测试过程

文艺小少年的博客 1498

Spring Boot自动装配原理(易懂)

熟练使用Spring Boot那么自动装配原理的掌握是必不可少的

总结分享 4万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Spring Boot的自动装配原理

自动装配是springboot的核心,一般提到自动装配就会和springboot联系在一起。实际上SpringFramework早就实现了这个功能。SpringBoot只是在其基础上,通过SPI的方式,做了进一步优化。SpringBoot定义了一套接口规范,这套规范规定SpringBoot在启动时会扫描外部引用jar包中的文件,将文件中配置的类型信息加载到Spring容器(此处涉及到JVM类加载机制与Spring的容器知识),并执行类中定义的各种操作。...

YING—country的博客 3万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Spring Boot 启动原理揭秘:从 main 方法到自动装配

Spring Boot 是由 Pivotal 团队推出的一个开源框架,旨在简化 Spring 应用的初始搭建和开发。它基于 Spring Framework 构建,提供了一种快速、便捷的方式来构建独立运行的、生产级的应用程序。Spring Boot 的核心理念是“约定优于配置”,这意味着开发者无需手动编写大量 XML 或 Java 配置代码即可快速启动项目。Spring Boot 的启动流程是一套高度封装且高度可扩展的机制,涵盖了从应用启动到上下文初始化、自动装配、Web 服务器启动等多个阶段。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

《SpringBoot启动流程五》:你真的知道SpringBoot自动装配原理吗(两万字图文源码分析)

你真的知道SpringBoot自动装配原理吗? 源码分析SpringBoot自动装配入口? SpringBoot自动装配在SpringBoot启动流程中的体现?

健身变秃,coding变强 3956

Spring Boot Starter 自动装配原理全解析:从概念到实践

在Spring Boot开发中,**Starter**和**自动装配**是两个核心概念,它们共同构成了“开箱即用”的开发体验。通过引入一个`Starter`依赖,开发者可以快速集成第三方组件(如Redis、MyBatis等),而无需手动配置大量Bean。这种“约定优于配置”的理念显著提升了开发效率。本文将深入解析Spring Boot Starter的自动装配原理,涵盖其核心机制、实现流程、自定义方法及最佳实践。

专注于与编程相关的知识内容分享 2469

springboot 项目起步讲解及自动装配原理

SpringBoot是约定优于配置这一理念下的一个产物,所以在很多的地方都会看到这一类的思想,它的出现让开发人员可以更加聚焦在业务代码的编写上,而不需要去关系和业务无关的配置,其实自动装配的思想在springframework3.x版本里面的@enable注解,就已经有了实现的一个雏形,@enable注解时候一个模块驱动的意思,就是说我们只要增加@enable注解,就可以自动打开某个功能,而不需要针对这个功能去做Bean的配置,@enable注解的底层是帮我们自动完成这样的一个模块相关bean的注入

程序猿追的博客 4万+

Spring Boot 自动装配原理

Spring Boot 自动装配原理 每次问到 Spring Boot, 面试官非常喜欢问这个问题:“讲述一下 SpringBoot 自动装配原理?”。 我觉得我们可以从以下几个方面回答: 什么是 SpringBoot 自动装配? SpringBoot 是如何实现自动装配的?如何实现按需加载? 如何实现一个 Starter? 前言 使用过 Spring 的小伙伴,一定有被 XML 配置统治的恐惧。即使 Spring 后面引入了基于注解的配置,我们在开启某些 Spring

林心涯的博客 4006

spring boot自动装配原理

springboot自动装配几乎是现在面试必问的面试题,要是逐行分析自动装配流程肯定是很复杂的,因此我们从大体上来梳理即可。

u012453843的专栏 1382
上一篇: Spring Boot 实战指南(五):自定义注解、拦截器
下一篇: Spring Boot原理分析(二):IoC
TracyCoder123
博客等级 码龄8年 1万+粉丝 226原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracyCoder123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值