Spirngboot基础2.0(上课版)及其高级技术

SpringBoot

简化配置,基于约定优于配置的思想

Spring缺点:1.配置繁琐2.依赖繁琐

Spring功能:

1.自动配置:运行时自动配置

2.起步依赖:传递依赖

3.辅助功能:嵌入服务器,安全,健康检查,外部配置等

是Spring的优化

Maven依赖 父工程

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

作用:1.标识该项目是一个Springboot项目 2.管理各项技术的版本号

引导类

@SpringBootApplication
public class Learn2Application {

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

}

使用jar的打包方式,通过引导类使用

@SpringBootApplication注解:

@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}
)}
)

@SpringBootConfiguration:标记当前为Springboot配置类

@EnableAutoConfiguration:开启自动配置功能

@ComponentScan:包扫描器

起步依赖

核心

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

在父工程指定了版本信息,版本锁定

测试

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
@SpringBootTest
public class webappTest {

    @Autowired
    private helloController Controller;

    @Test
    public void test() {
    }
}

必须要@S注解springBootTest:作用加载启动类并生成springboot应用程序的上下文,测试的组件,服务,功能

测试类必须和启动类同一个包或者子包中!! 否则需要设置属性指定启动类

@SpringBootTest(classes =webApplication.class )

热部署:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

Bean:

注入容器:2种方法

1.扫包

2.配置文件创建引入

属性绑定:
company:
  name: ry
  address: cq
  post: 6

1.使用@Value注解

@Data
@ToString
@Component
public class Company {
    @Value("${company.name}")
    private String name;
    @Value("${company.address}")
    private String address;
    @Value("${company.post}")
    private String post;
}

2.使用@ConfigurationProperties

@Data
@ToString
@Component
@ConfigurationProperties(prefix = "company")
public class Company {
    // @Value("${company.name}")
    private String name;
    // @Value("${company.address}")
    private String address;
    // @Value("${company.post}")
    private String post;
}

可以将和配置文件中的指定前缀属性进行绑定

配置文件:

使用yml,properties,yaml进行配置,

优先级:properties>yml>yaml

键值对

server.port=8080
##自定义内容如下
name=abc

yml ,在冒号之后需要有空格

server:
 port: 8080

优先级properties>yml>yaml

YAML

以数据为中心比xml简洁 数据前必须有空格

能显示程度,缩进不允许使用Table只允许空格

同级左对齐 #表示注释

数据格式:

1.对象(map)键值对集合

#行内写法 也是需要空格间隔
server: {port: 8081}
# 
server:
  port: 8081


2.数组 斜杠开始 依旧需要空格隔开

address:
  - shanghai
  - chongqing
  #行内写法
  address: [shanghai,chongqing]

3.纯量

单引忽略转义字符

双引号识别转义字符

msg1: 'hello \n word'
msg2: "hello \n word"

参数引用

name: abc

person:
 name:${name}

读取数据的方式:

1.@Values

@RestController
public class Hello {
    @Value("${address1[0]}")
    private String name;

    @RequestMapping("/hello")
    public String hello() {
        return "Hello World!"+this.name;
    }

}

2.Environment

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.@ConfigurationProperties

如果不指定会优先找名为name 和age的属性 不会导入person下的属性

server:
  port: 8080

name: ry
person:
  name: ${name}
  age: 18

person2: {name: zhangsan2, age: 18}

address1: [shanghai,chongqing]
address2:
  - shanghai
  - chongqing
  - xian

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

加入对应的标记:

@Data
@Component
@ConfigurationProperties(prefix = "person")
public class person {

    private String name;
    private int age;
}

需要加入这个依赖实现提示功能

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

proflie实现不同环境的配置:

@Profile注解 可以标识配置类所属环境

通过@Value 来注入值

多文件

创建多个文件,添加不同的后缀

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

dev开发,pro生产,test测试

想要使用哪个文件:active代表后缀

spring:
  profiles:
    active: pro
一个文件多个文档

使用—分割文档设置其spring:
profiles: pro 的标识

再spring:
profiles:
active: pro激活

server:
  port: 8081

name: ry
person:
  name: ${name}
  age: 18

person2: {name: zhangsan2, age: 18}

address1: [shanghai,chongqing]
address2:
  - shanghai
  - chongqing
  - xian


msg1: 'hello \n word'
msg2: "hello \n word"

Spring:
  profiles: dev
  devtools:
    restart:
      enabled: true  #开启热部署
      additional-paths: src/main/java  #指定监控的目录
      exclude: test/**

---
server:
  port: 8082

spring:
  profiles: test
---
server:
  port: 8083

spring:
  profiles: pro
---

spring:
  profiles:
    active: dev
虚拟机设置选择

在VM options指定-Dspring.profiles.active=dev

命令行参数:

java-jar xxx.jar --spring.profile.active=dev

内部配置加载顺序

高优先级会覆盖低优先级,都会生效可以互补

从大到小

1.file:/config/: 当前项目下的/config目录下

2.file:/ :当前项目的根目录

3.classpath:/config/:classpath的/config目录

4.classpath:/ :classpath的根目录

注意1和2不会打入jar包,命令打开jar包会读取3,4的配置

外部配置加载顺序

1.方式一在命令中指定参数

方式二:命令行中指定配置文件 java-jar xxx.jar --spring.config.location=配置文件目录

2.将配置文件放在jar同一级目录

3.将配置文件放jar同一级别的目录下的config文件。优先级最高

Spring高级:

SpringBoot自动配置:

Condition,通过这个功能可以实现选择性的创建Bean操作,导入坐标以后自动配置一些bean

@Conditional注解进行选择是否创建bean

继承接口进行设置

@Configuration
public class UserConfig {



    @Bean
    @Conditional(ClassCondition.class)
    public User user(){

        return new User();
    }


}

public class ClassCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //条件
        return true;
    }
}

切换内置服务器

tomcat,Netty,Jetty,Undertow

排除默认tomcat坐标加入其他服务器坐标,原理:Condition

   <!--排除tomcat-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加其他服务器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

启动成功外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

@Enable*注解

动态启动某些功能,底层原理使用@Import注解导入一些配置类,实现Bean的动态加载

@ComponenScan扫描范围:当前引导类所在包和及其子包

@Import注解:都会被Spring创建并放入IOC容器

对Import注解进行封装 ,写注解在注解中导入Import配置类

package com.example.learn2;
import com.example.learn2.config.UserConfig;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Import(UserConfig.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableUser {
}

@EnableUser

使用注解就可以导入Bean

@Import注解

4种导入方式

1.导入Bean,spring自动创建但不一定是类名

@SpringBootApplication
//@EnableUser
@Import(User.class)
public class Learn2Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Learn2Application.class, args);
       Map<String, User> system = context.getBeansOfType(User.class);
        System.out.println(system.toString());
    }

}

2.导入配置类,导入所以配置类的Bean

可以不加@Configuration注解

3.导入ImportSelector实现类,一般用于加载配置文件中的类


public class myImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.learn2.pojo.User"};//可以导入多个类,但必须全称
    }
}

@Import(myImportSelector.class)//使用注解导入

4.导入ImportBeanDefinitionRegeistrar实现类

public class myImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //类似于创建bean
        BeanDefinition user= BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
        //注册bean并写入名字
        registry.registerBeanDefinition("user",user);

    }
}

//使用@Import导入
@Import(myImportBeanDefinitionRegistrar.class)

@EnableAutoConfiguration

实际是@Import导入AutoConfigurationImportSelector.class}

META-INF/Spring.factories下定义了大量配置类,当Springboot启动时,会加载这些配置类,初始化Bean

Condition满足条件加载Bean

Starter案例,导入坐标。加载Bean

步骤:

创建redis-spring-boot-autoconfigure模块

创建redis-spring-boot-starter 模块 依赖redis-spring-boot-autoconfigure的模块

在redis-spring-boot-autoconfigure模块中初始化Jedis的Bean,并定义META-INF/spring.factories文件

在测试模块中引入自定义redis-starter依赖,测试获取的Jedis的Bean,操作redis

SpirngBoot监听机制

是对java监听机制的一种封装,

java中:1.事件,Event 继承java.until.EventObject

2.事件源:Source,任意对象

3.监听器:Listener,实现java.until.EventListener接口对象

Springboot中:

ApplicationContextlnitalizer,

在IOC容器加载之前实现

META-INF/spring.factories下配置,全路径名=自己实现接口的全路径名

org.springframewor…ApplicationContextlnitalizer=edcu.ry…myApplicationContextlnitalizer

SpringApplicationRunListener

META-INF/spring.factories下配置

类似生命周期对应的方法监听执行

CommandLineRunner

参数:配置参数

放入IOC容器运行时执行

ApplicationRuner

参数:配置参数

放入IOC容器运行时执行

作用:可以在用户没访问时候执行方法,来将数据库加入缓存来预热

Springboot启动流程分析

banner.txt文件resources下替换加载时候的Springboot图

Springboot监控

自带监控功能Actuator,可以实现对程序内部运行监控,比如监控状况,Bean加载,配置属性,日志信息等

<!-- 监控使用-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

访问 http:/localhost:8080/actuator

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

监控详情的程度

http://localhost/actuator/health查看监控情况

开启所有健康详情
management.endpoint.health.show-details=always
#暴露所有的url,端口
management.endpoints.web.exposure.include=*

SpringAdmin 开源监控

监控着:服务器模块

依赖:

 <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>

配置

访问端口

server.port=9000
被监控的:客户端模块

依赖

    <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
                <version>3.1.5</version>
        </dependency>

配置

#admin 服务器端口 监控详情
spring.boot.admin.client.url=http://localhost:9000
management.endpoint.health.show-details=always
#暴露的url端口 客户端的基本地址
management.endpoints.web.exposure.include=*
spring.boot.admin.client.instance.service-base-url=http://localhost:80

Spring Data的数据访问:

默认整合SpringData

也可以使用 JDBC模块 JPA模块 redis等等…

1.添加依赖: mysql为例子

 <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
<dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

2.配置文件写入

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springbootdata?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 303030
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  configuration:
    #开启驼峰命名
    map-underscore-to-camel-case: true

3.创建实体类和dao层接口

@Mapper
public interface CommentMapper {
    /**
     * a
     * @param id
     * @return Comment
     */
    Comment selectComment(int id);

    int deleteComment(int id);

    int insertComment(Comment comment);

    int updateComment(Comment comment);
}

加上@Mapper的注解 标识 Spirngboot容器才可以加入mapper-locations

或者在启动类上加上@MapperScan(“com.example.springbootconbindata.dao”)批量扫包

4.如果使用xml实现接口 需要修改配置文件 mapper-locations指定位置

mybatis:
  # mapper.xml文件位置
  mapper-locations: classpath:mapper/*.xml
  configuration:
    #打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #开启驼峰命名
    map-underscore-to-camel-case: true

Mybatis-Plus:

mybatis增强工作 不改变Mybatis

1.依赖
2.创建实体类:
@Data
@ToString
//绑定表明
@TableName("t_comment")
public class Comment {

    @TableId(type= IdType.AUTO)
    private int id;
    private String content;
    private String author;
    private Integer aId;
    
}

@TableName(“”) 实体类与表关联

@TableId 标识主键 以及指定生成主键策略

@TbaleFieId 将实体属性与表的列绑定

3.创建接口
@Mapper
public interface commentMapper extends BaseMapper<Comment> {
    
}

只用继承 BaseMapper注解 里面包含所有的单表操作接口 直接调用

4.扩展 MybatisPuls 的service

接口继承

public interface CommentService extends IService<Comment> {}

实现类继承

@Service
public class CommentServiceImpl  extends ServiceImpl<CommentMapper,Comment> implements CommentService {
}

Springboot 集成 Thymeleaf

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置文件:

  thymeleaf:
  #页面缓存
    cache: false
    encoding: UTF-8
    mode: HTML5
    prefix: classpath:/templates/
    suffix: .html

html例子

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 >nihao thymeleaf</h1>
<!--${.....}中的是模板引擎的变量-->
<h1 th:text="${name}"></h1>
</body>
</html>
常见th:标签

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

th:each 使用

和jsp中c:foreach 差不多 冒号前面是遍历的每一个对象

<fieldset>
    <legend>获取id为1的评论详情</legend>
    <table border="1">
        <tr><td>评论内容</td><td>评论作者</td></tr>
        <tr th:each="comment : ${commentList}">
            <td th:text="${comment.getContent()}"></td>
            <td th:text="${comment.author}"></td>
        </tr>
    </table>
</fieldset>
th:object:

*{}中可以直接写对象的属性

<table border="1">
        <tr><td>评论内容</td><td>评论作者</td></tr>
        <tr th:object=" ${comment}">
            <td th:text="*{content}"></td>
            <td th:text="*{author}"></td>
        </tr>
    </table>

Sringboot静态资源

Springboot默认设置了静态访问资源 将/**所有访问映射到一下目录

classpath:/META-INF/resources/

classpath:/resources/

classpath:/static/

classpath:/public/

也可以在配置文件中设置 来修改默认静态资源目录

  web:
    resources:
      static-locations: classpath:/static/
th:herf 和th:src 使用
 <link  th:href="@{/css/bootstrap.min.css}" href="login/css/bootstrap.min.css" rel="stylesheet">
    <link th:href="@{/css/signin.css}" href="login/css/signin.css" rel="stylesheet">
</head>
<body class="text-center">
<!--  用户登录form表单 -->
<form class="form-signin">
    <img class="mb-4" th:src="@{/img/login.jpg}" src="login/img/login.jpg" width="72" height="72">

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SpringMvc的扩展:

1.视图管理器

快速添加控制器跳转

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry
                .addViewController("/o/c")
                .setViewName("login");
    }

2.静态资源配置

配置文件会覆盖默认配置

  web:
  ##本地资源路径
    resources:
      static-locations: classpath:/static/
  mvc:
  ##映射
    static-path-pattern: /source/**

重写的方式 会添加不会覆盖

!!!但加上@EnableMvc 会覆盖默认配置

   @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
                .addResourceHandler("/resource/**")
                .addResourceLocations("classpath:/resource/");
    }

3.注册拦截器

创建拦截器
/**
 * @author 冉毓
 */
public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

注册拦截器(注意放行静态资源)

  @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry
                .addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/","/login","/source/**","/ok");

    }

4.CORS跨域

协议,端口号,主机 相同的称为同源

CORS是W3C跨域资源共享技术,目前是解决前端跨域请求问题,客服了ajax只能同源使用

3种方式实现:@CrossOrigin注解 ,全局配置实现 , 基于过滤器实现

注解:

 @RequestMapping(value = "/getData")
    @ResponseBody
    @CrossOrigin(value = "http://localhost:8081",maxAge = 3600,allowedHeaders = "*")
    public List<User> addUser() {

        LambdaQueryWrapper<User> queryWrapper=new LambdaQueryWrapper<>();
        List<User> list = userService.list(queryWrapper);
        return list;
    }

全局配置:

  @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                // .allowedOrigins("*")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","POST","PUT","DELETE","OPTIONS","HEAD")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }

全局异常处理:

全局处理机制:

Spring mvc 框架中通过HandlerExceptionResolver机制来实现全局异常处理功能

实现方式:

1.实现HandlerExceptionResolvr接口

public class ExceptionH implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return new ModelAndView("login");
    }
}
2.使用@ControllerAdvice 注解和@ExceptionHandler注解

@ExceptionHandler来定义异常处理方法

3.前后端分类 使用@RestControllerAdvice

@RestControllerAdvice 是@ExceptionHandler和@ResponseBody组合

4.Springboot全局异常处理扩展

如果没有部署HandlerException 转发给/error

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
st;
}


全局配置:

```java
  @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                // .allowedOrigins("*")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","POST","PUT","DELETE","OPTIONS","HEAD")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }

全局异常处理:

全局处理机制:

Spring mvc 框架中通过HandlerExceptionResolver机制来实现全局异常处理功能

实现方式:

1.实现HandlerExceptionResolvr接口

public class ExceptionH implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        return new ModelAndView("login");
    }
}
2.使用@ControllerAdvice 注解和@ExceptionHandler注解

@ExceptionHandler来定义异常处理方法

3.前后端分类 使用@RestControllerAdvice

@RestControllerAdvice 是@ExceptionHandler和@ResponseBody组合

4.Springboot全局异常处理扩展

如果没有部署HandlerException 转发给/error

[外链图片转存中…(img-fQ18sf8F-1718805199644)]

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot SAML 2.0 是一个用于在Spring Boot应用程序中实现SAML 2.0身份验证和授权的框架。SAML(Security Assertion Markup Language)是一种用于在不同的身份提供者和服务提供者之间进行安全单点登录(SSO)的协议。 使用Spring Boot SAML 2.0,我们可以方便地在我们的应用程序中集成SAML身份验证。它提供了一组注解和配置选项,以帮助我们配置和管理身份提供者和服务提供者的元数据,身份提供者是用于进行用户身份验证的系统,而服务提供者是提供了受保护资源的应用程序。 Spring Boot SAML提供了一个内置的身份提供者管理器,它可以与多个身份提供者进行集成,并处理与他们之间的认证和授权交互。我们可以使用适当的配置和注解将身份提供者的元数据加载到应用程序中,并配置应用程序的登录和注销行为。一旦配置完成,我们的应用程序将能够通过SAML协议与身份提供者进行身份验证。 Spring Boot SAML 2.0还为我们提供了处理和解析SAML消息的内置工具。我们可以使用这些工具来处理SAML消息,包括身份提供者的身份确认请求和服务提供者的断言响应。这有助于我们在应用程序中处理SAML身份验证流程的各个阶段,例如,在我们的应用程序中处理来自身份提供者的SAML断言,并将用户授权为受保护的资源。 总而言之,Spring Boot SAML 2.0为我们提供了一种简便的方式来集成SAML 2.0身份验证和授权功能到我们的Spring Boot应用程序中。它简化了与身份提供者和服务提供者之间的交互,并提供了一组有用的工具来处理SAML消息。这使得我们可以轻松地实现安全的单点登录功能,从而提高了我们应用程序的安全性和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值