Java面试锦囊(五)

目录

介绍一下Spring

说下Spring框架的组成

什么是Spirng的IOC

你对AOP的理解

Spring的Bean懒加载和非懒加载有什么区别

Spring的依赖注入方式有哪些

说一下定义切面相关的注解

Bean的四种注册方式

注册Bean的注解有哪些

IOC的启动流程有了解过吗

Bean的生命周期讲一下

单例多例的区别

Spring的Bean被指定为prototype以及singleton有什么区别

BeanFactory和ApplicationContext有什么区别

BeanFactory和FactoryBean的区别

IOC容器是如何保证Bean的单例的?

Spring如何解决Bean的循环依赖

Spring构造器注入能循环依赖吗

说几个Spring的IOC的容器工厂类

你知道Spring的AOP主要基于什么设计模式实现吗

你知道@Autowaire自动注入的实现原理吗?

你知道@Transcational注解的实现原理吗?

SpringMVC部分

SpringMVC怎么样设定重定向和转发的

SpringMVC如何对时间格式的参数进行格式化

SpringMVC常用的注解有哪些

如何定义SpringMVC的拦截器

HandlerInterceptor和HandlerInterceptorAdapter的区别

SpringMVC的执行原理

SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决

RequestMapping 和 GetMapping有什么区别

SpringBoot部分

相比Spring,Spring Boot有哪些优点

SpringBoot如何做全局异常处理

@SpringBootApplication注解的含义

spring-boot-starter-parent的作用

spring-boot-starter-web的作用

SpringBoot中如何读取配置

SpringBoot中日志的level有哪些

SpringBoot中如何管理事务

SpringBoot自动配置原理

那它是怎么去找到这些所谓的自动配置类的呢?

SpringBoot启动流程

SpringSeucity

说一下security中的的filter

说一下security的认证原理


Spring部分

介绍一下Spring

Spring是一个开源的轻量级控制反转和面向切面编程的容器框架。轻量级是说它开发使用简单,功能强大。控制反转是指将对象的创建,销毁控制交给ioc容器,方便解耦合,降低维护难度,面向切面编程是指将相同的逻辑横向抽取出来,可以对一些通用业务如事务,日志进行集中管理

说下Spring框架的组成

  1. CoreContain核心容器模块:

    1. spring-core:提供框架的基本组成部分,包括 IoC 和依赖注入功能

    2. spring-beans:提供 BeanFactory,工厂模式

    3. context:提供国际化,事件传播,资源加载等功能

    4. spring-ExpressionLanguage:提供表达式语言

  2. Web模块

    1. Web:提供面向web的基本功能和面向web的应用上下文

    2. Web-MVC:为web应用提供模型视图控制(MVC)

    3. Web-Socket:在 web 应用程序中提供客户端和服务器端之间通信的方式

    4. Web-Portlet:模块提供了用于Portlet环境的MVC实现

  3. 数据/集成模块

    1. JDBC:包含了Spring对JDBC数据访问进行封装的所有类

    2. ORM:为对象-关系映射提供交互层

    3. OXM:提供对Object/XML映射实现的抽象层

    4. JMS:主要包含了一些制造、消费和消息的功能

    5. Transaction:为实现特殊接口类以及所有的 POJO 支持编程式和声明式的事务管理

  4. 其他模块

    1. AOP:提供了面向切面编程相关实现

    2. Aspects:模块提供了与AspectJ的集成,是一个功能强大的AOP框架

    3. Instrumentation:提供了class instrumentation 的支持和类加载器classloader的实现

    4. Messaging:为 STOMP 提供支持

    5. Test:支持使用JUnit和TestNG对Spring组件进行测试

什么是Spirng的IOC

IOC控制反转,把对象的创建,属性设置,初始化,销毁等工作交给Spirng的IOC容器去管理,解放程序员的劳动力。

对象被注册到Spring的IOC容器中,使用的时候从容器中获取即可,非常方便。

它通过依赖注入,将需要的外部资源注入到组件中,使用IOC使得对象之间的耦合度降低,资源变得容易管理,从而使得代码更加优雅

你对AOP的理解

AOP,Aspect Oriented Programming 英文首字母缩写,意为面向切面编程,是Spring的核心思想之一

AOP是对OOP(面向对象编程)的一种补充,能够做到很多面向对象无法做到的事情,比如需要在所有方法执行前开启事务,打印日志,如果使用面向对象来编程,将会产生大量重复代码,而使用AOP,可以将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,一次解决这些问题。而这些重复的代码,一般统称为横切逻辑代码

使用AOP,在不改变原有业务逻辑的情况下,实现解耦合,避免横切逻辑代码重复

AOP的使用场景包括日志记录,性能统计,安全控制,事务处理,异常处理等等

它是基于动态代理实现的,分为JDK动态代理和CGLIB动态代理。JDK动态代理只支持实现了接口的类 ,CGLIB支持没有实现接口的类。Spring默认使用JDK动态代理,如果被代理类没有实现接口,会选择CGLIB动态代理

Spring的Bean懒加载和非懒加载有什么区别

懒加载:需要使用对象的时候才创建,节省资源,但不利于提前发现错误

非懒加载,也叫迫切加载,容器启动时就创建对象,消耗资源,但有利于提前发现错误

spring中默认时迫切加载,即在项目启动时,spring会扫描符合条件的所有bean并将其初始化

如果需要懒加载,可以使用@Lazy注释或者xml中配置属性default-lazy-init="true"

Spring的依赖注入方式有哪些

方式一:setter方式注入,通过反射调用无参构造方法生成对象,再通过对应的setter方法注入配置的值,支持注解和xml两种实现方式

方式二:构造器方式注入,通过反射调用有参构造方法生成对象,支持注解和xml两种实现方式

注解实现方式:@Autowired,它是默认按类型匹配的、@Resource,它是默认按名字匹配的

说一下定义切面相关的注解

@Aspect:定义切面

@Pointcut:定义切点 = cn.xx.service.*

@Before:前置通知,在目标方法运行之前运行

@After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)

@AfterReturning:返回通知,在目标方法正常返回之后运行

@AfterThrowing:异常通知,在目标方法出现异常以后运行

@Around:动态代理,手动推进目标方法运行

Bean的四种注册方式

方式一:普通注册方式,直接通过class注册

方式二:简单静态工厂方式注册

方式三:简单实例工厂方式注册

方式四:FactoryBean方式注册

注册Bean的注解有哪些

@Controller/@RestController 一般用于定义控制层的类

@Service 一般用于定义服务层的类

@Repository 一般用于定义持久层类

@Component 定义一般类

@Configuration 定义配置类

IOC的启动流程有了解过吗

当Spring启动时,IOC容器会加载Spring的配置文件,包括XML配置或者注解,然后解析这些Bean并把相关定义信息封装成BeanDefinition对象,通过Bean注册器BeanDefinitionRegistry注册到IOC容器,也就是一个ConcurrentHashMap中

此时会找出所有的单例且非惰性加载的bean,根据其BeanDefinition进行Bean的实例化,它会判断如果bean中有方法覆盖,就使用JDK反射创建Bean,否则使用CGLIB方式生成代理。然后把实例化好的Bean缓存到一个ConcurrentHashMap中

Bean的生命周期讲一下

从宏观的角度来说就是:实例化 ,属性注入,初始化,使用,销毁。更细的生命周期如下

  1. 实例化:如果是单例且迫切加载的bean,在Spring容器启动时就会根据BeanDefinition进行实例化,如果时设置了懒加载或者多例模式的bean,在用的时候才会实例化

  2. 属性赋值:通过BeanDeifinition找到当前Bean所依赖的其他Bean,如果容器中有就直接拿过来,如果没有就根据创建流程区创建依赖的bean,然后通过反射给依赖的字段注入值

  3. 然后会调用BeanPostProcessor的前置处理器,对于@Autowired和@Transcational就是基于BeanPostProcessor来实现的。

  4. 接着会看Bean是否实现InitializingBean ,如果有会触发其afterPropertiesSet方法的调用

  5. 接着是调用我们自定义的bean的init-method方法,此时会调用执行

  6. 然后是调用BeanPostProcessor的后置处理

  7. 容器正常关闭,Bean进行销毁,会先调用实现了DisposableBean的destory方法。

  8. 接着调用我们指定的bean的destroy-method方法,此时会调用执行

单例多例的区别

单例和多例属于对象模式,单例模式指对象在整个系统中只存在一份,多例模式则可以有多个实例。

在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以通过修改scope属性:scope="prototype"

如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个map中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。

如果是多例(prototype)模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。

Spring的Bean被指定为prototype以及singleton有什么区别

这两者分别指的是多例和单例模式,singleton即单例模式,指对象在整个系统中只存在一份;prototype即多例模式系统中可以有多个实例。

如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个map中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。

如果是多例模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。

在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以指定scope属性:scope="prototype"

BeanFactory和ApplicationContext有什么区别

BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法

ApplicationContext接口是BeanFactory接口的子接口,除了继承BeanFactory中所有管理bean的方法,还拥有环境、国际化、资源、事件等服务相关的接口

BeanFactory是延迟加载,ApplicationContext是迫切加载

BeanFactory和FactoryBean的区别

BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法

FactoryBean是IOC容器创建bean的一种形式,可以通过实现此接口来创建实例化过程比较复杂的bean

IOC容器是如何保证Bean的单例的?

IOC容器会将单例模式的bean放入一个ConcurrentHashMap中,需要这个bean时直接到这个map中获取,如果没有找到才会实例化这个bean。而ConcurrentHashMap本身时线程安全的,也就保证了Bean是单例的

Spring如何解决Bean的循环依赖

循环依赖分为三种,构造器注入循环依赖 ,setter方式注入循环依赖,多例模式Bean的循环依赖。而Spring解决了单例bean的setter注入循环依赖

setter循环依赖的解决主要使用了三级缓存

  • 一级缓存,用来缓存已经实例化好的bean,即单利Bean缓存池

  • 二级缓存,用来缓存正在创建的bean

  • 三级缓存,用来缓存创建bean的实例工厂ObjectFactory

假设有两个bean,A依赖B,B依赖A

当实例化好A,在属性注入环境,发现A依赖了B,会先将正在创建的A的实例工厂ObjectFactory放入三级缓存,然后去创建B的实例。

走Bean的实例化流程创建B,在B的属注入环节发现,B依赖了A,这个时候就会去三级缓存中,找到A的创建工厂ObjectFactory获取A的实例,并注入到B中。此时B就初始化好了,然后将B实例放入一级缓存。最后将B实例注入到A中,A也就创建好了

在getBean的时候,如果单利Bean缓存池没有Bean,就会走二级缓存尝试获取,如果也没有,就会走三级缓存拿到Bean的ObjectFacory创建Bean,然后把Bean放入二级缓存。

Spring构造器注入能循环依赖吗

构造注入不能解决循环依赖的原因是:如果A的构造其中依赖了B B的构造器中又依赖了A 在getSingleton中三级缓存需要调用getObject()构造器,来构造提早暴露但未设置属性的bean,此时就会产生无限递归创建

多例模式下Bean是不做缓存的,所以就没法暴露ObjectFactory,也就没办法解决循环依赖

说几个Spring的IOC的容器工厂类

BeanFactory:IOC容器顶层接口,提供了Bean获取的基础方法

DefaultListableBeanFactory:是整个 bean 加载的核心部分,Spring 注册及加载Bean 的默认实现

ApplicationContext:除了实现IOC基本功能外,还扩展了国际化支持,资源访问,事件发布

ClasspathXmlApplicationContext:从classpath中获取XML配置

你知道Spring的AOP主要基于什么设计模式实现吗

AOP的实现原理是基于动态代理,动态代理就是在运行时期动态的为原生类生成代理类以达到代码增强的目的,且代理类是持有原生类的,可以在代理类中调用原生类以及做一些增强业务。

动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包,两者的区别是JDK动态代理要求目标类需要实现至少一个接口。而CGLIB则是基于继承进行代理,原生类可以不实现任何接口

Spring中默认采用JDK动态代理,如果原生类没有实现任何接口,Spring会选择CGLIB代理,或者你可以在配置文件中强制指定使用CGLIB代理

你知道@Autowaire自动注入的实现原理吗?

自动注入是通过BeanPostProcessor 后置处理器AutowiredAnnotationBeanPostProcessor完成的,在Bean实例化过程中,触发了AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法的调用执行,它就会扫描当前类中是否有@Autowired注解,然后得到自动注入依赖的bean的类型,并去容器中得到依赖的bean实例,如果没有就走Bean的实例化流程创建依赖的Bean,然后反射进行字段赋值。

你知道@Transcational注解的实现原理吗?

分为两个动作把,第一个是解析@Transcational注解,在Sping中有个后置处理器InfrastructureAdvisorAutoProxyCreator,在Bean的初始化过程中,它负责解析标记了@Transcational注解的类,生成代理。还创建了 TransactionAttributeSource ,它是对事务注解的封装,以及 TransactionInterceptor 事务拦截器。

在执行业务方法的时候,代码会进入事务拦截器TransactionInterceptor去执行事务相关的代码,TransactionInterceptor主要是通过调用TranscationManagerment的事务API,而TranscationManagerment又是调用connection的事务API完成事务操作。

SpringMVC部分

SpringMVC怎么样设定重定向和转发的

重定向是指将用户从当前请求重新定向到一个视图页面,或者是一个handler处理请求,以前的request域中信息全部失效,同时地址栏会发生变化,它是客户端行为

转发是指将用户从当前请求转发给另一个视图页面或者handler处理请求,以前的request域可以共享,地址栏不会发生变化,它是服务器行为

springmvc默认是使用转发方式跳转的,且会默认经过视图解析器,我们也可以通过指定,转发时在返回值前面加"forward:",重定向时在返回值前面加"redirect:",且此时就不会再经过视图解析器了

SpringMVC如何对时间格式的参数进行格式化

第一种需求,后台接收前台页面返回的string类型时间,要转换成的Date类型数据,可以使用@DateTimeFormat注解来接收参数

第二种需求,后台将Date类型数据返回给前台页面,默认是返回时间戳,如果想要优雅的格式,可以在模型的Date字段或get方法上使用@JsonFormat注解

SpringMVC常用的注解有哪些

@Controller:用来标识一个类是控制器类

@RequestMapping:用来映射请求路径和参数

@ResponseBody:将返回值放到responsebody中,通常返回json或者xml格式数据

@RequestBody:将前台请求参数转换成对象

@PathVariable:接收路径参数,通常用在restful接口中

@RestController:@Controller和@ResponseBody的组合注解

@ControllerAdvice:运用aop的思想,对全局做一些处理,比如结合@ExceptionHandler做全局异常捕获

如何定义SpringMVC的拦截器

SpringMVC 的拦截器主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、判断登录等功能上

第1步,定义拦截器:可以实现 HandlerInterceptor 接口来自定义拦截器,接口定义了三个方法,preHandler方法是在请求到达处理器之前执行,postHandler方法是在请求经过处理器之后、解析试图之前执行,afterCompletion方法是在视图渲染之后、返回客户端之前执行

第2步,配置拦截器:在springmvc的配置文件xml中,配置所有拦截路径,以及需要放行的路径

HandlerInterceptor和HandlerInterceptorAdapter的区别

HandlerInterceptor是接口,我们可以实现该接口来定义拦截器,HandlerInterceptorAdapter是抽象类,它实现了HandlerInterceptor接口的子接口AsyncHandlerInterceptor,我们可以继承该类来定义拦截器,它简化拦截器的实现,默认preHandler返回true

SpringMVC的执行原理

1.Http请求:客户端请求提交到DispatcherServlet-前端控制器

2.寻找处理器:由DispatcherServlet调用HandlerMapping-处理器映射器,根据url找到对应的的Handler

3.调用处理器:DispatcherServlet指定HandlerAdapter-处理器适配器去调用Handler

4.调用业务处理和返回结果:Handler调用业务逻辑处理完成后,返回ModelAndView

5.处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler-视图解析器,找到ModelAndView指定的视图

6.Http响应:将结果显示到客户端

SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决

在spring中,bean默认都是单例的,controller也是交给spring容器管理的一个bean,因此它也是单例的。

单例的好处是减少了创建对象和垃圾回收的时间,节省了内存资源,但同时单例会造成线程不安全的问题,因为当所有请求访问同一个controller实例,controller中的成员变量是所有线程公用的,某个线程如果修改了这个变量,别的请求再来拿这个变量就编程修改后的值了

要解决这个问题,最直接有效的方式就是不要在controller中定义成员变量,如果你非要定义成员变量,两种方式

第一种,可以给controller上加注解@Scope("prototype"),将controller设置为多例模式,每次请求都重新实例化一个controller

第二种,使用ThreadLocal变量,让每一个线程都有自己独立的变量

RequestMapping 和 GetMapping有什么区别

@Getmapping是一个组合注解,即是@RequestMapping(method = RequestMethod.GET)的缩写,意思是只接收get请求的方法

@Requestmapping如果没有指定请求方式,可以接收get,put等各种类型的请求

SpringBoot部分

相比Spring,Spring Boot有哪些优点

Springboot是一个基于spring的框架,对spring做了大量简化,使开发流程更快,更高效

它大量简化maven依赖,管理了大量的基础依赖

基于注解配置(JavaConfig),无需xml配置

内嵌Tomcat,部署流程简单

打包和部署更加灵活,允许独立运行

SpringBoot如何做全局异常处理

可以使用@ControllerAdvice注解,编写一个全局异常处理类,再自定义一个方法使用@ExceptionHandler来捕获具体的异常并作相应的处理

通常情况下后台向前台返回结果时,会把结果封装成包含有错误码,错误信息以及数据本身的json数据,因此我们可以使用自定义异常类,自定义枚举错误码,在捕获全局异常后,向前台返回一个包含错误码的信息

@SpringBootApplication注解的含义

@SpringBootApplication是SprnigBoot项目的核心注解,目的是开启自动配置,并表示该类为主启动类。它包含三个子标签

  • @ComponentScan注解:开启ioc自动扫描注解,默认扫描当前包及其子包中@Controller,@Service等,并把这些bean加载到ioc器中

  • @EnableAutoConfiguration注解:启用springboot自动配置,自动所有扫描classpath目录下面所有jar中的spring.factories文件实现配置类批量注册

  • @SpringBootConfiguration注解:标志该类为springboot配置类

spring-boot-starter-parent的作用

这是SpringBoot的父工程,它的作用是帮我们管理了很多的基础jar包,同时它继承了spring-boot-dependencies,在spring-boot-dependencies项目中通过<dependencyManagement>管理了大量的依赖,同时通过<properties>维护了这些依赖的版本号

但是在项目中,还需要通过<dependencies> 去导入具体的依赖才能使用

spring-boot-starter-web的作用

此项目是Springboot和Springmvc整个的jar包,构建了web项目的基本环境,集成了日志,tomcat,springmvc,json支持等等

SpringBoot中如何读取配置

方式一:使用@Value读取配置文件

方式二:使用@ConfigurationProperties读取配置文件

SpringBoot中日志的level有哪些

日志级别从低到高分别为:

TRACE < DEBUG <INFO <WARN < ERROR

如果设置为 WARN,则低于 WARN 的信息都不会输出

Spring中默认使用INFO级别输出到控制台

SpringBoot中如何管理事务

事务(transaction)是指业务逻辑上对数据库进行的一系列持久化操作,要么全部成功,要么全部失败。

在Springboot中,可以通过xml配置和注解配置

xml方式通过配置DataSourceTransactionManager和transactionManager实现

注解方式配置通过在主启动类上加上@EnableTransactionManagement开启事务管理器,在具体的实现层service类上加上@Transactional 实现事务

SpringBoot自动配置原理

在启动类上我们会打上: @SpringBootApplication 注解,它是一个组合标签,包括:

  • SpringBootConfuration ,本质是一个 Configuration ,代表Spring的配置类。

  • IOC自动扫描的注解 ,ComponentScan 会去扫描类上是否有:@Component ,@Respository ,@Service @Controller ,如果有,就会把这个类自动注册到Spring容器中。

  • EnableAutoConfiguration :就是启动SpringBoot自动配置的注解

在 @EnableAutoConfiguration 注解中,注册了一个选择器,其中有一个方法会去返回很多的自动配置的的全限定名,这些类会自动注册到Spring容器中,

那它是怎么去找到这些所谓的自动配置类的呢?

他会通过Spring的SPI接口,也就是通过一个SpringFactoryLoader去扫描 classpath中的所有的jar包中的 MET-INF/spring.factories 中的自动配置类,比如: DispatchServlert就对应了DispatchServlertAutoConfiguration自动配置类 , 它通过@Bean+方法的方式注册了一个 DispatchServlert 到Spring容器中了

SpringBoot启动流程

1.开启秒表计时

2.starting监听器,

3.处理应用参数

4.加载环境对象

5.打印横幅

6.创建Spring容器对象:AnnotationConfigApplicationContext

7.容器刷新的前置工作

8.刷新容器 ,这里会执行spring的ioc属性容器的refrsh方法,Bean的加载,初始化等都在这个里面,Tomcat的启动也在这个方法里面。

9.刷新容器后置工作

10.秒表停止

11.started事件

12.调用runner

13.running.listeners

SpringSeucity

说一下security中的的filter

SecurityContextPersistenceFilter:请求开始会从SecurityContextRepository中获取SecurityContext对象并设置给SecurityContextHolder,在请求完处理成后将SecurityContextHolder持有的SecurityContext再保存到配置好的SecurityContextRepository中,同时清除SecurityContextHolder中的SecurityContext

UsernamePasswordAuthenticationFilter:默认拦截“/login”登录请求,将请求中的认证信息包括用户名,密码封装成UsernamePasswordAuthenticationToken,然后调用AuthenticationManager的认证方法进行认证

BasicAuthenticationFilter:处理 HTTP 请求的 BASIC 授权标头,如果身份验证成功,就把生成的Authentication对象放入SecurityContextHolder。如果设置了记住我,下次访问就不会走这里来了

RememberAuthenticationFilter:记住我,调用RememberMeServices的autoLogin方法自动登录

AnonymousAuthenticationFilter:匿名filter,检测SecurityContextHolder有没有Authentication对象,如果没有,就会创建一个AnonymousAuthenticationToken并保存到SecurityContextHolder

ExceptionTranslationFilter:处理filter链中的所有AccessDeniedException和AuthenticationException

FilterSecurityInterceptor:继承自AbstractSecurityInterceptor,通过调用AccessDecisionManager.decide方法进行授权

说一下security的认证原理

首先,请求会经过UsernamePasswordAuthenticationFilter拦截,请求的用户名密码会封装成UsernamePasswordAuthenticationToken,过滤器将token提交给认证管理器AuthenticationManager进行认证

然后,认证管理器调用AuthenticationProvider进行认证,AuthenticationProvider再调用UserDetailsService获取到数据库中存储的用户信息UserDetails,然后调用密码编码器对密码进行比较,认证成功后封装Authentication

再后来,请求回到UsernamePasswordAuthenticationFilter,调用SecurityContextHolder将Authentication对象封装成SecurityContext并保存到SecurityContextHolder中

最后,请求回到SecurityContextPersistenceFilter,它会调用SecurityContextRepository将SecurityContext对象存储起来,再清理掉SecurityContextHolder中的信息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值