Spring必备知识点(一)

你离开我真会死。 提交于 2020-03-10 23:48:38

Spring框架的7个模块

组成 Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

核心容器

核心容器提供 Spring框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC)模式将应用程序的配置和依赖性规范实际的应用程序代码分开。

那么我们该如何理解:BeanFactory和FactoryBean

1、 BeanFactory
    BeanFactory定义了IOC容器的最基本形式,并提供了 IOC 容器应遵守的的最基本的接口,也就是 Spring IOC 所遵守的最底层和最基本的编程规范。在Spring代码中,BeanFactory 只是个接口,并不是 IOC 容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory 、 XmlBeanFactory 、 ApplicationContext 等,都是附加了某种功能的实现。

2、 FactoryBean
    一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。 Spring为此提供了一个 org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式。

总结:
Spring的BeanFacotry是一个类工厂,使用它来创建各种类型的Bean,最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean,只不过其中有一种Bean是FacotryBean.
一个Bean 要想成为FacotryBean,必须实现FactoryBean 这个接口。
FactoryBean定义了三个接口方法:
    1)Object getObject():返回由FactoryBean创建的Bean的实例,如果isSingleton()方法返回true,是单例的实例,该实例将放入Spring的缓冲池中;
    2)boolean isSingleton*():确定由FactoryBean创建的Bean的作用域是singleton还是prototype;
    3) getObjectType():返回FactoryBean创建的Bean的类型。
FactoryBean 是一直特殊的bean,它实际上也是一个工厂,我们在通过FactoryBeanName得到的Bean,是FacotryBean创建的Bean,即它通过getObject()创建的Bean.我们要想得到FactoryBean本身,必须通过&FactoryBeanName得到,即在BeanFactory中通过getBean(&FactoryBeanName)来得到 FactoryBean
注:在spring 中是通过BeanFactoryUtils.isFactoryDereference()来判断一个Bean是否是FactoryBean.
spring 内部实现中应该是在通过BeanFacotry 的getBean(String beanName) 来得到Bean时,如果这个Bean是一个FactoryBean,则把它生成的Bean返回,否者直接返回Bean.

Spring 上下文

Spring上下文是一个配置文件,向 Spring框架提供上下文信息。Spring上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

###如何理解context?
从字面意思就应该知道是用来做什么的。可以看到spring的ApplicationContext继承了哪些类以及实现了哪些接口可以看到
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver{} {}
通过集成实现的接口就知道他具有spring里面经典的工厂方法,还有对国际化支持的Message,以及配置信息的Resource,还有spring支持的发布和监听事件功能。一个Context基本上把spring具有的核心功能都包裹起来了,那么这就是spring框架运行需要的环境,也就是常说的上下文。任何一个框架运行都通过一个类来进行描述它执行时的环境,ServletContext也是一样,就是Servlet环境信息。可以将context理解为一个框架执行信息的载体,可以理解问一个框架的门面(门面模式),将框架内部的各个组件信息都通过一个context暴露给外部。

Spring AOP

通过配置管理特性,Spring AOP模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP模块为基于 Spring的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB组件,就可以将声明性事务管理集成到应用程序中。

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。
事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。 
1 、原子性 
事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做 
2 、一致性 
事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。 
3 、隔离性 
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 
4 、持续性 
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
Spring事务管理分为声明式跟编程式两种,具体参考:http://www.cnblogs.com/newsouls/p/3988216.html

Spring DAO

JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO的面向 JDBC 的异常遵从通用的 DAO 异常层次结构

什么是DAO(Data Access Object)层?

首先,以JDBC为基础,开始思考这个问题。
在查询一个用户信息(id,name)的时候,我们常常是这么操作的:
1. 获取一个connection
2. 生成一个statement
3. 拼接SQL语句
4. 查询对象,获取结果集(假设已经找到我们需要的对象)
5. 读取结果集信息,封装成需要的用户对象
6. 关闭结果集,statement,connection

其次,考虑一下Hibernate怎么处理?
1. 建立用户信息对象,并配置好对象关系映射
2. 获取一个Session对象
3. 生产一个Query对象
4. 编写HQL语句
5. 执行Query.list()方法,获取对象信息(这里Hibernate框架根据映射关系,完成对象与关系之间的转换)、
6. 关闭Session对象

细细看来,其实,他们处理都是基本相同的,只是不同的就是在进行封装过程中,略有不同了。

那么我们来看一下Ibatis是如何处理的,
1. 编写PO对象,配置sqlMap文件
2. 生成SqlMapClient对象
3. 操作对应的SQL,返回结果(sql语句,我们写的配置文件中,对象封装由Ibatis框架完成)
4. 关闭SqlMapClient

DAO是Data Access Object数据访问接口,数据访问:故名思义就是与数据库打交道。夹在业务逻辑与数据库资源中间

Spring ORM

Spring框架插入了若干个 ORM框架,从而提供了 ORM的对象关系工具,其中包括 JDO、Hibernate和 iBatis SQL Map。所有这些都遵从Spring的通用事务和 DAO 异常层次结构。

什么是ORM?

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。那么,到底如何实现持久化呢?一种简单的方案是采用硬编码方式,为每一种可能的数据库访问操作提供单独的方法。 
    这种方案存在以下不足: 
    1.持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口 
    2.持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,毒药修改持久化曾的相关程序代码,增加了软件的维护难度。 

    ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。Java典型的ORM中间件有:Hibernate,ibatis,speedframework。 
    ORM的方法论基于三个核心原则: 
  · 简单:以最基本的形式建模数据。 
  · 传达性:数据库结构被任何人都能理解的语言文档化。 
  · 精确性:基于数据模型创建正确标准化了的结构。

ORM的概念 
     让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。 
     当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在DAL中写了很多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。 

     ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。 
     ORM技术特点: 
     1.提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。 
     2.ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。 

ORM的优缺点 
     ORM的缺点是会牺牲程序的执行效率和会固定思维模式。 
     从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。 

     在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。 
     在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。 
     但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。

Spring Web 模块

Web上下文模块建立在应用程序上下文模块之上,为基于 Web的应用程序提供了上下文。所以,Spring框架支持与 Jakarta Struts的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

Spring MVC 框架

MVC框架是一个全功能的构建 Web应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText和 POI。

Spring 框架的功能可以用在任何 J2EE服务器中,大多数功能也适用于不受管理的环境。Spring的核心要点是:支持不绑定到特定 J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE环境(Web或 EJB)、独立应用程序、测试环境之间重用。

Spring的优点


1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 
2.可以使用容易提供的众多服务,如事务管理,消息服务等 
3.容器提供单例模式支持 
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发 
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器 
9.spring的DI机制降低了业务对象替换的复杂性 
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部

什么是DI 机制?

依赖注入Dependency Injection和控制反转Inversion of Control是同一个概念,具体的讲:当某个角色 
需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由spring来完成,然后注入调用者因此也称为依赖注入。 
spring以动态灵活的方式来管理对象 , 注入的两种方式,设置注入和构造注入。 
设置注入的优点:直观,自然 
构造注入的优点:可以在构造器中决定依赖关系的顺序。

什么是AOP

面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面 
1.面向切面编程提供声明式事务管理 
2.spring支持用户自定义的切面 
    面向切面编程(aop)是对面向对象编程(oop)的补充,面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 
    AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,oop是静态的抽象,aop是动态的抽象,是对应用执行过程中的步骤进行抽象,,从而获得步骤之间的逻辑划分。
    aop框架具有的两个特征: 
1.各个步骤之间的良好隔离性 
2.源代码无关性

Hibernate工作原理?为什么使用?

  1. 读取并解析配置文件
  2. 读取并解析映射信息,创建SessionFactory
  3. 打开Session
  4. 创建事务Transaction
  5. 持久化操作
  6. 提交事务
  7. 关闭session
  8. 关闭SessionFactory

为啥要使用呢?

  1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。
  2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现,很大程度上简化了DAO层的编码工作。
  3. Hibernate使用了反射机制而不是字节码增强来实现透明性。
  4. 轻量级,支持各种关系型数据库。

Hibernate如何延迟加载?怎样实现类之间的关系?

当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。

类与类之间的关系主要体现在表与表之间的关系进行操作,它们都是对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many来实现类与类之间的关系。

如何优化Hibernate?

1.使用双向一对多关联,不使用单向一对多

2.灵活使用单向一对多关联

3.不用一对一,用多对一取代

4.配置对象缓存,不使用集合缓存

5.一对多集合使用Bag,多对多集合使用Set

6. 继承类使用显式多态

7. 表字段要少,表关联不要怕多,有二级缓存撑腰(关于Hibernate的缓存机制还需要再研究一下)

Struts工作机制?为什么要使用struts?

Struts的工作流程:

在web应用启动时就会加载初始化ActionServlet,ActionServlet从

struts-config.xml文件中读取配置信息,把它们存放到各种配置对象

当ActionServlet接收到一个客户请求时,将执行如下流程.

-(1)检索和用户请求匹配的ActionMapping实例,如果不存在,就返回请求路径无效信息; 

-(2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中; 

-(3)根据配置信息决定是否需要表单验证.如果需要验证,就调用ActionForm的validate()方法

-(4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActuibErrors对象, 就表示表单验证成功;

-(5)ActionServlet根据ActionMapping所包含的映射信息决定将请求转发给哪个Action,如果相应的 Action实例不存在,就先创建这个实例,然后调用Action的execute()方法;

-(6)Action的execute()方法返回一个ActionForward对象,ActionServlet在把客户请求转发给 ActionForward对象指向的JSP组件;

-(7)ActionForward对象指向JSP组件生成动态网页,返回给客户;

具体细节可以参考:http://blog.csdn.net/shb_derek1/article/details/39500257

为什么要用struts?

JSP、Servlet、JavaBean技术的出现给我们构建强大的企业应用系统提供了可能。但用这些技术构建的系统非常的繁乱,所以在此之上,我们需要一个规则、一个把这些技术组织起来的规则,这就是框架,Struts便应运而生。基于Struts开发的应用由3类组件构成:控制器组件、模型组件、视图组件

关于为何要使用这些个框架,可以看我另外一篇博文

struts的validate框架如何验证?

关于验证的方式,我只想说这是门学问,传送门在这里

struts的设计模式

MVC模式: web应用程序启动时就会加载并初始化ActionServlet。用户提交表单时,一个配置好的ActionForm对象被创建,并被填入表单相应的数据,ActionServler根据Struts-config.xml文件配置好的设置决定是否需要表单验证,如果需要就调用ActionForm的 Validate()验证后选择将请求发送到哪个Action,如果Action不存在,ActionServlet会先创建这个对象,然后调用 Action的execute()方法。Execute()从ActionForm对象中获取数据,完成业务逻辑,返回一个ActionForward对象,ActionServlet再把客户请求转发给ActionForward对象指定的jsp组件,ActionForward对象指定的jsp生成动态的网页,返回给客户。

Spring工作机制

说到底,知道spring这么多概念,还是没有弄清楚它的配置步骤是什么。先看一下我转的这篇博客文章,传送门。大体的步骤通过阅读Spring源码分析也有所了解,Spring的几种机制也大体知道,但是碰到代码还是懵逼,今天我就来解决一下这个问题。

它的大体的处理步骤是这样子的:

 1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

 2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

 3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)

 4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

 5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

 6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

 7. ViewResolver 结合Model和View,来渲染视图

 8. 将渲染结果返回给客户端。

再来个更2B的图,帮助理解更形象一点。

那么Spring的基本配置有哪些?常见的标签及属性又是什么意思?

Spring基本配置

在谈论spring基本配置之前,我们先来看看web工程的基本架构,我个人比较喜欢的一篇在这里。除此之外,你还应该了解一下web.xml的解析顺序。有了以上基础以后,你就可以看下面的基本配置了。

web.xml中配置前端处理器

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
    <!--前端控制器-->
    <!--1.配置spring分发器(是总的控制中心,被拦截的url会汇聚到该servlet) -->
	<servlet>
	    <servlet-name>springmvc</servlet-name>  
	    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	    <!--配置spring上下文  (从该目录下加载spring mvc的配置文件) -->
        <!--从这里也可以深刻理解上下文的含义跟功能-->
	    <init-param>
	       <param-name>contextConfigLocation</param-name>
	       <param-value>/WEB-INF/classes/spring/spring*.xml</param-value>
	    </init-param>
	    <load-on-startup>1</load-on-startup>  <!-- tomcat启动后立即加载 -->
	</servlet>
	
	<!--2. 配置spring拦截的url模板  以.do结尾的url-->
    <!--第二种:/, 所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析-->
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>
	
	<!--3. 注册配置文件读取器,监听spring配置文件的变化 详见 注3-->
	<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener> 
    
    <!--4.  设定配置文件列表 设置全局参数 为3的参数配置信息-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
	    <param-value>/WEB-INF/classes/spring/*Config.xml</param-value>
	</context-param> 
	
	<!--5. 编码字符集统一为UTF-8(过滤器) -->
	<filter>
		<display-name>encodingFilter</display-name>
		<filter-name>encodingFilter</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>
	
	
</web-app>

SpringMVC-servlet.xml配置处理器映射器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd            
    http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.0.xsd                 
    http://www.springframework.org/schema/mvc  http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd                 
    http://www.springframework.org/schema/util  http://www.springframework.org/schema/util/spring-util-3.0.xsd">
  
   <!-- @Controller注解的使用前提配置  详细解释见(注.txt 1. )-->
   <mvc:annotation-driven /> 
  <!--  <context:annotation-config/> -->
   <!-- 对module包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能-->
   <context:component-scan base-package="module">
   </context:component-scan>
   
   <!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
	<bean  class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
		
    <!-- 定义视图解析器,在视图模型前后添加前缀后缀 暂时只支持jsp后缀-->
   <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/" /><!-- 路径前缀 -->
		<property name="suffix" value=".jsp" /><!-- 后缀 -->
	</bean>
	
</beans>

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!