Spring
写在前面
相信对于 Spring 这个模块,只要是做过 java 开发的同学都是不陌生的,或多或少都使用过 Spring 体系的框架。对于平时自己做点小项目会写配置文件可能就行了,但是对于秋招面试只懂配置还是不行的。要明白整个框架的运行机制和流程。
这里需要说下“框架”这个问题,现在很多人的文章都说框架不重要, 计算机基础 才是最重要的,这一点我是非常赞同的。但是这个角度是从一个长远的角度看,但是对于一个正在秋招或者即将秋招的同学来说,如果你完全不懂 Java 开发的框架,估计会在这个上面栽跟头。建议大家把 Spring、SpringMVC、SpringBoot 了解下,当然你也可以直接看 SpringBoot,毕竟现在很多公司都已经在使用 SpringBoot 了。我学习这块比较早了,最开始学的是 SSH,后来又学了 SSM,SpringBoot 是后来实习时才学的。
本文主要说下我学习 Spring 的过程,我最开始是看某培训机构的 SSH 视频,然后做个小项目;再后来又看了某培训机构的 SSM 视频,同样做了一个小项目。后来找实习先投递的是一些小公司,都很顺利,只问你会不会用,怎么用。后来投递大公司的时候就不同了,都是问底层机制、源码之类的东西。所以就赶紧回去看书、看源码。看书的话我主要看了两本书(实际上只是翻了翻),如下图所示:
Spring 的源码可能不像 JDK 源码那样简短,比如: AOP 和 IOC 的相关源码特别的长,只能通过打断点 Debug 的方式去看,而且看了很可能会忘的很快。所以建议大家可以搜一搜一些大佬们关于 Spring 源码阅读的笔记,或者结合《Spring源码深度解析》这本书进行学习。
对于 Spring 最重要的就是 AOP 和 IOC,除此之外还有事务是面试中常问的点。其实我个人觉得对于 Spring 的学习,如果你是要秋招的同学,建议你先把 AOP 和 IOC 的大致流程搞懂,不要试图一下把 Spring 整个体系就完全弄明白了,有点不现实,也不需要在这个上面花费过多的时间,重点的几个点认真看下,面试基本上是够了的。时间充足,不需要面试的同学当然是要好好看看的。
说明:因为 Spring 的 AOP 和 IOC 的源码分享非常长,本文不对源码分析做过多阐述,如果你需要可以加我微信:pcwl_Java,备注“秋招资料”,我再分享给你。
1、使用 Spring 框架 的好处是什么?
1. 轻量:Spring 是轻量的,基本的版本大约 2MB。
2. 控制反转:Spring 通过控制反转实现了松散 耦合 ,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
3. 面向切面的编程(AOP):Spring 支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
4. 容器:Spring 包含并管理应用中对象的 生命周期 和配置。
5. MVC框架 :Spring 的 Web 框架是个精心设计的框架,是 Web 框架的一个很好的替代品。
6. 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务( JTA )。
7. 异常处理:Spring 提供方便的 API 把具体技术相关的异常(比如由 jdbc , hibernate or JDO 抛出的)转化为一致的 unchecked 异常。
2、解释下什么是 AOP?
AOP(Aspect-Oriented Programming,面向方面编程),可以说是 OOP (Object-Oriented Programing,面向对象编程)的补充和完善。OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP 则显得无能为力。也就是说,OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如:安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而 AOP 技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的 耦合度 ,并有利于未来的可操作性和可维护性。AOP 代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
使用“横切”技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,它们经常发生在核心关注点的多处,而各处都基本相似。比如:权限认证、日志、事务处理。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
3、AOP 的代理有哪几种方式?
AOP 思想的实现一般都是基于代理模式 ,在 Java 中一般采用 JDK 动态代理模式,但是我们都知道,JDK 动态代理模式只能代理接口而不能代理类。因此,Spring AOP 会按照下面两种情况进行切换,因为 Spring AOP 同时支持 CGLIB 、ASPECTJ、JDK 动态代理。
1. 如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
2. 如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类。不过这个选择过程对开发者完全透明、开发者也无需关心。
4、怎么实现 JDK 动态代理?
JDK 动态代理最核心的一个接口和方法如下所示:
- 1. java.lang.reflect 包中的 InvocationHandler 接口:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
对于被代理的类的操作都会由该接口中的 invoke 方法实现,其中的参数的含义分别是:
1. proxy:被代理的类的实例;
2. method:调用被代理的类的方法;
3. args:该方法需要的参数。
使用方法首先是需要实现该接口,并且我们可以在 invoke 方法中调用被代理类的方法并获得返回值,自然也可以在调用该方法的前后去做一些额外的事情,从而实现动态代理。
- 2. java.lang.reflect 包中的 Proxy 类中的 newProxyInstance 方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgument Exception
其中的参数含义如下:
1. loader:被代理的类的类加载器;
2. interfaces:被代理类的接口数组;
3. invocationHandler:调用处理器类的对象实例。
该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。
5、AOP 的基本概念:切面、连接点、切入点等?
1. 切面(Aspect):官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。
2. 连接点(Joinpoint):程序执行过程中的某一行为。
3. 通知(Advice):“切面”对于某个“连接点”所产生的动作。
4. 切入点(Pointcut):匹配连接点的断言,在 AOP 中通知和一个切入点表达式关联。
5. 目标对象(Target Object):被一个或者多个切面所通知的对象。
6. AOP 代理(AOP Proxy):在 Spring AOP 中有两种代理方式,JDK 动态代理和 CGLIB 代理。
6、通知类型(Advice)型(Advice)有哪些?
1. 前置通知 (Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext 中在 <aop:aspect> 里面使用 <aop:before> 元素进行声明;
2. 后置通知 (After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext 中在 <aop:aspect> 里面使用 <aop:after> 元素进行声明。
3. 返回后通知 (After return advice :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext 中在 <aop:aspect> 里面使用 <<after-returning>> 元素进行声明。
4. 环绕通知 (Around advice):包围一个连接点的通知,类似 Web 中 servlet 规范中的 Filter 的 doFilter 方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext 中在 <aop:aspect> 里面使用 <aop:around> 元素进行声明。
5. 抛出异常后通知 (After throwing advice):在方法抛出异常退出时执行的通知。ApplicationContext 中在 <aop:aspect> 里面使用 <aop:after-throwing> 元素进行声明。
7、谈谈你对 IOC 的理解?
IOC 是 Inversion of Control 的缩写,多数书籍翻译成“控制反转”。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC 理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的 解耦 。如下图:
由于引进了中间位置的“第三方”,也就是 IOC 容器,使得 A、B、C、D 这 4 个对象没有了 耦合关系 ,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC 容器,所以,IOC 容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把 IOC 容器比喻成“粘合剂”的由来。
把上图中间的 IOC 容器拿掉,然后再来看看这套系统:
现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D 这 4 个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现 A 的时候,根本无须再去考虑 B、C 和 D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现 IOC 容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!
我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:
软件系统在没有引入 IOC 容器之前,对象 A 依赖于对象 B,那么对象 A 在初始化或者运行到某一点的时候,自己必须主动去创建对象 B 或者使用已经创建的对象 B。无论是创建还是使用对象 B,控制权都在自己手上。
软件系统在引入 IOC 容器之后,这种情形就完全改变了,由于 IOC 容器的加入,对象 A 与对象 B 之间失去了直接联系,所以,当对象 A 运行到需要对象 B 的时候,IOC 容器会主动创建一个对象 B 注入到对象 A 需要的地方。
通过前后的对比,我们不难看出来:对象 A 获得依赖对象 B 的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
8、 bean 的生命周期?
在传统的 Java 应用中,bean 的生命周期很简单,使用 Java 关键字 new 进行 Bean 的实例化,然后该 Bean 就能够使用了。一旦 Bean 不再被使用,则由 Java 自动进行垃圾回收。
相比之下,Spring 管理 Bean 的生命周期就复杂多了,正确理解 Bean 的生命周期非常重要,因为 Spring 对 Bean 的管理可扩展性非常强,下面展示了一个 Bean 的构造过程:
1. Spring 启动,查找并加载需要被 Spring 管理的 Bean,进行 Bean 的实例化;
2. Bean 实例化后,对 Bean 的引入和值注入到 Bean 的属性中;
3. 如果 Bean 实现了 BeanNameAware 接口的话,Spring 将 Bean 的 Id 传递给 setBeanName() 方法;
4. 如果 Bean 实现了 BeanFactoryAware 接口的话,Spring 将调用 setBeanFactory() 方法,将 BeanFactory 容器实例传入;
5. 如果 Bean 实现了 ApplicationContextAware 接口的话,Spring 将调用 Bean 的 setApplicationContext() 方法,将 Bean 所在应用上下文引用传入进来;
6. 如果 Bean 实现了 BeanPostProcessor 接口,Spring 就将调用它们的 postProcessBefore Initialization () 方法;
7. 如果 Bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet() 方法。类似地,如果 Bean 使用 init-method 声明了初始化方法,该方法也会被调用;
8. 如果 Bean 实现了 BeanPostProcessor 接口,Spring 就将调用它们的 postProcessAfterInitialization() 方法;
9. 此时,Bean 已经准备就绪,可以被应用程序使用了。它们将一直驻留在应用上下文中,直到应用上下文被销毁;
10. 如果 Bean 实现了 DisposableBean 接口,Spring 将调用它的 destory() 接口方法,同样,如果 Bean 使用了 destory-method 声明销毁方法,该方法也会被调用。
9、Bean 的作用域?
1. singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的;
2. prototype : 每次请求都会创建一个新的 bean 实例;
3. request :每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效;
4. session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效;
5. global-session:全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
10、Spring 中的单例 Bean 的线程安全问题了解吗?
大部分时候我们并没有在系统中使用 多线程 ,所以很少有人会关注这个问题。单例 bean 存在线程问题,主要是因为:当多个 线程 操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。常见的有两种解决办法:
1. 在 Bean 对象中尽量避免定义可变的成员变量(不太现实)。
2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
11、谈谈你对 Spring 中的事物的理解?
事务是逻辑上的一组操作,要么都执行,要么都不执行。
- 事务特性
原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性:执行事务前后,数据保持一致;
隔离性:并发访问数据库时,一个用户的事物不被其他事物所干扰,各并发事务之间数据库是独立的;
持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
- Spring 事务管理接口
1. PlatformTransactionManager:(平台)事务管理器;
2. Transaction Definition:事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则);
3. TransactionStatus:事务运行状态;
所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。
12、Spring 中的事务隔离级别?
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离级别, MySQL 默认采用的 REPEATABLE_READ 隔离级别 Oracle 默认采用的 READ_COMMITTED 隔离级别;
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读;
TransactionDefinition.ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生;
TransactionDefinition.ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生;
TransactionDefinition.ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
13、Spring 中的事物传播行为?
事务传播行为是为了解决业务层方法之间互相调用的事务问题。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在 TransactionDefinition 定义中包括了如下几个表示传播行为的常量:
- 支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务;
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行;
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- 不支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起;
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- 其他情况:
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED。
14、Spring 常用的注入方式有哪些?
1. 构造器 依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
2. Setter 方法注入:Setter 方法注入是容器通过调用无参构造器或无参 static 工厂方法实例化 bean 之后,调用该 bean 的 Setter 方法,即实现了基于 Setter 的依赖注入。
3. 基于注解的注入:最好的解决方案是用构造器参数实现强制依赖,Setter 方法实现可选依赖。
15、Spring 框架中用到了哪些设计模式?
1. 工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象;
2. 代理设计模式 : Spring AOP 功能的实现;
3. 单例设计模式 : Spring 中的 Bean 默认都是单例的;
4. 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式;
5. 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源;
6. 观察者模式 :Spring 事件驱动模型就是观察者模式很经典的一个应用;
7. 适配器模式:Spring AOP 的增强或通知(Advice)使用到了适配器模式、SpringMVC 中也是用到了适配器模式适配 Controller。
16、 ApplicationContext 通常的实现有哪些?
1. FileSystemXmlApplicationContext:此容器从一个 XML 文件中加载beans 的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
2. ClassPath XmlApplicationContext:此容器也从一个 XML 文件中加载beans 的定义,这里,你需要正确设置 classpath 因为这个容器将在 classpath 里找 bean 配置。
3. WebXmlApplicationContext:此容器加载一个 XML 文件,此文件定义了一个 Web 应用的所有 bean。
往期精选
面试题:460道Java后端面试高频题答案版【模块一:Java基础】
面试题:460道Java后端面试高频题答案版【模块二:Java集合类】
面试题:460道Java后端面试高频题答案版【模块三:Java并发】
面试题:460道Java后端面试高频题答案版【模块四:Java虚拟机】
面试题:460道Java后端面试高频题答案版【模块五:计算机网络】
面试题:460道Java后端面试高频题答案版【模块六:计算机操作系统】
面试题:460道Java后端面试高频题答案版【模块七:设计模式】
面试题:460道Java后端面试高频题答案版【模块八:MySQL】
面试题:460道Java后端面试高频题答案版【模块十:SpringMVC】
SpringMVC
写在前面
对于 SpringMVC 并没有太多的学习经验分享给大家,因为我是通过某培训机构的视频学习的 SpringMVC。我个人认为 SpringMVC 的高频面试点有以下几个:
1. SpringMVC 的执行流程;
2. SpringMVC 的核心组件;
3. DispatchServlet 的源码分析(出于文章长度的考虑,没有把源码分析写到本篇文章中,需要的同学可以加我微信:pcwl_Java,再分享给你)。
1、谈谈你对 MVC 模式的理解?
MVC 是 Model — View — Controler 的简称,它是一种架构模式,它分离了表现与交互。它被分为三个核心部件:模型、视图、控制器。
Model(模型):程序的主体部分,主要包含业务数据和业务逻辑。在模型层,还会涉及到用户发布的服务,在服务中会根据不同的业务需求,更新业务模型中的数据。
View(视图):是程序呈现给用户的部分,是用户和程序交互的接口,用户会根据具体的业务需求,在 View 视图层输入自己特定的业务数据,并通过界面的事件交互,将对应的输入参数提交给后台控制器进行处理。
Controller(控制器):Controller 是用来处理用户输入数据,以及更新业务模型的部分。控制器中接收了用户与界面交互时传递过来的数据,并根据数据业务逻辑来执行服务的调用和更新业务模型的数据和状态。
2、SpringMVC 的工作原理/执行流程?
简单来说:客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler -> HandlerAdapter 会根据 Handler 来调用真正的处理器来处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象 -> 前端控制器 DispatcherServlet 渲染数据(Model)-> 将得到视图对象返回给用户。
上图用于辅助理解,面试时可用下列 8 步描述 SpringMVC 运行流程:
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 将帮你做一些额外的工作:
(1)HttpMessageConveter:将请求消息(如:Json、xml 等数据)转换成一个对象,将对象转换为指定的响应信息;
(2)数据转换:对请求消息进行数据转换。如:String 转换成 Integer、Double 等;
(3)数据格式化:对请求消息进行数据格式化。如:将字符串转换成格式化数字或格式化日期等;
(4)数据验证:验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中;
5. Handler 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;
6. 根据返回的 ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到 Spring 容器中的 ViewResolver)返回给DispatcherServlet;
7. ViewResolver 结合 Model 和 View,来渲染视图;
8. 将渲染结果返回给客户端。
3、SpringMVC 的核心组件有哪些?
- 1. 前端控制器 DispatcherServlet
作用:Spring MVC 的入口函数。接收请求,响应结果,相当于转发器,中央处理器。有了 DispatcherServlet 减少了其它组件之间的耦合度。用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
- 2. 处理器映射器 HandlerMapping
作用:根据请求的 url 查找 Handler。HandlerMapping 负责根据用户请求找到 Handler 即处理器(Controller),SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
- 3. 处理器适配器 HandlerAdapter
作用:按照特定规则(HandlerAdapter 要求的规则)去执行 Handler。通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
- 4. 处理器 Handler
注意:编写 Handler 时按照 HandlerAdapter 的要求去做,这样适配器才可以去正确执行 Handler。Handler 是继 DispatcherServlet 前端控制器的后端控制器,在 DispatcherServlet 的控制下 Handler 对具体的用户请求进行处理。由于 Handler 涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发 Handler。
- 5. 视图解析器 View resolver
作用:进行视图解析,根据逻辑视图名解析成真正的视图(View )。View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。SpringMVC 框架提供了很多的 View 视图类型,包括:jstlView、freemarkerView、pdfView 等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。
- 6. 视图 View
View 是一个接口,实现类支持不同的 View 类型(jsp、freemarker…)。
注意:处理器 Handler(也就是我们平常说的 Controller 控制器)以及视图层 View 都是需要我们自己手动开发的。其他的一些组件比如:前端控制器 DispatcherServlet、处理器映射器 HandlerMapping、处理器适配器 HandlerAdapter 等等都是框架提供给我们的,不需要自己手动开发。
4、SpringMVC 常用的注解有哪些?
1. @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径;
2. @RequestBody:注解实现接收 HTTP 请求的 json 数据,将 json 转换为 Java 对象;
3. @ResponseBody:注解实现将 Controller 方法返回对象转化为 json 对象响应给客户。
5、@RequestMapping 的作用是什么?
RequestMapping 是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。RequestMapping 注解有六个属性,下面我们把它分成三类进行说明。
- value、method:
1. value:指定请求的实际地址,指定的地址可以是 URI Template 模式;
2. method:指定请求的method类型, GET、POST、PUT、DELETE 等;
- consumes、produces:
1. consumes:指定处理请求的提交内容类型(Content-Type),例如 application/json、text/html;
2. produces:指定返回的内容类型,仅当 request 请求头中的(Accept)类型中包含该指定类型才返回;
- params、header:
1. params:指定 request 中必须包含某些参数值是,才让该方法处理。
2. headers:指定 request 中必须包含某些指定的 header 值,才能让该方法处理请求。
6、如何解决 POST 请求中文乱码问题,GET 的又如何处理呢?
1.解决 POST 请求乱码问题:在 web.xml 中配置一个 CharacterEncodingFilter 过滤器,设置成 utf-8;
2. GET 请求中文参数出现乱码解决方法有两个:
(1)修改 tomcat 配置文件添加编码与工程编码一致,如下:
<ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
(2)对参数进行重新编码:
String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
7、SpringMVC 的控制器是不是单例模式,如果是会有什么问题,怎么解决?
是单例模式,所以在多线程访问的时候有线程安全问题。但是不要使用同步,会影响性能,解决方案是在控制器里面不能写字段。
8、SpringMVC 怎么样设定重定向和转发的?
1. 转发:在返回值前面加 “forward:”,譬如:
"forward:user.do?name=method2"
2. 重定向:在返回值前面加 “redirect:”,譬如:
"redirect:#34;
9、SpringMVC 里面拦截器是怎么写的?
方法一:实现 HandlerInterceptor 接口;
方法二:继承适配器类,接着在接口方法当中,实现处理逻辑,然后在 SpringMVC 的配置文件中配置拦截器即可。
10、SpringMVC 和 Struts2 的区别有哪些?
1. SpringMVC 的入口是一个 Servlet 即前端控制器(DispatchServlet),而 Struts2 入口是一个 filter 过滤器(StrutsPrepareAndExecuteFilter);
2. SpringMVC 是基于方法开发(一个 url 对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),Struts2 是基于类开发,传递参数是通过类的属性,只能设计为多例;
3. Struts2 采用值栈存储请求和响应的数据,通过 OGNL 存取数据;SpringMVC 通过参数解析器是将 request 请求内容解析,并给方法形参赋值,将数据和视图封装成 ModelAndView 对象,最后又将 ModelAndView 中的模型数据通过 request 域传输到页面。jsp 视图解析器默认使用 jstl。
往期精选
如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,
咱们下期见!有需要这份资料完整版的朋友转发后关注我私信回复【666】免费下载!