前言:Spring面试内容比较多,分成两部分整理总结,这是第一部分~~
Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是控制反转(Inversion of Control,IOC)和面向切面编程(aspect-oriented programming,AOP)。
为了降低Java开发的复杂性,Spring采取了以下4种关键策略
Java反射:
是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且都能够调用它的任意一个方法;
反射原理:
Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组成部分。 如何得到Class的实例:
1.类名.class(就是一份字节码) 2.Class.forName(String className);根据一个类的全限定名来构建Class对象 3.每一个对象多有getClass()方法:obj.getClass();返回对象的真实类型
使用场景:
Spring设计目标:Spring为开发者提供一个一站式轻量级应用开发平台;
Spring设计理念:在JavaEE开发中,支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;Spring通过IoC容器实现对象耦合关系的管理, 并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦;
Spring框架的核心:IoC容器和AOP模块。通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
优点 ①. 方便解耦,简化开发
Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。
②. AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
③. 声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程。
④. 方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序。
⑤. 方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。
⑥. 降低JavaEE API的使用难度
Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。
缺点 Spring明明一个很轻量级的框架,却给人感觉大而全 Spring依赖反射,反射影响性能 使用门槛升高,入门Spring需要较长时间
工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例; 单例模式:Bean默认为单例模式。 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术; 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被自动更新,如Spring中listener的实现–ApplicationListener。
声明式事务虽然优于编程式事务,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。
要想实现事务管理和业务代码的抽离,就必须得用到 Spring 当中的AOP,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
在配置文件中设置以下6项
(1)、required
如果客户端没有事务 在bean中新起一个事务
如果客户端有事务bean 中就加进去
子事务 | 主事务 | 结果 |
---|---|---|
异常 | 正常,并try-catch异常 | 均回滚 |
正常 | 异常 | 均回滚 |
正常 | 异常,并try-catch异常 | 不回滚 |
(2)、 requiresNew
不管客户端有没有事务服务器段都新起一个事务
如果客户端有事务就将事务挂起
子事务 | 主事务 | 结果 |
---|---|---|
异常 | 正常,并try-catch异常 | 子回滚,主不回滚 |
正常 | 异常 | 子不回滚,主回滚 |
异常 | 正常 | 均回滚 |
(3)、supports
如果客户端没有事务服务端也没有事务
如果客户端有事务服务端就加一个事务
(4)、mandatcry
如果客户端没有事务服务端就会报错
如果客户端有事务服务端就加事务
(5)、notSupported
不管客户端有没有事务服务端都没有事务
如果客户端有事务服务端就挂起
(6)、never
不管客户端有没有事务服务端都没有事务
如果客户端有事务就报错
(7)、NESTED
如果当前存在事务,则在嵌套事务内执行。
如果当前没有事务,则进行与REQUIRED类似的操作
子事务 | 主事务 | 结果 |
---|---|---|
异常 | 正常,并try-catch异常 | 子回滚,主不回滚 |
正常 | 异常 | 均回滚 |
异常 | 正常 | 均回滚 |
Javax.transaction.UserTranscation
JTA 事务可以精确到事务的开始和结束
(1)默认情况下,IOC容器中bean的生命周期分为五个阶段:
(2)当加入了Bean的后置处理器后,IOC容器中bean的生命周期分为七个阶段:
. 调用构造器 或者是通过工厂的方式创建Bean对象
名称 | 作用域 |
---|---|
singleton | 单例对象,默认值的作用域 |
prototype | 每次获取都会创建⼀个新的 bean 实例 |
request | 每⼀次HTTP请求都会产⽣⼀个新的bean,该bean仅在当前HTTP request内有效。 |
session | 在一次 HTTP session 中,容器将返回同一个实例 |
global-session | 将对象存入到web项目集群的session域中,若不存在集群,则global session相当于session |
默认作用域是singleton,多个线程访问同一个bean时会存在线程不安全问题
@Override
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) {
// 第一步:刷新前的预处理
prepareRefresh();
//第二步: 获取BeanFactory并注册到 BeanDefitionRegistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 第三步:加载BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
prepareBeanFactory(beanFactory);
try {
// 第四步:完成BeanFactory准备工作后的前置处理工作
postProcessBeanFactory(beanFactory);
// 第五步:实例化BeanFactoryPostProcessor接口的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步:注册BeanPostProcessor后置处理器,在创建bean的后执行
registerBeanPostProcessors(beanFactory);
// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
// 第八步:注册初始化事件派发器
initApplicationEventMulticaster();
// 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
onRefresh();
// 第十步:注册应用的监听器。就是注册实现了ApplicationListener接口的监听器
registerListeners();
//第十一步:初始化所有剩下的非懒加载的单例bean 初始化创建非懒加载方式的单例Bean实例(未设置属性)
finishBeanFactoryInitialization(beanFactory);
//第十二步: 完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,完成创建
finishRefresh();
}
……
}
总结:
四个阶段
多个扩展点
完整流程
spring的默认bean作用域是单例的,单例的bean不是线程安全的,但是开发中大部分的bean都是无状态的,不具备存储功能,比如controller、service、dao,他们不需要保证线程安全。
如果要保证线程安全,可以将bean的作用域改为prototype,比如像Model View。
另外还可以采用ThreadLocal来解决线程安全问题。ThreadLocal为每个线程保存一个副本变量,每个线程只操作自己的副本变量。
IoC(Inverse of Control:控制反转)是⼀种设计思想,就是将原本在程序中⼿动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语⾔中也有应⽤,并⾮ Spring 特有。
IoC 容器是 Spring⽤来实现 IoC 的载体,将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注⼊。这样可以很⼤程度上简化应⽤的开发,把应⽤从复杂的依赖关系中解放出来。 IoC 容器就像是⼀个⼯⼚⼀样,当我们需要创建⼀个对象的时候,只需要配置好配置⽂件/注解即可,完全不⽤考虑对象是如何被创建出来的。
DI 依赖注入
DI:(Dependancy Injection:依赖注入)站在容器的角度,将对象创建依赖的其他对象注入到对象中。
Spring 的 IoC 设计支持以下功能:
IOC容器初始化是由refresh()方法来启动的,这个方法标志着IOC容器的正式启动。Spring将IOC容器启动的过程分开,并使用不同的模块来完成,如使用ResourceLoader,BeanDefinition等模块, IOC容器的启动主要包括三个过程:
Resource定位过程: Resource定位指beanDefinition的资源定位,由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一的接口。这个过程类似于容器寻找数据的过程。
BeanDefinition的载入: BeanDefinition载入过程指的是把用户定义好的Bean表示为容器内部的数据结构,这个容器的数据结构其实就是BeanDefinition。实际上BeanDefinition就是POJO对象在容器的抽象,通过BeanDefinition来定义的数据结构,像是世间万物在java中的抽象,java的对象又在容器中的抽象就是BeanDefinition。
向容器注册BeanDefinition: 这个注册过程是通过调用BeanDefinitionRegistry接口来完成的,就是把载入过程中解析得到的BeanDefinition向IOC容器进行注册。通过下面的源码可以得到,注册过程就是在IOC容器将BeanDefinition注入到一个HashMap中,IOC容器就是通过这个HashMap来持有BeanDefinition数据的。