《2021最新Java面试题全集-2021年第二版》不断更新完善!

    

第十一章 Spring/SpringMVC

1:为什么要使用 Spring

1.简介

·       目的:解决企业应用开发的复杂性

·       功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

·       范围:任何Java应用

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

2.轻量 

从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

3.控制反转  

Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoCJNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

4.面向切面  

Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

5.容器

Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

6.框架

Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

2:Spring 有哪些主要模块?

Spring5 总共大约有 22 个模块,而这些组件被分别整合在核心容器(Core Container)、AOPAspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成(Data Access/Integeration)、Web、报文发送(Messaging)、Test6 个模块集合中。以下是 Spring 5 的模块结构图:

../../../Desktop/11.png

 

3:解释一下什么是 IoC

IoCInversion of Control的缩写,也就是控制反转

1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IoC 这个概念。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。

IoC理论提出的观点大体是这样的:借助于第三方实现具有依赖关系的对象之间的解耦。如下图

https://img-blog.csdnimg.cn/20190329004147894.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoYWRvd196ZWQ=,size_16,color_FFFFFF,t_70

 

大家看到了吧,由于引进了中间位置的第三方,也就是IOC容器,使得ABCD4个对象没有了耦合关系,齿轮之间的传动全部依靠第三方了,全部对象的控制权全部上缴给第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似粘合剂的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个粘合剂,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成粘合剂的由来。

如果把上图中间的IOC容器拿掉,然后再来看看这套系统:

https://img-blog.csdnimg.cn/20190329004205575.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoYWRvd196ZWQ=,size_16,color_FFFFFF,t_70

 

我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,ABCD4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑BCD了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

我们再来看看,控制反转(IoC)为什么要起这么个名字?我们来对比一下:

软件系统在没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

软件系统在引入IOC容器之后,这种情形就完全改变了,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是控制反转这个名称的由来。

 

4:说说Spring 框架中的 IoC实现?

    Spring 中的 org.springframework.beans 包和org.springframework.context 包构成了 Spring 框架 IoC 容器的基础。

    BeanFactory 接口提供了一个先进的配置机制,使得任何类型的对象的配置成为可能。ApplicationContex 接口对BeanFactory(是一个子接口)进行了扩展,在 BeanFactory的基础上添加了其他功能,比如与 Spring AOP 更容易集成,也提供了处理 message resource 的机制(用于国际化)、事件传播以及应用层的特别配置,比如针对 Web 应用的 WebApplicationContext

    org.springframework.beans.factory.BeanFactory Spring IoC 容器的具体实现,用来包装和管理前面提到的各种beanBeanFactory 接口是 Spring IoC 容器的核心接口。

 

5:什么是IoCDIDI是如何实现的?

IoC叫控制反转,是Inversion of Control的缩写,DIDependency Injection)叫依赖注入,是对IoC更简单的诠释。

控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的"控制反转"就是对组件对象控制权的转移,从程序代码本身转移到了外部容器,由容器来创建对象并管理对象之间的依赖关系。IoC体现了好莱坞原则 - "Don’t call me, we will call you"

依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出来,交给容器来完成。DI是对IoC更准确的描述,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。

依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。

 

6:解释一下什么是AOP

AOPAspect-Oriented Programming,面向方面编程),是一种程序设计范式,该范式以切面(aspect)为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern),AOPOOPObject-Oriented Programing,面向对象编程)的补充和完善。

OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。

例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术则恰恰相反,它利用一种称为横切的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓方面,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

AOP代表的是一个横向的关系,如果说对象是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的方面了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用横切技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。

横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。

AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。

 

7:Spring 常用的注入方式有哪些?

Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:

1.  构造方法注入

2.  setter注入

3.  基于注解的注入

 

8:构造方法注入和setter注入有什么区别?

1setter注入支持大部分的依赖注入,构造方法注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则报错。

2setter注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了setter注入的话,那么构造方法将不能覆盖由setter方法注入的值。因为构造方法尽在对象被创建时调用。

3)在使用setter注入时,有可能不能保证某种依赖是否已经被注入,也就是说这时对象的依赖关系有可能是不完整的。而构造器注入则不允许生成依赖关系不完整的对象。

4)在setter注入时,如果对象 A 和对象 B 互相依赖,在创建对象 A Spring 会抛出 sObjectCurrentlyInCreationException 异常,因为在 B 对象被创建之前 A 对象是不能被创建的,反之亦然。所以 Spring setter注入的方法解决了循环依赖的问题。

 

9:你是如何理解"横切关注"这个概念的?

"横切关注"是会影响到整个应用程序的关注功能,它跟正常的业务逻辑是正交的,没有必然的联系,但是几乎所有的业务逻辑都会涉及到这些关注功能。通常,事务、日志、安全性等关注就是应用中的横切关注功能。

 

10:你如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?

1)连接点(Joinpoint):

程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。

2)切点(Pointcut):

如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。

3)增强(Advice):

增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的,如:BeforeAdviceAfterReturningAdviceThrowsAdvice等。

4)引介(Introduction):

引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的未该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

5)织入(Weaving):

织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:①编译期织入:需要特殊的Java编译期(例如AspectJajc);②装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强;③运行时织入:在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。

6)切面(Aspect):

切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

 

11:BeanFactory ApplicationContext 有什么区别?

BeanFactory 可以理解为含有 bean 集合的工厂类。

BeanFactory 包含了种 bean 的定义,以便在接收到客户端请求时将对应的 bean 实例化。

BeanFactory 还能在实例化对象的时生成协作类之间的关系。此举将 bean 自身与 bean 客户端的配置中解放出来。BeanFactory 还包含了 bean 生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。

从表面上看,ApplicationContext 如同 BeanFactory 一样具有 bean 定义、bean 关联关系的设置,根据请求分发 bean 的功能。但 application context 在此基础上还提供了其他的功能。

1.  提供了支持国际化的文本消息

2.  统一的资源文件读取方式

3.  已在监听器中注册的 bean 的事件

 

12:Spring 中的 bean 是线程安全的吗?

Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性

 

13:阐述Spring框架中Bean的生命周期?

1Spring IoC容器找到关于Bean的定义并实例化该Bean

2Spring IoC容器对Bean进行依赖注入。

3)如果Bean实现了BeanNameAware接口,则将该Beanid传给setBeanName方法。

4)如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。

5)如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。

6)如果Bean实现了InitializingBean接口,则调用其afterPropertySet方法。

7)调用指定初始化方法init

8)如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。

9)当销毁Bean实例时,如果Bean实现了DisposableBean接口,则调用其destroy方法。

 

14:Spring 支持几种 bean 的作用域?

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:

·       singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例

·       prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例

·       request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效

·       session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效

·       globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

 

15:Spring 自动装配 bean 有哪些方式?

Spring容器负责创建应用程序中的bea,同时通过ID来协调这些对象之间的关系。作为开发人员,我们需要告诉Spring要创建哪些bean并且如何将其装配到一起。

Springbean装配有两种方式:

·       隐式的bean发现机制和自动装配

·       Java代码或者XML中进行显示配置

当然这些方式也可以配合使用。

Spring 框架中共有 5 种自动装配方式:

1no:这是 Spring 框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在 bean 定义中用标签明确的设置依赖关系。

2byName:该选项可以根据 bean 名称设置依赖关系。当向一个 bean 中自动装配一个属性时,容器将根据 bean 的名称自动在在配置文件中查询一个匹配的 bean。如果找到的话,就装配这个属性,如果没找到的话就报错。

3byType:该选项可以根据 bean 类型设置依赖关系。当向一个bean 中自动装配一个属性时,容器将根据 bean 的类型自动在在配置文件中查询一个匹配的 bean。如果找到的话,就装配这个属性,如果没找到的话就报错。

4constructor:造器的自动装配和 byType 模式类似,但是仅仅适用于与有构造器相同参数的 bean,如果在容器中没有找到与构造器参数类型一致的 bean,那么将会抛出异常。

5autodetect:该模式自动探测使用构造器自动装配或者byType 自动装配。首先,首先会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在 bean 内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe 的自动装配方式。

 

16:Spring如何处理线程并发问题?

Spring使用ThreadLocal解决线程安全问题

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(RequestContextHolderTransactionSynchronizationManagerLocaleContextHolder)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal

由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的使用。

概括起来说,对于多线程资源共享的问题,同步机制采用了以时间换空间的方式,而ThreadLocal采用了以空间换时间的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

 

17:Spring 框架中有哪些不同类型的事件?

Spring ApplicationContext 提供了支持事件和代码中监听器的功能。我们可以创建 bean 用来监听在ApplicationContext 中发布的事件。

ApplicationEvent 类和在 ApplicationContext 接口中处理的事件,如果一个 bean实现了 ApplicationListener 接口,当一个 ApplicationEvent被发布以后,bean 会自动被通知。

Spring 提供了以下 5 中标准的事件:

1)上下文更新事件(ContextRefreshedEvent):

该事件会在ApplicationContext 被初始化或者更新时发布。也可以在调用 ConfigurableApplicationContext 接口中的 refresh()方法时被触发。

2)上下文开始事件(ContextStartedEvent):

当容器调用ConfigurableApplicationContext Start()方法开始/重新开始容器时触发该事件。

3)上下文停止事件(ContextStoppedEvent):

当容器调用ConfigurableApplicationContext Stop()方法停止容器时触发该事件。

4)上下文关闭事件(ContextClosedEvent):

ApplicationContext 被关闭时触发该事件。容器被关闭时,其管理的所有单例 Bean 都被销毁。

5)请求处理事件(RequestHandledEvent):

Web 应用中,当一个http请求结束触发该事件。除了上面介绍的事件以外,还可以通过扩展ApplicationEvent类来开发自定义的事件。

 

18:Spring 事务实现方式有哪些?

Spring支持编程式事务管理和声明式事务管理。许多Spring框架的用户选择声明式事务管理,因为这种方式和应用程序的关联较少,因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在灵活性方面它弱于编程式事务管理,因为编程式事务允许你通过代码控制业务。

1.  编程式事务管理,我们需要在代码中调用beginTransaction()commit()rollback()等事务管理相关的方法,这就是编程式事务管理。

2.  基于 TransactionProxyFactoryBean 的声明式事务管理

3.  基于 @Transactional 的声明式事务管理

4.  基于 Aspectj AOP 配置事务

 

19:说一下 Spring 的事务隔离?

事务隔离级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,就可能发生以下问题:

·       脏读:一个事务读到另一个事务未提交的更新数据。

·       幻读:例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样。

·       不可重复读:比方说在同一个事务中先后执行两条一模一样的select语句,期间在此次事务中没有执行过任何DDL语句,但先后得到的结果不一致,这就是不可重复读

Spring的事务隔离跟数据库的是一样的,也是:READ-UNCOMMITTEDREAD-COMMITTEDREPEATABLE-READSERIALIZABLE这几种。

 

20:说说Spring的事务传播?

事务传播行为(为了解决业务层方法之间互相调用的事务问题): 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

TransactionDefinition定义中包括了如下几个表示传播行为的常量:

1)支持当前事务的情况:

TransactionDefinition.PROPAGATION_REQUIRED 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

TransactionDefinition.PROPAGATION_SUPPORTS 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

2)不支持当前事务的情况:

TransactionDefinition.PROPAGATION_REQUIRES_NEW 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NEVER 以非事务方式运行,如果当前存在事务,则抛出异常。

3)其他情况:

TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED

 

21:Spring 框架中都用到了哪些设计模式?

Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:

代理模式AOPremoting中被用的比较多。

单例模式spring配置文件中定义的bean默认为单例模式。

模板方法用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate

前端控制器—Spring提供了DispatcherServlet来对请求进行分发。

依赖注入贯穿于BeanFactory / ApplicationContext接口的核心理念。

工厂模式—BeanFactory用来创建对象的实例

 

22:说一下 Spring mvc 运行流程?

Spring MVC运行流程图:

 

https://img-blog.csdnimg.cn/20190329004255659.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoYWRvd196ZWQ=,size_16,color_FFFFFF,t_70

Spring运行流程描述:

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

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

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

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

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

·       数据转换:对请求消息进行数据转换。如String转换成IntegerDouble

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

·       数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResultError

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

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

7ViewResolver 结合ModelView,来渲染视图;

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

 

23:Spring mvc 有哪些组件?

Spring MVC的核心组件:

1.  DispatcherServlet:中央控制器,把请求给转发到具体的控制类

2.  Controller:具体处理请求的控制器

3.  HandlerMapping:映射处理器,负责映射中央处理器转发给controller时的映射策略

4.  ModelAndView:服务层返回的数据和视图层的封装类

5.  ViewResolver:视图解析器,解析具体的视图

6.  Interceptors :拦截器,负责拦截我们定义的请求然后做处理工作

 

24:@RequestMapping 的作用是什么?

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

RequestMapping注解有六个属性,下面我们把她分成三类进行说明。

value method

·       value:指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);

·       method:指定请求的method类型, GETPOSTPUTDELETE等;

consumesproduces

·       consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html

·       produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

paramsheaders

·       params 指定request中必须包含某些参数值是,才让该方法处理。

·       headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。

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

    一般使用@Controller注解标识控制器。

    也可以使用@RestController注解替代@Controller注解,@RestController相当于@ResponseBody@Controller,表示控制器中所有的方法都返回JSON格式数据。

 

26:怎样在控制器方法里面得到request或者session

    直接在控制器方法的形参中声明requestsessionSpringMvc就会自动把它们注入。

@RequestMapping("/login")

public String login(HttpServletRequest request, HttpSession session){}

 

27:如果想在拦截的方法里面得到从前台传入的参数,怎么得到?

    直接在控制器方法的形参里面声明这个参数就可以,但名字必须和传过来的参数名称一样,否则参数映射失败。

   下面方法形参中的userId,就会接收从前端传来参数名称为userId的值。

@RequestMapping("/deleteUser")

public void deleteUser(Long userId){//删除用户操作...}

 

28:前台传入多个参数,并且这些参数都是一个对象的属性,怎么进行参数绑定?

    直接在控制器方法的形参里面声明这个参数就可以,SpringMvc就会自动会请求参数赋值到这个对象的属性中。

    下面方法形参中的user用来接收从前端传来的多个参数,参数名称需要和User实体类属性名称一致。

@RequestMapping("/saveUser")

public void saveUser(User user){//保存用户操作...}

 

29:SpringMVC用什么对象从后台向前台传递数据的?

    使用MapModelModelMap的方式,这种方式存储的数据是在request域中。

@RequestMapping("/getUser")

public String getUser(Map<String,Object> map,Model model,ModelMap modelMap){   

//1.放在map     

map.put("name", "abcd");   

//2.放在model里,一般是使用这个   

model.addAttribute("name", "abcd");   

//3.放在modelMap    

modelMap.addAttribute("name", "abcd");   

modelMap.put("gender", "male");   

}

使用request的方式

@RequestMapping("/getUser")

public String getUser(Map<String,Object> map,Model model,ModelMap modelMap,HttpServletRequest request){   

//放在request     

request.setAttribute("user", userService.getUser());   

return "userDetail";

}

使用ModelAndView

@RequestMapping("/getUser") 

public ModelAndView getUser(ModelAndView modelAndView){   

mav.addObject("user", userService.getUser());       

mav.setViewName("userDetail");     

return modelAndView; 

}

 

30:SpringMVC 的控制器是不是单例模式,如果是,有什么问题,怎么解决?

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

 

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

1)转发:在返回值前面加"forward:",如"forward:user.do?name=abcd"

2)重定向:在返回值前面加"redirect:",如"redirect:http://www.baidu.com"