SpringBoot面试热题

1.Spring IOC(控制反转)和AOP(面相切面编程)的理解

控制反转意味着将对象的控制权从代码中转移到Spring IOC容器。
本来是我们自己手动new出来的对象,现在则把对象交给Spring的IOC容器管理,IOC容器作为一个对象工厂,管理对象的创建和依赖关系。
控制反转是一种思想,依赖注入是控制反转的实现方式,通过IOC容器自动将依赖关系注入到需要它们的对象中。
Spring IOC的好处:

  • 集中统一管理对象,降低耦合度。
  • 便于单元测试、处理复杂对象创建和依赖关系、实现单例等。
  • Spring提供了完整的Bean生命周期管理,包括对象增强(如AOP)。

所谓的「面向切面编程」在我理解下其实就是在方法前后增加非
业务代码AOP底层的技术是动态代理,在Spring内实现依赖的是BeanPostProcessor
比如我们需要在方法上注入些「重复性」的非业务代码,就可以利用Spring AOP
Spring AOP 解决的是 非业务代码抽取的问题

你们项目一般是怎么把对象交给IOC容器管理的?(换个问法:一般是怎么定义Bean的?)
Spring提供了4种方式,分别

1):注解
2):XML
3):JavaConfig
4):基于GroovyDSL配置
一般项目我们用注解或XML比较多,少部分用JavaConfig
日常写业务代码一般用注解来定义各种对象,责任链这种一般配
置在XML,「注解」解决不了的就用JavaConfig

2.Spring、SpringMVC、SpringBoot有什么区别?

pring+spring MVC+mybatis=传统ssm项目,spring负责本地的bean管理和事务处理,spring MVC负责对接web。spring boot则是综合了这俩,延展出来的新生态企业应用级快速开发架构,使各位cv工程师专注于敲代码,而不是去搞传统ssm那套繁琐配置。
因此,SpringBoot可以看作是在 Spring 的基础上,通过自动配置和约定优于配置的方式,提供了更加简单、快速的开发体验。而 SpringMVC 则是 Spring 框架中用于构建 Web 应用程序的模块。

3.SpringBoot实现自动化配置的原理

自动配置是通过@EnableAutoConfiguration注解实现的。这个注解会通过@import注解导入
AtuoConfigurationlmportSelector类,这个类会扫描META-INF/Spring.factories文件,这个文件里面定义了所有的自动配置类,这些配置类上可能存在条件注解,spring boot启动的时候根据这些注解,加载这些自动配置类。
1.自定义自动配置类步骤
定义一个类使用Configuration注解修饰;
定义META-INF/Spring-factories文件,向EnableAutoConfiguration中注入这个类

4.Spring 框架中都用到了哪些设计模式?

Spring 框架中使用了许多设计模式,以下列举一些比较重要的:

  • 单例模式:Spring 的 Bean 默认是单例模式,通过 Spring 容器管理 Bean 的生命周期,保证每个 Bean 只被创建一次,并在整个应用程序中重用。
  • 工厂模式:Spring 使用工厂模式通过 BeanFactory 和 ApplicationContext 创建并管理 Bean 对象。
  • 代理模式:Spring AOP 基于动态代理技术,使用代理模式实现切面编程,提供了对 AOP 编程的支持
  • 观察者模式:Spring 中的事件机制基于观察者模式,通过 ApplicationEventPublisher 发布事件,由
    ApplicationListener 监听事件,实现了对象间的松耦合。
  • 模板方法模式:Spring 中的 JdbcTemplate 使用了模板方法模式,将一些固定的流程封装在父类中,子类只需实现一些抽象方法即可。
  • 策略模式:Spring 中的 HandlerInterceptor 和 HandlerExecutionChain 使用了策略模式,允许开发者自定义处理器拦截器,按照一定顺序执行。
  • 责任链模式:Spring 中的过滤器和拦截器使用了责任链模式,多个过滤器和拦截器按照一定顺序执行,每个过滤器和拦截器可以拦截请求或者响应并做出相应的处理。

5.为什么SpringBoot使用CGLIB作为默认AOP动态代理?

动态代理可以代理任何类型的目标类,无论它是否实现了接口;
JDK 动态代理只能代理实现了接口的目标类;
CGLIB 动态代理可以覆盖 JDK 动态代理的所有场景,而 JDK 动态代理不能覆盖 CGLIB 动态代理的所有场景;
自动注入时,JDK动态代理只能用接口IUserService接收,而CGLIB两种都可以。
在这里插入图片描述

6.SpringBoot启动原理

  1. 启动类与注解解析。Spring Boot 应用通常由一个带有 @SpringBootApplication 注解的主启动类启动。@SpringBootApplication 是一个复合注解,主要由以下三个注解构成。
    装饰器模式:@SpringBootApplication 通过组合多个注解来扩展功能,类似装饰器模式,为启动类添加了多个行为。
  2. SpringApplication 的构建与服务类型确定。SpringApplication.run() 是 Spring Boot 应用启动的入口。SpringApplication 是核心服务对象,构建过程包括:资源加载器与主方法类的记录。
  3. 环境准备。在 run() 方法中,环境准备阶段的目标是为 Spring 应用上下文提供配置和上下文信息:创建并配置 ConfigurableEnvironment 环境对象。
  4. 容器创建。容器创建是 Spring Boot 启动过程的核心环节,它涉及到 Spring 应用上下文的初始化和配置。
  • 通过 createApplicationContext 创建合适的 ApplicationContext 实例.
  • 加载并注册核心 Bean 工厂、配置类处理器、自动装配处理器等组件。
  1. 填充容器。容器创建后,Spring Boot会自动装配应用中定义的所有 Bean,过程包括:
  • 加载所有 Bean 定义并实例化。
  • 执行 Bean 的生命周期回调方法,如 @PostConstruct、@PreDestroy。
  • 启动内嵌的 Web 服务器(如 Tomcat、Jetty)。

7.SpringBoot配置文件加载顺序(优先级)?

优先级从高到低,高优先级的配置覆盖低优先级的配置,所有配置会形成互补配置。
1.命令行参数。所有的配置都可以在命令行上进行指定;
2.Java系统属性(System.getProperties0));
3.操作系统环境变量;
4.jar包,外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
5.jar包内部的application-{profile).properties或application.yml(带spring.profile)配置文件 再来加载不带profile
6.jar包外部的application.properties或application.yml(不带spring.profile)配置文件
7.jar包内部的application.properties或application.yml(不带spring.profile)配置文件
8.@Configuration注解类上的@PropertySource

8.SpringCloud是什么?

Spring Cloud是一系列框架的集合,使用这些组件实现微服务。它利用Spring Boot的开发便利性简化了分布式系统基础设施的开发,如服务发现、服务注册、配置中心、消息总线、负载均衡、熔断器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud必须依赖Spring Boot开发,单独Spring Boot可以单独开发。
SpringCloud的几种常用组件如下:
1.服务发现–Netflix Eureka (Nacos)
Nacos用于实现注册中心,更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它支持多种服务治理能力,包括服务注册与发现、动态配置管理、动态DNS服务等。
2.服务调用–Netflix Feign
Feign是一个声明式的Web服务客户端,用来简化HTTP远程调用。在Spring Cloud中,Feign可以用来封装HTTP调用的接口。
Feign与Ribbon结合使用可以实现客户端负载均衡。‌Feign本身不直接支持负载均衡策略,‌而是通过集成Ribbon来实现。‌因此,‌Feign可以使用Ribbon支持的各种负载均衡策略,‌包括轮询、‌随机、‌权重、‌最佳可用等。‌
3.熔断器–Netflix Hystrix (Sentinel)
4.服务网关–Spring Cloud GakteWay
5.分布式配置–Spring Cloud Config (Nacos)
6.负载均衡–Ribbon
ribbon用于实现负载均衡,发起远程调用feign。
Ribbon负载均衡策略有:

  • RoundRobinRule:简单轮询服务列表来选择服务器
  • WeightedResponseTimeRule:按照权重来选择服务器,响应时间越长,权重越小
  • RandomRule:随机选择一个可用的服务器

9.SpringCloud微服务是什么?

微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API,这些服务基于业务能力构建,并能够通过自动化部署机制来独立部署,这些服务使用不同的编程语言实现。
微服务,关键其实不仅仅是微服务本身,而是系统要提供一套基础的架构,这种架构使得微服务可以独立的部署、运行、升级,不仅如此,这个系统架构还让微服务与微服务之间在结构上“松耦合”,而在功能上则表现为一个统一的整体。这种所谓的“统一的整体"表现出来的是统一风格的界面,统一的权限管理,统一的安全策略,统一的上线过程,统一的日志和审计方法,统一的调度方式,统一的访问入口等等

10.Spring中写一个接口最后返回json对象该怎么实现?

  1. 使用SpringMVC时可以使用@ResponseBody注解,方便的返回json数据 ,该注解用于将Controller的方法返回的对象,根据HTTP Request Header的中的Accept属性,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
  2. 使用@RestController注解Controller,它的作用相当于@ResponseBody+@Controller,意思是Controller处理完请求后直接返回json格式化数据。

11.Springboot过滤器和拦截器的区别?

过滤器是拦截所有请求,而拦截器是拦截在进入到前端控制器之后的请求
在这里插入图片描述
Filter和Listener:依赖Servlet容器,基于函数回调实现。可以拦截所有请求,覆盖范围更广,但无法获取ioc容器中的bean。
Interceptor和aop:依赖spring框架,基于java反射和动态代理实现。只能拦截controller的请求,可以获取ioc容器中的bean,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
过滤器的应用场景
Filter过滤器是Servlet容器层面的,在实现上基于函数回调,可以对几乎所有请求进行过滤。

  • 在访问网站时,有时候会发布一些敏感信息,发完以后有的会用*替代。
  • 还有就是登陆权限控制等,一个资源,没有经过授权,肯定是不能让用户随便访问的,
    拦截器应用场景
    拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:
  • 登录验证,判断用户是否登录。
  • 权限验证,判断用户是否有权限访问资源,如校验token
  • 日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
    拦截器的拦截是基于反射实现的。

12.SpringCloud中的Nacos是AP还是CP?

CAP原则:cap理论是针对分布式数据库而言的,它是指在一个分布式系统中,一致性(Consistency,C)、可用性(Availability,A)、分区容错性(Partition Tolerance, P)三者不可兼得。
AP模式(Availability Priority Mode)高可用性
CP模式(Consistency Priority Mode)一致性
Nacos支持AP(可用性 | 分区容错性) 和 CP(一致性 | 分区容错性)两种 默认是AP
nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;eureka采用AP方式。
如果注册Nacos的client节点注册时ephemeral=true,那么Nacos集群对这个client节点的效果就是AP,采用distro协议实现;而注册Nacos的client节点注册时ephemeral=false,那么Nacos集群对这个节点的效果就是CP的,采用raft协议实现。根据client注册时的属性,AP,CP同时混合存在,只是对不同的client节点效果不同。Nacos可以很好的解决不同场景的业务需求。

13.Spring AOP是什么?

Spring的AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的一个重要特性,用于将横切关注点从应用程序的主业务逻辑中分离出来,使得关注点的代码可以被模块化、重用,并且与主业务逻辑解耦。
1)切面(Aspect)
:其实就是定义了一个java类,里面包含了通知(advice)和切点(pointcut),定义了在何处
以及何时执行通知,将切面的一些东西模块化了,即定义横切关注点的模块,封装了不同模块共享的功能。
2)通知(Advice):切面中的实际逻辑,即在连接点上执行的操作。通知可以在方法执行前、执行后或抛出异常时
运行。

  • 前置通知(Before advice):在目标方法执行前执行。
  • 后置通知(Afterreturning advice):在目标方法成功执行后执行。
  • 后置异常通知(After throwing advice):在目标方法抛出异常后执行。
  • 后置最终通知(After (finally) advice):无论目标方法如何结束(正常返回或抛出异常),都会执行。
  • 环绕通知(Around advice):在目标方法执行前后都执行,并且可以控制目标方法的执行过程,环绕通知可以
    用作日志打印或者权限校验。

3)切点(Pointcut):切点是一个表达式,用于定义在哪些连接点上执行通知,简单理解就是通过这个表达式可以
找到想要织入的哪些方法。
4)连接点(Join point):连接点是程序执行过程中可以应用切面的点,例如方法的调用、方法的执行、异常的抛
出等,可以拿到切入方法名等诸多属性。
5)目标对象(Target Object):被切面增强的对象,也就是原本的业务类。
6)代理(Proxy):Spring AOP 通过生成代理对象来增强目标对象的方法。代理对象包含目标对象的原始方法和增
强逻辑。
7)织入(Weaving):将切面应用到目标对象上的过程。Spring AOP 是在运行时进行织入的。

实现切点的方式@Pointcut(“execution(* com.example.aopdemo.service..(…))”)
@Aspect
@Component
public class LoggingAspect {

    // 定义切入点,匹配 service 包中的所有方法
    @Pointcut("execution(* com.example.aopdemo.service.*.*(..))")
    public void serviceMethods() {}

    // 前置通知:在方法执行前记录日志
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    // 后置通知:在方法执行后记录日志
    @After("serviceMethods()")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }

    // 返回通知:在方法成功返回结果后记录日志
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("Method returned with value: " + result);
    }

    // 异常通知:在方法抛出异常后记录异常信息
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        System.out.println("Method threw exception: " + error.getMessage());
    }

    // 环绕通知:在方法执行前后记录日志,且可以控制方法是否执行
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before executing method: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed();  // 执行目标方法
        System.out.println("After executing method: " + joinPoint.getSignature().getName());
        return result;
    }
}
advice 将按照以下的顺序

这里说下简单情况——针对一个方法只被一个aspect类拦截时,aspect类内部的 advice 将按照以下的顺序进行执行情况如下:

执行到核心业务方法或者类时,会先执行AOP。在aop的逻辑内,先走@Around注解的方法。然后是@Before注解的方法,然后这两个都通过了,走核心代码,核心代码走完,无论核心有没有返回值,都会走@After方法。然后如果程序无异常,正常返回就走@AfterReturn,有异常就走@AfterThrowing。

14.Spring AOP和AspectJ的区别?

Spring AOP:是 Spring 框架提供的一种 AOP 实现,主要用于运行时的代理机制。较轻量级,使用方便。
特点:Spring AOP 是基于动态代理实现的,适用于Spring 容器管理的 Bean,
使用场景:适合大部分业务场景,尤其是需要简单 AOP 功能的 Spring 应用.
AspectJ:AspectJ是功能更强大的 AOP 框架,支持编译时、类加载时和运行时的 AOP 功能。
特点:Aspect支持更加灵活的切点和增强操作,提供编译期和加载期的织入方式,性能较高
使用场景:适合对性能要求较高或需要复杂切点匹配的场景,如日志、监控等。
总的来说,Spring AOP 更适合于那些希望在现有的Spring 应用程序中轻松引入 AOP 的场景,而 Aspect 则
更适合于那些需要更强大、更灵活的 AOP 功能的应用场景。选择哪一个取决于具体的需求以及项目的技术
栈。
Aspect在编译时织入,静态代理,需要特殊编译器,功能更强大。而Spring aop是运行时织入的,基于动态
代理,因此不需要特殊编译器,功能限于增强方法。

15.Java Agent是什么?

在这里插入图片描述
面向切面编程(AOP)允许我们在目标方法的前后织入想要执行的逻辑
家介绍的Java Agent技术,在思想上与aop比较类似,翻译过来可以被称为Java代理、Java探针技术,
Java Agent出现在JDK1.5版本以后,它允许程序员利用agent技术构建一个独立于应用程序的代理程序,用途也非常广泛,可以协助监测、运行、甚至替换其他JVM上的程序,先从上图图直观的展示了其应用场景。
主要有两种模式:

  • Premain模式允许在主程序执行前执行一个agent代理,启动之前在启动参数中添加-javaagent:/jar包路径[=agentArgs]
  • Agentmain模式可以说是premain的升级版本,它允许代理的目标主程序的jvm先行启动,再通过attach机制连接两个jvm

将Agent项目打包生成jar包,在META-INFO中找到一个MANIFEST.MF文件,在里面指定加载的是哪一个Agent:

Manifest-Version:1.0
Premain-Class: cn.bigfire.Agent1 //指定项目需要使用的Agent
Archiver-Version:Plexus Archiven
Built-By:xushu
Can-Retransform-Classes:true
Build-Jdk-spec:1.8
Created-By:Apache Maven 3.6.0
Build-Jdk:1.8.0 171

在这里插入图片描述

16.AOP环绕是否会导致业务代码异常,如何处理。

在Spring AOP中,环绕通知(@Around)确实有可能导致业务代码出现异常。这是因为环绕通知不仅可以在目标方法调用前执行代码,还可以在目标方法调用后执行代码,并且可以控制是否执行目标方法。如果环绕通知中的代码出现错误,可能会导致目标方法无法正确执行,甚至抛出异常。
如何处理环绕通知中的异常:

  • 记录日志:在捕获异常后,可以记录详细的日志信息,以便后续排查问题。
@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Around("execution(* com.example.service.MyService.targetMethod(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            // 环绕通知前的操作
            logger.info("Around advice before target method");

            // 执行目标方法
            Object result = joinPoint.proceed();

            // 环绕通知后的操作
            logger.info("Around advice after target method");

            return result;
        } catch (Exception e) {
            // 记录异常日志
            logger.error("Exception in around advice: ", e);
            // 可以选择重新抛出异常,或者返回一个默认值
            throw e; // 重新抛出异常
            // 或者 return null; // 返回一个默认值
        }
    }
}
  • 使用AOP提供的异常通知:Spring AOP提供了专门的异常通知注解 @AfterThrowing,可以在目标方法抛出异常后执行特定的逻辑。
@Aspect
@Component
public class ExceptionHandlingAspect {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingAspect.class);

    @AfterThrowing(pointcut = "execution(* com.example.service.MyService.targetMethod(..))", throwing = "ex")
    public void handleException(Exception ex) {
        // 处理异常
        logger.error("Exception in target method: ", ex);
    }
}

17.@Component和@Bean的区别?

  • 用途不同:
    @Component用于标识普通的类
    @Bean是在配置类中声明利配置Bean对象
  • 使用方式不同:
    @Component是一个类级别的注解,Spring通过@ComponentScan注解扫描并注册为Bean
    @Bean通过方法级别的注解使用,在配置类中手动声明一个Bean的定义
  • 控制权不同:
    @Component注解修饰的类是由Spring框架来创建和初始化的
    @Bean注解允许开发人员手动控制Bean的创建和配置过程,更灵活一些

18.@Autowired和@Resource的区别?

  1. @Autowired:自动注入,按照类型(type)自动装配,如果有多个同类型的Bean。可以用@Qualifier指定具体的Bean。
    @Resource:JSR-250提供的Java自带的注入方式,按照名称(name)自动装配,也可以显示指定byType按类型注入。

  2. @Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。

  3. @Autowired能够用在:构造器、方法、参数、成员变量和注解上,而@Resource能用在:类、成员变量和方法上。

@Component
public class QualifierTest {
 
    //@Qualifier 和 @Autowired结合使用可以通过唯一Bean的id实现自动装配
    @Autowired
    @Qualifier("userA")
    private User user;
 
 
    public void test(){
        System.out.println(user.getName());
    }
}

19.Spring Bean的生命周期?

在这里插入图片描述

20.Spring中常用注解有哪些?以及对应的作用是什么?

1.@SpringBootApplication:

包含了 @Configuration、@EnableAutoConfiguration 和 @ComponentScan。通常用于标注主类(启动类),以激活自动配置和组件扫描。

2.@RestController 和 @Controller:

@RestController 包含了@ResponseBody和@Controller。用来创建 RESTful Web 服务控制器,方法默认返回 JSON 或 XML 数据。
@Controller 一般用于传统的 MVC 控制器,需要配合 @ResponseBody 使用来返回非视图内容的数据。

3.@Configuration 和 @Bean:

@Configuration 表示该类是一个配置类,@Bean 用于在方法上定义新的 Spring bean。

21.SpringBoot自动配置原理?

1.通过@SpringBootConfiguration 引入了@EnableAutoConfiguration(负责启动自动配置功能)
2.@EnableAutoConfiguration引入了@lmport
3.Spring容器启动时:加载loc容器时会解析@Import 注解
4.@lmport导入了一个DeferredlmportSelector,它会使SpringBoot的自动配置类的顺序在最后,这样方便我们扩展和覆盖
5.然后读取所有的**/META-INF/spring.factories**文件
6.过滤出所有AutoConfiqurtionClass类型的类
7.最后通过@ConditionalOnClass排除无效的自动配置类
在这里插入图片描述

22.什么是循环依赖?Spring怎么解决循环依赖问题?

循环依赖:2个或多个对象之间相互依赖,导致Bean无法实例化(因为Spring中会优先创建Bean的属性对象)
出现循环依赖的场景:

  • Spring的Bean的实例化
  • JVM使用 引用计数法 进行垃圾回收,可能出现循环依赖导致内存泄漏。
  • 线程互相占用对方的锁,发生死锁

Spring内部通过三级缓存解决循环依赖

  • 1.一级缓存(singletonObjects 单例对象) :存放已经实例化、属性填充、初始化最终形态的 Bean。
  • 2.二级缓存(earlySingletonObjects 早期单例对象):存放提前暴露的、尚未属性填充的过渡 Bean。也就是
    级缓存中 objectFactory 产生的对象,与三级缓存配合使用的,可以防止 AOP 的情况下,每次调用
    0bjectFactory.getobject()都是会产生新的代理对象的。
  • 3.三级缓存(singletonFactories 单例工厂):存放 objectFactory ,objectFactory 的 getobject()方法最终调
    用 getEarlyBeanReference()方法可以生成原始 Bean 对象或者AOP代理对象。
    Spring创建Bean流程
    1.当 Spring 创建 A 时发现 A 依赖了 B,又去创建 B,B 依赖了 A ,又去创建 A;
    2.在 B 创建 A 的时候, A此时还没有初始化完成,因此在 -二级缓存 中肯定没有 A;
    3.那么此时就去三级缓存中调用 getobiect()方法最终调用 getEarlyBeanReference()方法去生成并获取 A 的前
    期暴露的对象。
    从三级缓存中移除,并且将前期暴露对象放入到二级缓存中,那么B就将它注入
    4.然后就将这个 objectFactory 到依赖,来支持循环依赖。
注: 只用两级缓存够吗?

在没有 AOP 的情况下,确实可以只使用一级和三级缓存来解决循环依赖问题。
当涉及到 AOP 时,二级缓存非常重要,避免了同一个 Bean 有多个代理对象的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jay_fearless

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

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

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

打赏作者

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

抵扣说明:

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

余额充值