Spring 家族


​​​​​​​​​​​​​​

目录

​​​​Spring 家族的组成关系

Java补充

 反射

Git

1. git的常用命令有哪些?

Maven

1. Maven 是什么? 主要功能? 坐标? /为什么用maven?

2. 依赖范围?

3. Maven依赖传递原则

4. maven依赖冲突及解决方案

5.  Maven 的生命周期?

5. 聚合和继承的区别与联系 ?

作用:聚合用于快速构建项目; 继承用于快速配置

6. 如何自动化部署平台搭建?

SpringMVC

1. MVC 设计思想 : 主要作用: 解耦

2. Spring MVC的优点:

3. 基本配置文件的作用

4. SpringMVC 的主要执行组件 

5. SpringMVC执行流程

6. SpringMVC 注解 

注解原理:

Spring MVC常用的注解有哪些?

SpingMvc中的控制器的注解一般用哪个,有没有别的注解可以替代?

5. 拦截器和ServletFilter 的区别

SpringMvc里面拦截器是怎么写的:

6. SpringMVC异常处理之XML配置方式

1.Spring MVC的异常处理 ?

2. 如何解决POST请求中文乱码问题,GET的又如何处理呢?

6. SpringMVC面试小题

6.1 Spring MVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?

6.2 SpringMVC和struts2的区别有哪些?

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

6.4 SpringMvc怎么和AJAX相互调用的?

7. Operate - tips : 

1.1 IOC / 依赖注入

1.2 AOP

Spring 

Spring 是什么 : 

1. spring如何管理事务的?

2. ApplicationContext 接口的实现类 (近期常用):

3.Spring 实例化bean 的三种方式

4. 控制反转 IOC -- 依赖注入 DI

5. 循环依赖

6.Jdbc 操作数据库

7. 注解开发

8. IOC 与 AOP

8.1 谈谈你对Spring IOC 的理解

8.2 SpringAOP 的实现机制

8.3  AOP操作术语

8.4 BeanFactory和ApplicationContext有什么区别?

9. 动态代理和cglib 代理

9.1 JDK和cglib代理的区别 :

9.2 cglib 和 jdk 如何选择?

9.3 JDK和cglib字节码生成的区别?

9.4 Cglib 比 JDK快?NO

9.5 Spring如何选择是用JDK还是cglib?

9.6 静态代理库的实现细节

Springboot

1. 常用注解 

2. springboot 的5种异常处理机制

SpringSecurity

SSM整合

Mybatis

Mybatis 是什么?

其他

task:

1.springmvc 九大组件

springmvc 九大组件,到底包括DispatcherServlet吗,核心组件又是谁_xzb5566的专栏-CSDN博客

Spring的IOC


​​​​​​​

​​​​Spring 家族的组成关系

source :  Spring SpringMVC SpringBoot SpringCloud概念、关系及区别_奋斗码农的博客-CSDN博客

        Spring主要是基于IOC反转Beans管理Bean类,主要依存于SSH框架(Struts+Spring+Hibernate)这个MVC框架,所以定位很明确,Struts主要负责表示层的显示,Spring利用它的 IOC 和 AOP 来处理控制业务(负责对数据库的操作),Hibernate主要作用是数据的持久化到数据库

使用Spring的好处?框架能更让我们高效的编程以及更方便的维护我们的系统。

1. 轻量:Spring是轻量的,相对其他框架来说。

2. 控制反转 (IOC) :Spring通过控制反转实现了松散耦合,对象给出他们的依赖,而不是创建或查找依赖的对象们。

3. 面向切面编程(AOP):Spring支持面向切面编程,并且把业务逻辑和系统服务分开。

4. 容器:Spring包含并管理应用中对象的生命周期和配置。

5. MVC框架:Spring的WEB框架是个精心设计的框架,是WEB框架的一个很好的替代品。

6. 事务管理:Spring提供一个持续的事务管理接口,提供声明式事务和编程式事务。

7. 异常处理:Spring提供方便的API把 具体技术相关的异常 转化为 一致的unchecked异常。

        SpringMVC是基于Spring的一个MVC框架,用以替代初期的SSH框架; 是一个轻量级Web框架.(spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext,使得拥有web功能)。MVC为现代web项目开发的一种很常见的模式,简言之Controller(控制器)、将View (视图、用户客户端) 与 Model (模块,业务) 拆分开构成了MVC . 通过把模型-视图-控制器分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

        ( 业内常见的mvc模式的开发框架有Struts1,Struts2等。spring作为专业的开发web项目的开源框架,springMvc为内部的一个模块环节,同样采取mvc设计模式。 所以在使用spring开发web项目时,作为核心环节的MVC可以使用struts1/struts2/springMVC )

        ​​​​​​​Spring Boot是基于Spring的条件注册的一套快速开发整合包.实现了自动配置,降低了项目搭建的复杂度, Spring Boot的核心思想就是约定大于配置,一切自动完成。采用 Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持

        大多数初级人员在搭建项目时话费了很多时间也未必能搭建明白。 spring-boot就是为了解决开发人员这个痛点而诞生的,说白了就是把以前的手动配置的过程自动化封装了,提供默认的配置,借用大牛总结的特点就是: 简单易用,初学者和大牛都可以轻松上手,其中的注解会给使用者提供方便; Spring boot对第三方技术进行了很好的封装和整合,提供了大量第三方接口;可以通过依赖自动配置,不需要XML等配置文件.

三者的发展与联系:[ 依赖关系 :  Spring > spring boot > spring cloud ]
         Spring 最初利用“工厂模式”(DI )和“代理模式”(AOP)解耦应用组件. 大家觉得挺好用,于是按照这种模式搞了一个 MVC 框架(一些用 Spring 解耦的组件),  开发 web 应用( SpringMVC )。 

        然后有发现每次开发都要搞很多依赖,写很多样板代码很麻烦,于是搞了一些懒人整合包(starter ),这套就是 Spring Boot . 

        ​​​​​​​Spring Cloud :现在是大数据、云时代,上云是必然选择. Spring Cloud就是一套分布式服务治理的框架,既然它是一套服务治理的框架, 那么它本身不会提供具体功能性的操作, 更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能。

        对于springCloud框架来说,它和springBoot一样,注重的是微服务的开发,但是springCloud更关注的是全局微服务的整合和管理,相当于管理多个springBoot框架的单体微服务;(Spring Cloud是一系列框架的有序集合。它利用 Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线线、负载均衠、断路器、数据监控等,都可以用 Spring Boot的开发风格做到一键启动和部署)


Java补充

 反射

使用反射, 可以动态获取当前class 信息, 比如方法的信息, 注解的信息, 方法的参数, 属性等.

反射技术的应用场景:

(1) JDBC 加载驱动连接 class.forname

(2) Spring 容器框架IOC 实例化对象 :  通过反射技术实例化bean对象.

(3) 自定义注解生效 (反射+AOP) : 通过反射技术判断当前是否有加自定义注解, 若加上之后会去做一些流程的操作. 

(4) 第三方核心的框架

反射技术的使用:

Git

1. git的常用命令有哪些?

Maven

source : maven面试题_木村的博客-CSDN博客_maven面试题

maven 导包问题 : https://www.jb51.net/article/191944.htm

1. Maven 是什么? 主要功能? 坐标? /为什么用maven?

maven 优点 : 

去公司的时候, 公司用的就是maven. 用git往下down项目, maven管理项目.

也是因为 maven 简化了项目之间的关系, 可以进行依赖管理, 把不同的系统依赖进行统一管理. 而且可以进行依赖之间的传递和继承. 在开发测试时候的部署, 都会相对很高效.

maven缺点 :

maven用的是约定大于配置的策略, 出现问题之后不好调试的.

maven的坐标, 算是唯一标识吧, Maven 的坐标元素包括 : groupId(当前项目的所属组织)、artifactId(实际项目中的一个模块)、version(当前项目的版本)、[maven的三大坐标GAV] 、packaging、classfier 。

用三大坐标可以在仓库中和找到响应的构件jar包.

2. 依赖范围?

注意 : maven 中有直接依赖和间接依赖之分

compile:默认范围全阶段,如果未指定任何范围,则使用该范围。编译依赖项在所有(编译,测试,运行)类路径中都可用。此外,这些依赖关系会传播到依赖的项目

provided:这很像compile,但provided范围表明 JDK或者某个容器提供运行时依赖。它只在编译和测试类路径上可用,不可传递。

runtime:编译不需要依赖项,但需要执行依赖项。它在运行时和测试类路径中,但不在编译类路径中。(servlet-api)   

test:测试时起作用. 只在编译测试代码和运行测试的时候需要,应用的正常运行不需要此类依赖。它不是传递的。(jdbc)     

system:系统依赖范围。该依赖与三种classpath的关系和provided依赖范围完全一致。

但 使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植

3. Maven依赖传递原则

①最短路径原则(依赖传递的路径越短越优先)

②pom文件申明/引入顺序优先 先引入的优先级高于后引入的(路径长度一样,先申明的优先) 

③覆写原则(当前pom文件里申明的直接覆盖父工程传过来的)

注意 : 如果项目期待别人继承了,也看不到依赖的包,那就加上<optional> 

用别人的包 , 但是不全用, 用关键字排除不要的东西  <exclusion>

 <exclusions>
     <exclusion>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
     </exclusion>
 </exclusions>

4. maven依赖冲突及解决方案

source : maven依赖冲突以及解决方法 - Linyb极客之路 - 博客园

5.  Maven 的生命周期?

Maven 中有三个独立的生命周期, Clean 、Default、Site.

5. 聚合和继承的区别与联系 ?

聚合 : 管理项目 <packaging>pom<packaging>

继承 : 父工程里面的jar包,都只是定义,子工程里面,才是真正的引入,只是不需要写版本号,版本号由父工程统一来管理

作用:聚合用于快速构建项目; 继承用于快速配置

作用:聚合用于快速构建项目; 继承用于快速配置


6. 如何自动化部署平台搭建?


SpringMVC

重点: IOC 和 AOP , SpringMVC的执行流程.

1. MVC 设计思想 : 主要作用: 解耦

image.png

2. Spring MVC的优点:

(1)可以支持各种视图技术,而不仅仅局限于JSP;

(2)它是基于组件技术的。全部的应用对象,无论控制器和视图,还是业务对象之类的都是 java组件.并且和Spring提供的其他基础结构紧密集成(如 IOC容器、AOP 等);

(3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。

(4) 支持各种请求资源的映射策略,易于扩展。
(5)不依赖于Servlet API (目标虽是如此,但是在实现的时候确实是依赖于Servlet的)
 

3. 基本配置文件的作用

1. springmvc.xml 配置文件的作用 : 

注解、扫描controller包注解;静态资源映射;视图解析(defaultViewResolver);文件上传(multipartResolver);返回消息json配置。

2. web.xml 配置文件的作用:

web.xml的加载:随Tomcat的启动而启动dispatcherServlet 类,从而加载文件Springmvc.xml

3. 建Springmvc 的步骤 :

image.png

4. 写拦截器的步骤:

image.png

4. SpringMVC 的主要执行组件 

九大基本组件:

🍀 注意 :  前端控制器 DispatcherServlet不是组件, 它的作用是: 处理所有的HTTP请求和响应结果.[相当于转发器],会减少组件间耦合度。

(1)处理器映射器HandlerMapping(不需要程序员开发),

作用:根据request的URL来查找相对应的Handler处理器。因为Handler(相当于Controller)有两种形式,一种是基于的Handler,另一种是基于方法Method的Handler(也就是我们常用的). 

(2)处理器适配器HandlerAdapter

注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。

🍀 关系 :Handler是用来干活的工具,HandlerMapping用于根据需要干的活找到相应的工具,HandlerAdapter是使用工具干活的人。

(3)视图处理器ViewResolver : 进行视图的解析 根据视图逻辑名解析成真正的视图(view). View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)

(4)处理器异常处理器 HandlerExceptionResolver

(5)RequestToViewNameTranslator

(6)LocalResolver

(7)ThemeResolver

(8)MulitipartResolver

(9)FlashMapmanager

5. SpringMVC执行流程

image.png

(1) 用户发送请求至前端控制器DispatcherServlet;

(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;

(3)处理器映射器根据请求url找到具体的处理器(可以根据 xml 配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;

(4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器;

(5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器);

(6)Handler执行完成返回ModelAndView;

(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;

(8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器进行解析;

(9)ViewReslover解析后返回具体View;

(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)

(11)DispatcherServlet响应用户。

source : SpringMVC 组件类大全_xlxxcc的专栏-CSDN博客_springmvc的组件

image.png

6. SpringMVC 注解 

source : springmvc常用注解标签详解 - 梦醒点灯 - 博客园

注解原理:

        注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,最终会调用Annotation Invocation Handler的invoke( )方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。​​​​​​​

Spring MVC常用的注解有哪些?

@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,表示类中所有响应请求的方法都是以该地址作为父路径

@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。

@ResponseBody:注解实现将conreoller( )方法返回对象转化为json对象响应给客户。

SpingMvc中的控制器的注解一般用哪个,有没有别的注解可以替代?

        一般用@Controller注解,也可以使用@RestController, @RestController注解相当于@ResponseBody + @Controller, 表示是表现层,除此之外,一般不用别的注解代替。

  1. @Controller 🍊
  2. @RequestMapping 🍊
  3. @Resource 和 @Autowired 🍊

  1. @RequestParam 🌟
  2. @Component 🌟
  3. @Repository  🌟

  1. @ResponseBody
  2. @PathVariable
  3. @ModelAttribute 和 SessionAttributes 

@Controller  用于标记在一个类上

  • Controller控制器负责处理由DispatcherServlet 分发的请求,把用户请求的数据经过业务处理层处理之后封装成一个Model. 然后把Model返回给对应的View进行展示.
  • @Controller 标记一个类后, 使用@RequestMapping 和 @RequestParam 等注解, 以此来定义URL请求和Controller方法之间的映射,这样的Controller 就能被外界访问到. 
  • 注意: Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象,它们可以通过Controller 的方法参数灵活的获取到。

@Controller 用于标记在一个类上,使用它标记的类就是一个Spring MVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是Spring MVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式:

在Spring MVC 的配置文件中定义MyController 的bean 对象。
在Spring MVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
 

@RequestMapping

  • 这是一个用于处里请求地址映射的注解, 可用于类或方法上. 用于类上,标识类中所有响应请求的方法都是以该地址作为父路径.
  • @RequestMapping属性value 用来指定请求的实际地址, 指定的地址可以是URL模式的
  • @RequestMapping属性method 用来指定请求的方法类型, Get、Post、Put、Delete等.

@ResponseBody

作用: 用于将Controller( )返回对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。

使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;

@Resource 和 @Autowired

  • @Resource 和 @Autowired 都是在bean 注入时使用 ;
  • 共同点 : 两者都恶意写在字段和setter方法上. --- [如果写在字段上, 就不需要写setter方法了]
  • 不同点 : @Autowired 是按照类型装配依赖对象的, 默认情况需要依赖对象存在

@RequestParam

  • 主要用于SpringMVC后台控制层获取参数, 类似于request.getParametter()

@Repository

  • 用于注解dao层,在daoImpl类上面注解

@Component

  • 算是通用注解, 不知道该咋归类的时候就写它, [不建议用]

5. 拦截器和ServletFilter 的区别

拦截器是指通过统一拦截从浏览器发往服务器的请求来完成功能的增强。 
使用场景:解决请求的共性问题,如:乱码问题、权限验证问题等

SpringMvc里面拦截器是怎么写的:

有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可:

<!-- 配置SpringMvc的拦截器 -->
<mvc:interceptors>
	<!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 -->
	<bean id="myInterceptor" class="com.et.action.MyHandlerInterceptor"></bean>
	<!-- 只针对部分请求拦截 -->
 	<mvc:interceptor>
		<mvc:mapping path="/modelMap.do" />
			<bean class="com.et.action.MyHandlerInterceptorAdapter" /> 
	</mvc:interceptor>
</mvc:interceptors>

6. SpringMVC异常处理之XML配置方式

1.Spring MVC的异常处理 ?

答:可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。

2. 如何解决POST请求中文乱码问题,GET的又如何处理呢?

(1)解决post请求乱码问题:
在web.xml中加入:

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(2)get请求中文参数出现乱码解决方法有两个:

①修改tomcat配置文件添加编码 URIEncoding="utf-8"与工程编码一致,如下:

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

②另外一种方法对参数进行重新编码:ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。

String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

6. SpringMVC面试小题

6.1 Spring MVC的控制器是不是单例模式,如果是,有什么问题,怎么解决?

答:是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。

6.2 SpringMVC和struts2的区别有哪些?

(1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。

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

(1)在返回值前面加"forward:"就可以让结果转发,譬如"forward:user.do?name=method4"
(2)在返回值前面加"redirect:"就可以让返回值重定向,譬如"redirect:http://www.baidu.com"

6.4 SpringMvc怎么和AJAX相互调用的?

通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :
(1)加入Jackson.jar
(2)在配置文件中配置json的映射
(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。

7. Operate - tips : 

1. 原用request.getParameter : 

1.request.getParameter() 方法. 获取通过http协议提交来的数据. 通过容器的实现来取得通过get或者post方式提交过来的数据

2.request.getParameter()方法传递的数据,从web客户端到web服务器端,代表HTTP请求数据,该方法返回String类型的数据

request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段

2. 请求参数中文乱码问题

用get方法测试时,tomcat会帮我们处理编码,但post就有可能有乱码,因此需要在web.xml中加上⬇️ ,进行编码处理.

 <!-- 配置过滤器,解决中文乱码的问题 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 
 <!-- 指定字符集 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

4. Date数据格式与自定义转换器

5. ModelAndview 和 json 格式

ModelandView 格式:

image.png

json 格式:

image.png

//请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解找到执行的具体方法. [注解⬇️ ]
@Controller()
//在类中拼接的话,@RequestMapping("/user")能找到的是http://localhost:8080/demo/user/hello
public class hellocontroller{
    @RequestMapping("/hello")
    //RequestMapping中输入hello时,就找到了下面的hello类
    public String hello(){
        System.out.println("hello呀小XN");
        return "success";   //在web.xml中(好想是这里)配置好了,索引的前后缀, 会在配置的地方索引到有改名字的的文件
    }
}



//专门生成随机号的
String uuid = UUID.randomUUID().toString();

//设置文件存放位置,和唯一标识名称
File file = new File(pathname:“/Users/edz/Desktop/”+uuid+originalFilename);


//文件的上传部分
public String upload(HttpServletRequest request, MultipartFile myfile) throws IOException {
   String originalFilename = myfile.getOriginalFilename();
   //UUID.randomUUID().toString()一个自动生成主键的方法
   String uuid = UUID.randomUUID().toString();
   System.out.println(originalFilename);
   File file = new File("/users/edz/Desktop/"+uuid+originalFilename);

// 如果文件不存在,则自动生成一个文件
//      if (!file.exists()){
//          file.mkdir();
//      }

    //第一种方法,将输入流写入到文件中
    InputStream inputStream = myfile.getInputStream();
    FileUtils.copyInputStreamToFile(inputStream, file);

    //第二种, 是第一种方法的合并写法,直接将myflie写到file中。
    myfile.transferTo(file);
    
    return "success"; //这是完成字符串的拼接」=
}

1.1 IOC / 依赖注入

IOC (Inversion of Control) 又被成为依赖注入DI (dependency injection)

IOC 解析, 适合慢慢看 Spring的IOC原理[通俗解释一下] - 牧涛 - 博客园

1.2 AOP


Spring 

Spring 是什么 : 

spring 是一个轻量级的、一站式的开发框架, 在应用的时候, 他帮我们写好了类、属性、方法,并且帮我们给属性赋值,执行方法. 在需要的时候帮我们注入其他代码. 用一些第三方类库来简化工作.

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架

--从大小与开销两方面而言Spring都是轻量级的。

--通过控制反转(IoC)的技术达到松耦合的目的

--提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发

--包含并管理应用对象(Bean)的配置和生命周期,(否则就要自己去new)这个意义上是一个容器。 

--将简单的组件配置、组合成为复杂的应用,这个意义上是一个框架。

1. spring如何管理事务的?

2. ApplicationContext 接口的实现类 (近期常用):

  • classpathxmlApplicationContext [从类路径中找配置文件] 
  • fileSystemxmlApplicationContext [从文件系统中找配置文件] 
  • AnnotationConfigApplicationContext [使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解] 

3.Spring 实例化bean 的三种方式

4. 控制反转 IOC -- 依赖注入 DI

IOC <控制反转Inversion of Control>: 程序在编写时,通过控制反转把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。ioc 解耦只是降低他们的依赖关系,但不会消除

DI <依赖注入 dependency injection>

作用 : 就是用Spring给自动赋值.

5. 循环依赖

6.Jdbc 操作数据库

getbean 的作用, 用来获取applicationContext.xml文件下的bean, ( )中写的是bean的id.

public class AccountTest {
    public static void main(String[] args) {
        
        // 读取配置文件, new 一个对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
        
        // getBean是用来获取applicationContext.xml文件里bean的,()写的是bean的id
        JdbcTemplate jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate");
        // 插入 更新语句
        jdbcTemplate.update("insert into account(name,money) values (?,?)","lisi",3000);
        System.out.println(jdbcTemplate);

        // 查询语句
        String sql = "select max(money) from account";
        Double object = jdbcTemplate.queryForObject(sql, Double.class);
        System.out.println("钱最多的是:" + object);
    }
}

7. 注解开发

source : Spring系列之Spring常用注解总结 - 平凡希 - 博客园

image.png

<context:component-scan base-package="com.banyuan"></context:component-scan> 的作用和bean标签中的内容作用一致. 也存放于bean.xml中.

  • 创建对象的注解 : 

@Component 放在上,相当于一个<bean.id>以下三个注解起的作用也一样, 就是提供了更明确话的语义.

@Controller 一般用于表现层的注解。

@Service 一般用于业务层的注解

@Repository 一般用于持久层的注解, 除了以上三个都用这个注解.

  • 注入数据的注解 :

@Autowired 相当于bean.xml文件中的: <property name="" ref=""> 或<property name="" value="">

 @Qualifier 给 字段注入 时不能独立使用,必须和@Autowire 一起使用;但是给 方法参数注入 时,可以独立使用。

默认,spring会按照类型注入,只有一个类型的时候,id怎么写都是可以找到的,如果有多个相同类型的对象,spring找不到的时候,就需要@Qualifier("loan2"),来指定使用哪个对象

[比如说: 一个接口的实现类有两个的时候, 单用@Autowired的时候不能定位到指定对象, 此时就需要@Qualifier]

@Resource  //的作用相当于@Autowired + @Qualifier 的合体, 是java自带的,不是Spring的. 默认按名称装配, 找不到与名称匹配的bean才会按类型装配/

@PostConstruct  //注解的意思和 <bean init-method>一样, 注解作用在方法上,和init()方法是一样的 是java自带

@PreDestroy  //注解的意思和 <bean destory-method> 一样, 注解作用在方法上,和destory()方法是一样的. 如果是多例的情况下,默认不调用销毁方法. 且ApplicationContext没有close方法,所以要用ClassPathXmlApplicationContext的close方法。

@Scope //注解的范围,相当于:<bean id="" class=""scope="">注解放在类上,取值为:singleton / prototype / request / session / globalsession

如果我们要彻底不用xml文件,所有的东西都全部用注解,怎么办?  答 :在config文件中写的是bean.xml的等效代替文件,用以下注解辅助完成

@Configuration 对应于我们的配置文件beanx.xml,在一个类上标注这个注解即可。

@ComponentScan 在类上标注,对应这段 <context:component-scan base-package="com.banyuan"></context:component-scan> ,这是启动后扫描的包。

@Bean 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。这是第三方的类,我们是无法到类上加@Component的,所以需要用它

@PropertySource 和bean的位置一样, 是写属性信息的

8. IOC 与 AOP

8.1 谈谈你对Spring IOC 的理解

 从三个方面来回答问题 :  容器概念、控制反转、依赖注入

ioc容器: 实际上是存放个种对象和注解的map(key,value),项目启动前会读取配置文件里的bean,扫描注解, 通过反射创建对象到map中. 在代码里需要用到里面的对象时,再通过依赖注入把属性往对象里面注入. ​​​​​​​ 

        实际上就是个map(key,value)里面存的是各种对象(在xml里配置的bean节点、通过注解修饰的实例(bean 对象), @repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的 bean,扫描注解的类, 然后通过反射创建对象放到map里。

        这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过依赖注入把属性往对象里面注入. (@Autowired、@resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性;  根据类型或id注入;id就是对象名)。

控制反转: 创建对象时, 由原来的主动手动创建变成依赖IOC 的自动创建, 控制权颠倒过来了,全部对象的控制权全部上缴给“第三方”IOC容器, ​​​​​​​IOC 相当于是个粘合剂. 

    

依赖注入: “获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自我管理变为 IOC容器主动注入。依赖注入是实现IOC的方法就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象中

8.2 SpringAOP 的实现机制

直白说: 就是把每个组件中共有的一些服务给提取出来, 像打印日志、处理安全问题、进行事务管理这种, 都给封装成一个切面. 然后注入目标对象. AOP 是对比OOP 来说的, 用OOP的方式来实现时, 会导致大量代码的冗余.

AOP 可以在目标方法前后进行一个拦截, 判断目标方法上是否加上了自定义注解, 

系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这 些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心 业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。

当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。OOP允许你定义从 上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。 在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象 (具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情.

8.3  AOP操作术语

source : 详解AOP——用配置文件的方式实现AOP - 花咖 - 博客园

1、Joinpoint(连接点):指那些被拦截到的点。在Spring中这些点指的是方法,因为Spring只支持方法类型的连接点。——即类里面可以被增强的方法,这些方法被称为连接点。

2、Pointcut(切入点):所谓切入点是指我们要对那些 Joinpoint 进行拦截的定义.——在类中,实际被增强的方法就是切入点。(即连接点不一定是切入点,而切入点一定是连接点)

3、Advice(通知 / 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能)

    • 前置通知:在方法之前执行
    • 后置通知:在方法之后执行
    • 异常通知:方法出现异常执行
    • 最终通知:在后置通知之后执行
    • 环绕通知:在方法之前和之后执行

4、Aspect(切面)是切入点和通知(或引介)的结合. —— 把增强应用到具体方法(切入点)的过程称为切面。

5、Target(目标对象):代理的目标对象(要增强的方法所在的类)

6、Weaving(织入):是把增强应用到目标的过程,把 advice 应用到 target 的过程

7、Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类

8、Introduction(引介)引介是一种特殊的通知,在不修改类代码的前提下,Introduction 可以在运行期为类动态地添加一些方法或 Field

8.4 BeanFactory和ApplicationContext有什么区别?

两者都是bean 容器. ApplicationContext是BeanFactory的子接口,AC提供了更完整的功能:
1. 继承MessageSource,因此支持国际化。
2. 统一的资源文件访问方式。
3. 提供在监听器中注册bean的事件。
4. 同时加载多个配置文件。
5. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

  • 加载形式 :

BeanFactroy 采用:延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用 getBean()),才对该Bean进行加载实例化。这样不便于发现一些Spring的配置问题,如果又问题的话, 只有在使用时才会抛出异常.(若Bean的某个属性没有注入,BeanFacotry加载后才报出).

ApplicationContext 它是在容器启动时,一次性创建了所有的Bean。这样容器启动时,就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入. ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保需要的时候可以直接用.

  • 内存占用角度:

相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。 

  • 创建方式: 

BeanFactory通常以编程的方式被创建, ApplicationContext还能以声明的方式创建.BeanFactory需要手动注册, 而ApplicationContext则是自动注册。

9. 动态代理和cglib 代理

🍀 文章特别好 : source : Cglib和jdk动态代理的区别 - 橙发 - 博客园

9.1 JDK和cglib代理的区别 :

最本质的区别 : 动态代理是基于接口实现的[代理类和被代理类使用的是同一个接口]. cglib 中代理与被代理是父子关系.

共同点: 都是在给方法做增强. 意在把重复的东西提取出来, 但是不对原来的方法进行修改.

JDK 动态代理 : 通过利用拦截器+反射机制Proxy成生一个代理接口的匿名类,在调用具体的方法前调用InvocationHandler回调接口实现的,JDK中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率也不高.

cglib 动态代理 : cglib代理类可以不是接口,因为cglib底层是用ASM框架,使用字节码技术生成代理类,使用Java反射的效率要高,cglib不能对声明final的方法进行代理,因为cglib原理是动态生成被代理类的子类

9.2 cglib 和 jdk 如何选择?

1、目标对象生成了接口 默认用JDK动态代理

2、如果目标对象使用了接口,可以强制使用cglib

3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换

9.3 JDK和cglib字节码生成的区别?

1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类

2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的

9.4 Cglib 比 JDK快?NO

1、cglib底层是ASM字节码生成框架,但是字节码技术生成代理类,在JDL1.6之前比使用java反射的效率要高

2、在jdk6之后逐步对JDK动态代理进行了优化,在调用次数比较少时效率高于cglib代理效率

3、只有在大量调用的时候cglib的效率高,但是在1.8的时候JDK的效率已高于cglib

4、Cglib不能对声明final的方法进行代理,因为cglib是动态生成代理对象,final关键字修饰的类不可变只能被引用不能被修改

9.5 Spring如何选择是用JDK还是cglib?

1、当bean实现接口时,会用JDK代理模式

2、当bean没有实现接口,用cglib实现

3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)

9.6 静态代理库的实现细节

 https://blog.csdn.net/tsundere_ning/article/details/120270910


Springboot

1. 常用注解 

source : SpringBoot之常用注解 - Nihaorz - 博客园

• 项目配置注解

1、@SpringBootApplication 注解

查看源码可发现,@SpringBootApplication是一个复合注解,包含了@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan这三个注解。

这三个注解的作用分别为:

        @SpringBootConfiguration:标注当前类是配置类,这个注解继承自@Configuration。并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。

        @EnableAutoConfiguration:是自动配置的注解,这个注解会根据我们添加的组件jar来完成一些默认配置,我们做微服时会添加spring-boot-starter-web这个组件jar的pom依赖,这样配置会默认配置springmvc 和tomcat。

        @ComponentScan:扫描当前包及其子包下被@Component,@Controller,@Service,@Repository注解标记的类并纳入到spring容器中进行管理。等价于<context:component-scan>的xml配置文件中的配置项。

2、@ServletComponentScan:

        Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,这样通过注解servlet ,拦截器,监听器的功能而无需其他配置,所以这次相中使用到了filter的实现,用到了这个注解。

3、@MapperScan:

        spring-boot支持mybatis组件的一个注解,通过此注解指定mybatis接口类的路径,即可完成对mybatis接口的扫描。

        它和@mapper注解是一样的作用,不同的地方是扫描入口不一样。@mapper需要加在每一个mapper接口类上面。所以大多数情况下,都是在规划好工程目录之后,通过@MapperScan注解配置路径完成mapper接口的注入。        添加mybatis相应组建依赖之后。就可以使用该注解。

4、资源导入注解:@ImportResource @Import @PropertySource 这三个注解都是用来导入自定义的一些配置文件。

@ImportResource(locations={}) 导入其他xml配置文件,需要标准在主配置类上。

导入property的配置文件 @PropertySource指定文件路径,这个相当于使用spring的<importresource/>标签来完成配置项的引入。 

@import注解是一个可以将普通类导入到spring容器中做管理.

 2. controller 层

 1. @Controller 表明这个类是一个控制器类,和@RequestMapping配合使用拦截请求,如果不在method中注明请求的方式,默认是拦截get和post请求。

        这样请求会完成后转向一个视图解析器。但在大多微服务搭建的时候,前后端会做分离。

        所以请求后端只关注数据处理,后端返回json数据的话,需要配合@ResponseBody注解来完成。

  • @RestController 是@Controller 和@ResponseBody的结合

@RequestMapping(value="",method= RequestMethod.GET ),我们都需要明确请求方式,也可以写成@GetMapping(value = " ")

2、@CrossOrigin: @CrossOrigin(origins = "", maxAge = 1000) 这个注解主要是为了解决跨域访问的问题。这个注解可以为整个controller配置启用跨域,也可以在方法级别启用。

我们在项目中使用这个注解是为了解决微服在做定时任务调度编排的时候,会访问不同的spider节点而出现跨域问题。

3、@Autowired:  是spring的自动装配,这个注解可以用到构造器,变量域,方法,注解类型上。当我们需要从bean 工厂中获取一个bean时,Spring会自动为我们装配该bean中标记为@Autowired的元素。

4、@EnablCaching:  这个注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。其作用相当于spring配置文件中的cache manager标签。

5、@PathVariable:路径变量注解,@RequestMapping中用{}来定义url部分的变量名,

3. servcie层注解

1、@Service:业务层的组件,事务的切面也会配置在这一层。这个注解不是一定要用。有个泛指组件的注解,当不能确定具体作用的时候 可以用泛指组件的注解托付给spring容器。 

2、@Resource:@Resource和@Autowired一样都可以用来装配bean,都可以标注字段上,或者方法上。 @resource注解不是spring提供的,是属于J2EE规范的注解。

两个之前的区别就是匹配方式上有点不同,@Resource默认按照名称方式进行bean匹配,@Autowired默认按照类型方式进行bean匹配。

4. 持久层注解

1、@Repository:@Repository注解类作为DAO对象,管理操作数据库的对象。

综述 @Component, @Service, @Controller, @Repositoryspring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理

@Component是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能。通过这些注解的分层管理,就能将请求处理,业务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发。我们在正常开发中,如果能用@Service, @Controller, @Repository其中一个标注这个类的定位的时候,就不要用@Component来标注。

2、@Transactional: 通过这个注解可以声明事务,可以添加在类上或者方法上。

在spring boot中 不用再单独配置事务管理,一般情况是我们会在servcie层添加了事务注解,即可开启事务。要注意的是,事务的开启只能在public 方法上。并且主要事务切面的回滚条件。正常我们配置rollbackfor exception时 ,如果在方法里捕获了异常就会导致事务切面配置的失效。

5. 其他相关注解

  • @ControllerAdvice 和 @RestControllerAdvice:通常和@ExceptionHandler、@InitBinder、@ModelAttribute一起配合使用。
  • @ControllerAdvice 和 @ExceptionHandler 配合完成统一异常拦截处理
  • @RestControllerAdvice 是 @ControllerAdvice 和 @ResponseBody的合集,可以将异常以json的格式返回数据
  • 如下面对数据异常返回的统一处理。

image.png


2. springboot 的5种异常处理机制

source: SpringBoot--SpringBoot 5种异常处理机制_我和井盖都笑了博客-CSDN博客_springboot异常处理机制

source : Spring Boot 全局异常处理 - fishpro - 博客园

2.1 五种处理方式顺序:

  1. 局部的异常处理
  2. 全局的异常处理
  3. 实现 HandlerExceptionResolver 接口的异常处理
  4. 返回 SimpleMappingExceptionResolver 对象的异常处理
  5. 自定义 error 页面

2.2  springboot 全局异常的处理步骤: 

  1. 新建一个Class,这里取名为GlobalDefaultExceptionHandler; 
  2. 在class上添加注解,@ControllerAdvice;
  3. 在class中添加一个方法
  4. 在方法上添加@ExcetionHandler拦截相应的异常信息;
  5. 如果返回的是View -- 方法的返回值是ModelAndView;
  6. 如果返回的是String或者是Json数据,那么需要在方法上添加@ResponseBody注解.

2.3 SpringBoot全局异常准备可不可以处理filter:

这个异常处理只能够处理controller中的异常,对于filter中的异常无法捕获,所以我在网上也找了一些,基本上都是继承抽象类或者实现一些接口,比如BasicErrorController/ErrorController,我尝试了一下,未能解决我自己的问题,还是无法拦截filter中的异常,所以我在最后不得已而为之,用了一个不是办法的办法:

那就是直接在filter中将错误信息和错误状态码输出到浏览器,我直接不经过异常处理器,也没有处理异常,而是直接把我想告诉浏览器的信息直接输出.


SpringSecurity


SSM整合


Mybatis

Mybatis 是什么?


其他

1. 强引用、软/ 弱/ 虚 引用

source : 强引用,软引用,弱引用和虚引用总结 - chaoer - 博客园

1.强引用

     以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用(SoftReference)

    如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

   软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA虚拟机就会把这个软引用加入到与之关联的引用队列中。 

3.弱引用(WeakReference)

    如果一个对象只具有弱引用,那就类似于可有可物的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 

    弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

4.虚引用(PhantomReference)

    "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

    虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是 否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

    特别注意,在实际程序设计中一般很少使用弱引用与虚引用,使用软用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

task:

1.springmvc 九大组件

springmvc 九大组件,到底包括DispatcherServlet吗,核心组件又是谁_xzb5566的专栏-CSDN博客

Spring的IOC

bean实例化的三种方式 Bean实例化的三种方式 - RosaDarker - 博客园

beanfactory和applicationcontext的区别

spring的作用域

Spring:been的作用域_Jcsim~-CSDN博客_spring作用域

循环依赖

Spring中的循环依赖解决详解 - 淡墨痕 - 博客园​​​​​​​

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值