@SpringBootApplication详解

一篇文章读懂@SpringBootApplication 基于 springboot3.2 和 springframework6.1.4。 阅读详情
AI摘要

本文详细解析了SpringBoot中的核心启动类@SpringBootApplication,包括其内部注解如@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan的含义,重点讲解了自动配置的原理,以及如何通过@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)动态引入配置。

@SpringBootApplication详解

1 .简介

在Spring Boot的学习中难免需要接触源码,而入手及时从Spring Boot项目启动类开始入手。项目启动类非常简单,仅仅存在一个注解@SpringBootApplication以及一个运行参数为被该注解标注类run函数。

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

对于该启动类的分析,就从这个Spring Boot的核心注解开始入手。

2 .核心注解@SpringBootApplication

字面分析,这个注解是标注一个Spring Boot应用。

 
 
@Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
 @SpringBootConfiguration
 @EnableAutoConfiguration//引入EnableAutoConfiguration 
 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
 public @interface SpringBootApplication {
    ...//具体参数暂时忽略
 }

进入到这个注解后,可以发现该注解由四个元注解,以及其他三个注解组成分别是:@SpringBootConfiguration、

@EnableAutoConfiguration(自动配置有关的核心注解,主要分析)、

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

2.1 Spring的配置类@SpringBootConfiguration

字面分析,这是一个Spring Boot的配置类。

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

从他的源码来看,除了元注解之外,它仅仅被@Configuration注解所标注,那么可以理解@SpringBootConfiguration为他仅仅就是一个配置类。

@Configuration 源码

 
@Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Component
 public @interface Configuration {...}

再分析@Configuration的源码,该注解为Spring中的配置类注解,其中被@Component标注为Spring组件,意味着他被注册到IOC容器。

因此,套用官方文档的答案:@SpringBootConfiguration表示一个类提供 Spring Boot 应用程序@Configuration。可以用作 Spring 标准@Configuration注释的替代方法,以便可以自动找到配置(例如在测试中)。

2.2 开启自动配置@EnableAutoConfiguration !!!

这个注解是Spring Boot的自动装配的核心。

 

 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
 @AutoConfigurationPackage
 @Import(AutoConfigurationImportSelector.class)//自动配置的引入选择器实现
 public @interface EnableAutoConfiguration {...}

除了四个元注解,这个注解被两个注解所标注:

@AutoConfigurationPackage、

@Import(AutoConfigurationImportSelector.class)

那么我们先直接往下分析

2.2.1 自动配置包@AutoConfigurationPackage
 
@Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
 @Import(AutoConfigurationPackages.Registrar.class)
 public @interface AutoConfigurationPackage {...}

从注解来看,@AutoConfigurationPackage中使用注解@Import(@Import:的作用)导入了AutoConfigurationPackages.Registrar.class到容器中,那么来分析这个类,进入到这个内部类Regisrar:

 
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
 ​
     @Override
     public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
         register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
     }
 ​
     @Override
     public Set<Object> determineImports(AnnotationMetadata metadata) {
         return Collections.singleton(new PackageImports(metadata));
     }
 ​
 }

该类引入的重点在于方法registerBeanDefinitions():

 public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
         register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
 }

首先先分析方法体中所调用的方法register()的第二个参数

PackageImports(metadata).getPackageNames().toArray(new String[0])

进入到类PackageImports的构造方法:

 
PackageImports(AnnotationMetadata metadata) {
     AnnotationAttributes attributes = AnnotationAttributes
                     .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
     List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
     for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
                 packageNames.add(basePackageClass.getPackage().getName());
     }
     if (packageNames.isEmpty()) {
                 packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
     }
     this.packageNames = Collections.unmodifiableList(packageNames);
 }

在这个构造方法中将元数据即启动类AnnotationMetadata metadata经过处理

1.获取标签注解信息,注解信息里面的 basePackages 和 basePackageClasses是否有数据。

basePackages、 basePackageClasses为注解@AutoConfigurationPackage中的属性。

2.如果没有数据则获取注解所在的类的名字目录,放到List中

获得packageNames属性也就是启动类所在的包。

回到Registrar中的registerBeanDefinitions()方法中register()方法的第二个参数即为启动类所在的包的名称,并且使用数组来进行表示。

在分析register()方法,register()源码如下:

 
private static final String BEAN = AutoConfigurationPackages.class.getName();
 ​
 public static void register(BeanDefinitionRegistry registry, String... packageNames) {
     //BeanDefinitionRegistry registry  其中放方法boolean containsBeanDefinition(String beanName);是为了判断是否已经注册了`AutoConfigurationPackages`的类路径所对应的`bean(AutoConfigurationPackages)`
    if (registry.containsBeanDefinition(BEAN)) {
       BasePackagesBeanDefinition beanDefinition =(BasePackagesBeanDefinition)registry.getBeanDefinition(BEAN);
       beanDefinition.addBasePackages(packageNames);
    }
    else {
       registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
    }
 }

这个方法的if语句为判断registry这个参数中是否已经注册了AutoConfigurationPackages的类路径所对应的bean(AutoConfigurationPackages)。如若已经被注册,则把上面分析的第二个参数所获取的包(启动类所在的包的名称)添加到这个bean的定义中。如若没有,则注册这个bean并且把包名设置到该bean的定义中。

小结:@AutoConfigurationPackage就是添加该注解的类所在的包作为自动配置包进行管理。他的实现就是依赖于工具类AutoConfigurationPackages中的内部类Registrar对所标注的包进行注册。

2.2.2 导入 自动配置导入选择器@Import(AutoConfigurationImportSelector.class)

这个@import使用了2.2.1中@import的用法中的通过ImportSelector 方式导入的类,所以我们进入到该类,直接找到selectImports方法。在这个用法中,所返回的字符串数组为所有的将要被导入的类的全类名。那么知道这个方法是做什么的,就开始分析这个方法。

再分析selectImports方法,selectImports()源码如下:

 
public String[] selectImports(AnnotationMetadata annotationMetadata) {//selectImports方法会被springboot自动调用,从而得到他返回的全类名的字符串数组,然后把对应类的bean对象注入到ioc容器中
         if (!this.isEnabled(annotationMetadata)) {
             return NO_IMPORTS;
         } else {
             AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
             return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
         }
     }

从return开始分析,autoConfigurationEntry自动配置实体中List的属性configurations将被返回。autoConfigurationEntry是通过方法getAutoConfigurationEntry()获得的,那么就进入到这个方法

 
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
       return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
 }

根据return所返回的内容,返回的是一个使用属性configurations所生成的自动配置实体,configurations是使用getCandidateConfigurations()获取候选配置所得到的。

 
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
         List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())//load 加载的意思
             .getCandidates();
         Assert.notEmpty(configurations,
                 "No auto configuration classes found in "
                         + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
                         + "are using a custom packaging, make sure that file is correct.");
         return configurations;
 }
 ​

从以上源码可以发现,在springboot3之后,已完全放弃对META-INF/spring.factories的读取。而是从传的AutoConfiguration.class取的类名称。即org.springframework.boot.autoconfigure.AutoConfiguration。拼接之后 ,META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(.imports配置文件)。(2.7升级到3.0后的变化)

读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

DispatcherServletAutoConfiguration配置类中还有配置类DispatcherServletConfiguration,其中还有方法dispatcherServlet(WebMvcProperties webMvcProperties)添加了@bean注解springboot会继续解析,直到把@Bean注解的方法都解析到,然后执行这些方法,把返回值注入到ioc容器中,因此自动配置的核心在配置文件里

 
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
 @AutoConfiguration(after = ServletWebServerFactoryAutoConfiguration.class)
 @ConditionalOnWebApplication(type = Type.SERVLET)
 @ConditionalOnClass(DispatcherServlet.class)//如果当前环境存在DispatchServlet类,则注入,否则不注入
     //注意 DispatchServlet类 是引用web启动依赖后会有的
 public class DispatcherServletAutoConfiguration {
     ...
     @Configuration(proxyBeanMethods = false)
     @Conditional(DefaultDispatcherServletCondition.class)
     @ConditionalOnClass(ServletRegistration.class)
     @EnableConfigurationProperties(WebMvcProperties.class)
     protected static class DispatcherServletConfiguration {
 ​
         @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
         public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
             DispatcherServlet dispatcherServlet = new DispatcherServlet();
             dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
             dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
             dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
             dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
             dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
             return dispatcherServlet;
         }
 ​
         @Bean
         @ConditionalOnBean(MultipartResolver.class)
         @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
         public MultipartResolver multipartResolver(MultipartResolver resolver) {
             // Detect if the user has created a MultipartResolver but named it incorrectly
             return resolver;
         }
 ​
     }
     ...
 }
2.2.3总结!!!

@SpringBootApplication是一个组合注解,其中有@EnableAutoConfiguration也是一个组合注解,@EnableAutoConfiguration有`@Import(AutoConfigurationImportSelector.class),导入了AutoConfigurationImportSelector.class类,AutoConfigurationImportSelector.class实现了selectImports方法,经过层层调用最终读取一个配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(注意,springboot2.7前读取的是spring.factories文件,2.7-3.0兼容两个,3.0之后只有.imports)这个配置文件中写了一堆的全类名,其中有一个是org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,它是完成DispatcherServlet这个对象的自动注入的,DispatcherServletAutoConfiguration类中有@AutoConfiguration标明是一个自动配置类,@ConditionalOnClass(DispatcherServlet.class)用来去设置bean注册的条件(如果环境里有DispatcherServlet.class这个配置类,DispatcherServletAutoConfiguration这个自动配置类就生效,否则就不生效,注意引入web启动依赖就有DispatcherServlet.class这个配置类),因此说引入了web启动依赖springboot就自动注入了一个DispatcherServlet。

关于springboot中的@SpringBootApplication注解 @SpringBootApplication 是 Spring Boot 的核心入口注解,其设计体现了“约定优于配置”的理念,通过以下方式大幅简化开发: 组合注解:集成配置、自动配置和组件扫描功能,减少样板代码。 智能推断:基于依赖和类路径动态配置应用,减少手动干预。 灵活性:支持自定义扫描范围和覆盖默认配置,适应复杂场景。 阅读详情

相关推荐

SpringBoot启动过程完全指南:从原理到实战的深度解析与性能优化

Spring Boot是一个基于Spring框架的快速开发工具,通过自动配置和约定优于配置原则简化应用开发。其核心特性包括自动配置(根据依赖自动配置应用)、起步依赖(简化依赖管理)、命令行界面支持以及生产级监控功能。与传统的Spring框架相比,Spring Boot提供了更便捷的配置方式和内嵌服务器支持,特别适合微服务和快速开发场景。启动过程包含环境准备、上下文创建与刷新等关键步骤,通过@SpringBootApplication注解实现自动配置。配置文件支持properties和yml格式,后者更适合复

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 2142

@SpringBootApplication详解(Spring Boot启动原理)

本文来说下Spring Boot 中的Starters 文章目录概述 概述 Starters 可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。 Starters 包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。 ...

wh柒八九的博客 3万+

传感器采集信息的频率(采样频率)

较高的采集频率通常能提供更详细的信息,但也会增加数据处理和存储的负担。在某些应用中,较高的采样频率可能是必要的,而在其他情况下,则可能会选择适当的低采样频率以降低功耗和数据处理压力。传感器采集信息的频率(采样频率)是指传感器在一定时间内对输入信号进行采样的次数。对于一些需要快速检测和响应的应用,如运动传感器、声音传感器等,高采样频率是至关重要的。高采样频率通常需要更多的功耗和更大的存储容量,因为产生的数据量更大。例如,对于快速运动的对象,较低的采样频率可能无法捕捉到运动的真实轨迹。

chen的博客 5345

springboot中@SpringBootApplication详解

理解springboot框架,首先最重要就是要理解最关键的注解@SpringBootApplication: @SpringBootApplication注解的代码如下,这些注解中 有关SpringBoot的注解只有三个,分别是: SpringBootConfiguration EnableAutoConfiguration ComponentScan() 实际上@SpringBootApplication这个注解是这三个注解的复合注解。每次写三个会显得极其麻烦,将其整合。 首先、@Spring

qq_39817135的博客 2万+

构建家庭NAS之三:在TrueNAS SCALE上安装qBittorrent

以qBittorrent为例,说明如何在TrueNAS SCALE上使用docker来安装应用。

luoqingyong的博客 7006

Palabos User Guide中文解读 | 第一章 | 介绍

译者的话:本人在学习palabos时,发现国内中文资料甚少,恰好网上可以直接搜到palabos user guide这种英文资料,加之时间充裕,便打算开始翻译,能不能完成全本翻译再说,而且文中我也会标记我不确定的地方,以传达意思为主,翻译不当的地方还请各位读者帮忙指正。 Palabos User Guide Release 1.0 r1 Copyright © 2019 University of...

Yulan Fang's notes. 6977

@SpringBootApplication注解详解

@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot 就应该运行这个类的main方法来启动SpringBoot应用。 如下代码: @SpringBootApplication public class HelloWorldMainApplication { public static voi...

killerofjava的博客 2117

详解一典型的反激式开关电源方案

如图,在某系统方案上看到下图所示的单端反激式开关电源方案。

alala120的博客 1万+

@SpringBootApplication启动类详解

@SpringBootApplication启动类详解 SpringBoot所具备的特征有: 内嵌Tomcat或Jetty等Servlet容器;(这个最赞!) 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs; 提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置; 尽可能自动配置Spring容器; 提供准备好的特性,如指标、健康检查和外部化配置; 接下来我们来看一下SpringBoot主程序 @SpringBootA

mangzi29的博客 1712

6、业务影响分析(BIA)的数据收集与关键指标评估

本文全面解析了业务影响分析(BIA)的核心内容,包括数据收集、业务流程分析、信息系统与资产清单、人员与供应商依赖、影响声明、关键指标评估(如MTD、RTO、RPO)、威胁建模与风险分析等。通过详细的介绍和图表展示,帮助组织系统性地制定灾难恢复计划,以应对各种潜在风险,确保关键业务流程在灾难发生后能够快速恢复并持续运营。

moss5的博客 374

PyTorch强化学习实战(3)——Gymnasium API扩展功能

我们已经学习了编写智能体必需的 Gymnasium 核心功能,其余 API 功能部分虽然非必需,但能显著提升开发效率和代码整洁度。在本节中,我们简要地介绍了 Gymnasium API 的扩展功能,学习了如何以模块化的方式扩展现有环境的功能,并熟悉了使用包装器可视化智能体活动的方法。

盼小辉丶的博客 1201

@SpringBootApplication注解

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CU...

hou_xudong的博客 3068

基于SiamFC的红外目标跟踪

初始化阶段:在第一帧中,通过用户标注获得目标框,从目标区域中提取一个固定大小的模板(称为 exemplar)。搜索阶段:在后续帧中,在以当前目标中心为中心的较大区域(称为 instance)内进行搜索,将模板与搜索区域通过卷积操作得到响应图。位置更新:在响应图中找到峰值位置,并转换到原图坐标,用以更新目标中心。部分实现中还会考虑多尺度搜索和目标尺寸更新。import cv2# 定义 SiamFC 简易网络(示例)# 特征提取部分,注意红外图像通常为单通道,使用较少的卷积核数提高实时性。

weixin_43745234的博客 774

@SpringBootApplication解析

我们经常写的@SpringBootApplication 是什么意思呢?* @SpringBootApplication 源码点开瞟一眼: 》我们发现@SpringBootApplication 中包含 @SpringBootConfiguration、@ EnableAutoConfiguration、@ ComponentScan ,这一个注解代替了这三个注解; * @Configur...

Syntacticsugar's blog 4212

从 Classic ABAP 到 ABAP Cloud:一份面向开发者的 SAP 学习路线图与实战指南

ABAP技术演进路线:从语法到架构思维 本文系统梳理了现代ABAP开发者的能力成长路径。在S/4HANA与云时代,ABAP已从传统过程式语言转变为支持面向对象、测试驱动和云原生开发的现代技术栈。学习路线应围绕四大核心维度展开:1) 语言基础(ABAP OO/Modern ABAP);2) 工程化实践(ABAP Unit/ATC/Clean ABAP);3) 数据建模(CDS/AMDP);4) 服务化架构(Gateway/OData/Fiori)。文章强调以交付形态倒推技术栈,通过真实案例展示了如何将零散技术

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 361

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

@SpringBootApplication 注解详解

很多人知道“”,但它究竟做了什么,却并不清楚。

2401_88153531的博客 841

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

@SpringBootApplication的使用

之前用户使用的是3个注解注解他们的main类。分别是@Configuration,@EnableAutoConfiguration,@ComponentScan。由于这些注解一般都是一起使用,spring boot提供了一个统一的注解@SpringBootApplication。 @SpringBootApplication = (默认属性)@Configuration + @EnableAut

码农 5万+

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

@SpringBootApplication详解 以及 pom 讲解

一、@SpringBootApplication 主要有以下注解组成 @SpringBootConfiguration @EnableAutoConfiguration 二、@SpringBootConfiguration Spring Boot 配置类, 它本质上是@Configuration @Configuration配置类注解,它本质上是一个组件@Component @Configurat...

Vivienne_ChenW的博客 3042

SpringBoot中@SpringBootApplication的详解

@SpringBootApplication的详解

Stephen GS的博客 1599
下一篇: 自定义starter
Ldealing
博客等级 码龄6年 139粉丝 4原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

Ldealing

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

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

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

打赏作者

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

抵扣说明:

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

余额充值