# 什么是Spring框架?
Spring是一种轻量级的开发框架,旨在提高开发人员的开发效率以及系统的可维护性。我们一般情况下说的Spring框架是指Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。
Spring Core:基础,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。
Spring Aspects:该模块为与AspectJ的集成提供支持。
Spring AOP:提供面向切面的编程实现。
Spring JDBC:Java数据库连接。
Spring JMS:Java消息服务。
Spring ORM:用于支持Hibernate等ORM工具。
Spring Web:为创建Web应用程序提供支持。
Spring Test:提供了对JUnit和TestNG测试的支持。
# Spring框架的优点
- 轻量: Spring 是轻量的,基本的版本大约2MB
- **控制反转:**Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们
- **面向切面的编程(AOP):**Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开
- **容器:**Spring 包含并管理应用中对象的生命周期和配置
- **MVC框架:**Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品
- **事务管理:**Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)
- **异常处理:**Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked 异常
# Spring的Bean生命周期
- Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
- Bean实例化后对将Bean的引入和值注入到Bean的属性中
- 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
- 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
- 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
- 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
- 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
- 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
- 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
# 谈谈你对控制反转的理解
Ioc,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
# ApplicationContext通常的实现
ApplicationContext
通常有三种实现方式:FileSystemXmlApplicationContext
、ClassPathXmlApplicationContext
、WebXmlApplicationContext
。
FileSystemXmlApplicationContext
:此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。ClassPathXmlApplicationContext
:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。WebXmlApplicationContext
:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
# Spring Bean作用域
Spring 3 中为 Bean 定义了 5 种作用域,分别为 singleton(单例)、prototype(原型)、 request、session 和 global session,5 种作用域说明如下:
singleton:单例模式,Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个 Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是 Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:
<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>
prototype:原型模式,每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton作用域。
request:在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的Bean,而且该bean仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean 实例也将会被销毁。
<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
session:在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请 求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次 session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求 内有效,请求结束,则实例将被销毁。
global Session:在一个全局的Http Session中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效。
# 谈谈你对MVC的理解
MVC是Model—View—Controler的简称。即模型—视图—控制器。MVC是一种设计模式,它强制性的把应用程序的输入、处理和输出分开。
MVC中的模型、视图、控制器它们分别担负着不同的任务。
视图: 视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并接受用户的输入。视图不进行任何业务逻辑处理。
模型: 模型表示业务数据和业务处理。相当于JavaBean。一个模型能为多个视图提供数据。这提高了应用程序的重用性
控制器: 当用户单击Web页面中的提交按钮时,控制器接受请求并调用相应的模型去处理请求。
然后根据处理的结果调用相应的视图来显示处理的结果。
MVC的处理过程:首先,控制器接受用户的请求,调用相应的模型来进行业务处理,并返回数据给控制器。控制器调用相应的视图来显示处理的结果。并通过视图呈现给用户。
# Spring MVC 原理
Spring 的模型-视图-控制器(MVC)框架是围绕一个 DispatcherServlet 来设计的,这个 Servlet 会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染 等,甚至还能支持文件上传。
- 客户端(浏览器)发送请求,直接请求到
DispatcherServlet
。 DispatcherServlet
根据请求信息调用HandlerMapping
,解析请求对应的Handler
。- 解析到对应的
Handler
(也就是我们平常说的Controller
控制器)后,开始由HandlerAdapter
适配器处理。 HandlerAdapter
会根据Handler
来调用真正的处理器开处理请求,并处理相应的业务逻辑。- 处理器处理完业务后,会返回一个
ModelAndView
对象,Model
是返回的数据对象,View
是个逻辑上的View
。 ViewResolver
会根据逻辑View
查找实际的View
。DispaterServlet
把返回的Model
传给View
(视图渲染)。- 最后,
DispacherServlet把
View
返回给请求者(浏览器)
# Spring MVC中注解及作用
@Controller
:用于标识控制层组件,标识这个类是一个控制器,分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping
注解。@RestController
:相当于@Controller
和@ResponseBody
的效果@Component
:泛指组件,当组件不好归类时,我们可以使用这个组件进行标注@Respository
:用于注解dao层,在daoImpl实现类上面注解@Service
:用于标注业务层组件@RequestMapping
:一个用来处理请求地址映射的注解,可以用于方法或者类上,用于类上,表示类中所有响应请求的方法都以该方法作为父路径。@Autowired
:它可以对类成员变量、方法或构造函数进行标注,完成自动装配的工作。通过@Autowired
注解可以消除set、get方法。@ResponseBody
:将java对象转成json,并且发送给客户端@RequestBody
:将客户端请求过来的json转成java对象@RequestParam
:当表单参数和方法形参名字不一致时,做一个名字映射@PathVarible
:用于获取uri中的参数,比如user/1中1的值@Valid
:实现数据校验,可以结合hibernate validator一起使用@CookieValue
:用来获取Cookie中的值@RequestHeader
:用来把Request请求header部分的值绑定到参数上
# 将一个类声明为Spring的bean的注解有哪些
我们一般使用@Autowired
注解去自动装配bean。而想要把一个类标识为可以用@Autowired
注解自动装配的bean,可以采用以下的注解实现:
@Component
注解。通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。@Controller
注解。对应Spring MVC的控制层,即Controller层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。@Service
注解。对应服务层,即Service层,主要涉及一些复杂的逻辑,需要用到Dao层(注入)。@Repository
注解。对应持久层,即Dao层,主要用于数据库相关操作。
# Srping事务
# Spring事务的定义与特性
事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。
原子性 (atomicity):强调事务的不可分割. 一致性 (consistency):事务的执行的前后数据的完整性保持一致. 隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰 持久性(durability) :事务一旦结束,数据就持久到数据库
# Spring事务中的隔离级别有哪几种
在TransactionDefinition
接口中定义了五个表示隔离级别的常量:
ISOLATION_DEFAULT
:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ
隔离级别;Oracle默认采用的READ_COMMITTED
隔离级别。
ISOLATION_READ_UNCOMMITTED
:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
ISOLATION_READ_COMMITTED
:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
ISOLATION_REPEATABLE_READ
:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
ISOLATION_SERIALIZABLE
:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
# Spring事务中的脏读、不可重复读、幻读是什么
脏读 :一个事务读到了另一个事务的未提交的数据。
不可重复读 :一个事务读到了另一个事务已经提交的update
的数据导致多次查询结果不一致。
幻读 :一个事务读到了另一个事务已经提交的insert
的数据导致多次查询结果不一致。
# Spring事务中有哪几种事务传播行为
事务传播行为指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
在TransactionDefinition
接口中定义了7个表示事务传播行为的常量。
支持当前事务的情况:
PROPAGATION_REQUIRED
:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
PROPAGATION_SUPPORTS
: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
PROPAGATION_MANDATORY
: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory
:强制性)。
不支持当前事务的情况:
PROPAGATION_REQUIRES_NEW
: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NOT_SUPPORTED
: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
PROPAGATION_NEVER
: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
PROPAGATION_NESTED
: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED
。
# Spring支持的事务管理类型
Spring支持两种类型的事务管理:编程式事务管理和声明式事务管理
- 编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
- 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
# Transactional失效场景
Transactional失效场景可以分为三种:
- 第一种Transactional注解标注方法修饰符为非public时,
@Transactional
注解将会不起作用。 - 在类内部调用调用类内部
@Transactional
标注的方法。这种情况下也会导致事务不开启。 - 事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
# Spring AOP基本概念
AOP
(Aspect Orient Programming),直译过来就是面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP
)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
AOP
把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。
从该图可以很形象地看出,所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
通过AOP,我们可以在不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码。
# AOP的应用场景
- 日志处理
- 用户登录
- 权限(Authentication )
- 性能优化(Performance optimization)
- 事务(Transactions )
- 记录跟踪 优化 校准(logging, tracing, profiling and monitoring)
- 调试(Debugging)
- 懒加载(Lazy loading)
- 错误处理(Error handling)
- 资源池(Resource pooling)
- 同步(Synchronization)
# AOP的优点
- 降低模块之间的耦合度。
- 使系统容易扩展。
- 更好的代码复用。
# AOP的底层实现
AOP 实现的关键在于**[代理模式]**,AOP 主要分为静态代理和动态代理。静态代理的代表为 AspectJ;动态代理的代表为 Spring AOP。
所谓静态代理,就是 AOP 框架会在编译阶段生成 AOP 代理类,因此也称为编译时增强,它会在编译阶段将 AspectJ(切面)weaving(织入)到 Java 字节码中,运行的时候就是增强之后的 AOP 对象。
所谓动态代理,就是 AOP 框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个 AOP 对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并调回原对象。
# AOP实现方式
类型 | 原理 | 优点 | 缺点 |
---|---|---|---|
静态AOP | 在编译期,切面直接以字节码的形式编译到目标字节 码文件中。 AspectJ属于静态AOP,是在编译时进行增强,会在编译的时候 将AOP逻辑织入到代码中,需要专有的编译器和织入器。 | 被织入的类性能不受影响。 | 不够灵活 |
动态AOP(JDK动态代理) | 在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中。 Java从1.3引入动态代理。实现原理是为被代理的业务接口生成代理类, 将AOP逻辑写入到代理类中,在运行时动态织入AOP,使用反射执行织入的逻辑。 主要实现方式依赖java.lang.reflect包下的InvocationHandler和Proxy类。 | Java标准库原生支持,使用简单 ,无需引用额外的包。相对于静态AOP更灵活。 | 被代理的类必须是接口,灵活性受到一些限制;使用反射会影响一些性能。 |
动态代码字节生成(CGLib) | 在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中。 | 没有接口也可以织入,灵活性高。 | 扩展类的实例方法为final时,则无法进行织入 |
自定义类加载器 | 在运行期,目标加载前,将切面逻辑加到目标字节码中。 | 可以对绝大部分类织入。 | 如果用到了其他类加载器,则这些类将不被织入 |
# AOP常用术语
AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
# Spring AOP通知类型
注解 | 通知 |
---|---|
@Before | 通知方法在目标方法调用前执行 |
@After | 通知方法在目标方法返回或异常后执行 |
@AfterReturning | 通知方法在目标方法返回后执行 |
@AfterThrowing | 通知方法在目标方法异常后执行 |
@Around | 通知方法将目标方法封装起来 |
# Spring AOP切点指示器
Spring AOP 所支持的 AspectJ 切点指示器
在Spring中尝试使用AspectJ其他指示器时,将会抛出IllegalArgumentException异常。
当我们查看上面展示的这些spring支持的指示器时,注意只有execution指示器是唯一的执行匹配,而其他的指示器都是用于限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器,在此基础上,我们使用其他指示器来限制所匹配的切点。
下图的切点表达式表示当Instrument的play方法执行时会触发通知。
我们使用execution指示器选择Instrument的play方法,方法表达式以 *
号开始,标识我们不关心方法的返回值类型。然后我们指定了全限定类名和方法名。对于方法参数列表,我们使用 ..
标识切点选择任意的play方法,无论该方法的入参是什么。
多个匹配之间我们可以使用链接符 &&
、||
、!
来表示 “且”、“或”、“非”的关系。但是在使用 XML 文件配置时,这些符号有特殊的含义,所以我们使用 “and”、“or”、“not”来表示。