一、为了降低Java开发的复杂性,Spring采取了那4种关键策略
基于POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码。
二、Spring框架的核心:
IoC容器和AOP模块。
通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
三、Spring由哪些模块组成?
Spring 总共大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被
分别整合在核心容器(Core Container) 、 AOP(Aspect Oriented Programming)
和设备支持(Instrumentation) 、数据访问与集成(Data Access/Integeration) 、
Web、 消息(Messaging) 、 Test等 6模块中。 以下是 Spring 5 的模块结构
图:
spring core:提供了框架的基本组成部分,包括控制反转(Inversion of
Control,IOC)和依赖注入(Dependency Injection,DI)功能。
spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管
理对象称为Bean。
spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框
架式的对象访问方法。
spring jdbc:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂
商特有的错误代码解析, 用于简化JDBC。
spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet
listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进
行单元测试和集成测试。
四、Spring 框架中都用到了哪些设计模式?
1. 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
2. 单例模式:Bean默认为单例模式。
3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
4. 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
5. 观察者模式:
定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
五、Spring框架中有哪些不同类型的事件
Spring 提供了以下5种标准的事件:
1. 上下文更新事件(ContextRefreshedEvent):
在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
2. 上下文开始事件(ContextStartedEvent):
当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
3. 上下文停止事件(ContextStoppedEvent):
当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
4. 上下文关闭事件(ContextClosedEvent):
当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
5. 请求处理事件(RequestHandledEvent):
在Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了
ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。
六、Spring 的 IoC支持哪些功能
Spring 的 IoC 设计支持以下功能:
依赖注入
依赖检查
自动装配
支持集合
指定初始化方法和销毁方法
支持回调某些方法(但是需要实现 Spring 接口,略有侵入)
IoC 在 Spring 里,只需要低级容器就可以实现,2 个步骤:
1. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
2. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。
七、BeanFactory 和 ApplicationContext有什么区别?
ApplicationContext是BeanFactory的子接口。
依赖关系:
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取
bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean
之间的依赖关系。
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具
有的功能外,还提供了更完整的框架功能:
继承MessageSource,因此支持国际化。
统一的资源文件访问方式。
提供在监听器中注册bean的事件。
同时加载多个配置文件。
载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
加载方式:
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean
时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些
存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加
载后,直至第一次使用调用getBean方法才会抛出异常。
ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,
在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所
依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通
过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建
好了。
相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空
间。当应用程序配置Bean较多时,程序启动较慢。
创建方式:
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方
式创建,如使用ContextLoader。
注册方式:
BeanFactory和ApplicationContext都支持BeanPostProcessor、
BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要
手动注册,而ApplicationContext则是自动注册。
BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,
Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我
们可以称之为 “低级容器”。
ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了
更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支
持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等
待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上
下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此
方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新
加载/刷新所有的 bean。
八、解释Spring框架中bean的生命周期
正确理解Spring bean的生命周期非常重要,因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。
上图进行详细描述:
1、Spring对bean进行实例化;
2、Spring将值和bean的引用注入到bean对应的属性中;
3、如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-
Name()方法;
如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方
法,将BeanFactory容器实例传入;
如果bean实现了ApplicationContextAware接口,Spring将调用
setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;
如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-
ProcessBeforeInitialization()方法;
如果bean实现了InitializingBean接口,Spring将调用它们的after-
PropertiesSet()方法。
如果bean使用initmethod声明了初始化方法,该方法也会被调用;
如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-
ProcessAfterInitialization()方法;
此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上
下文中,直到该应用上下文被销毁;
如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方
法。
如果bean使用destroy-method声明了销毁方法,该方法也会被调用。
九、使用@Autowired注解自动装配的过程是怎样的?
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置<context:annotation-config>。
在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
配置项<context:component-scan base-package="pack.pack"/>,其实也包含了自动注入上述processor的功能,因此当使用<context:component-scan/>后,即可将<context:annotation-config/>省去。
<context:annotation-config>实现原理:
当我们需要使用BeanPostProcessor时,直接在Spring配置文件中定义这些Bean显得比较笨拙,例如:
使用@Autowired注解,必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor的Bean:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
使用 @Required注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean:
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
类似地,使用@Resource、@PostConstruct、@PreDestroy等注解就必须声明 CommonAnnotationBeanPostProcessor;使用@PersistenceContext注解,就必须声明 PersistenceAnnotationBeanPostProcessor的Bean。
这样的声明未免太不优雅,而Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式,即使用<context:annotation- config/>隐式地向 Spring容器注册AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor这4个BeanPostProcessor。
十、说一下Spring的事务传播行为
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事
务,如果当前不存在事务,就以非事务执行。
③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事
务,就把当前事务挂起。
⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
十一、说一下 spring 的事务隔离?
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
2. ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
3. ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
4. ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
5. ISOLATION_SERIALIZABLE:串行读,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务
尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
不可重复读 :是指一个事务范围内,多次查询某个数据,却得到不同的结果。在第一个事务中的两次读取数据之间,由于第二个事务的修改,第一个事务两次读到的数据可能就是不一样的。
幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一
次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好
像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一
个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录
就变多或者变少了。
脏读举例:
时间点 | 事务A | 事务B |
---|---|---|
1 | 开启事务A | |
2 | 开启事务B | |
3 | 查询余额为100 | |
4 | 余额增加至150 | |
5 | 查询余额为150 |
不可重复读举例:
时间点 | 事务A | 事务B |
---|---|---|
1 | 开启事务A | |
2 | 开启事务B | |
3 | 查询余额为100 | |
4 | 余额增加至150 | |
5 | 查询余额为100 | |
6 | 提交事务 | |
7 | 查询余额为150 |
幻读举例:
时间点 | 事务A | 事务B |
---|---|---|
1 | 开启事务A | |
2 | 开启事务B | |
3 | 查询id<3的所有记录,共3条 | |
4 | 插入一条记录id=2 | |
5 | 提交事务 | |
6 | 查询id<3的所有记录,共4条 |
十二、解释一下Spring AOP里面的几个名词
(1)切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
(2)连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
(3)通知(Advice):在AOP术语中,切面的工作被称为通知。
(4)切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
(5)引入(Introduction):引入允许我们向现有类添加新方法或属性。
(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7)织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:
编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
十三、Spring通知有哪些类型?
在AOP术语中,切面的工作被称为通知,实际上是程序执行时要通过SpringAOP框架触发的代码段。Spring切面可以应用5种类型的通知:
1. 前置通知(Before):在目标方法被调用之前调用通知功能;
2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。