Spring
1.1 什么是Spring IOC 和DI ?
① 控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我
们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控
制反转的思想。
② 依赖注入(DI):Spring使用Java Bean对象的Set方法或者带参数的构造方法为
我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本
思想。
1.2 有哪些不同类型的依赖注入实现方式?
依赖注入分为Setter方 法注入和构造器注入
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类
有一系列参数,每 个参数代表一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方
法实例化bean之 后,调用该bean的setter方法,即实现了基于setter的依赖注
入。
1.3 Spring支持的几种bean的作用域
Spring框架支持以下五种bean的作用域:
singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的SpringApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的 Spring ApplicationContext情形下有效。
application:属于应用程序域,应用程序启动时bean创建,应用程序销毁时bean销毁。该作用域仅在基于web的ServletContext.
1.4 Spring框架中的单例bean是线程安全的吗?
不是线程安全的
当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线
程会并发执行该请求对应的业务逻辑(成员方法),如果该处理逻辑中有对该单
列状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程
安全和并发问题需要开发者自行去搞定。
Spring bean并没有可变的状态(比如Service类和DAO类),所以在某种程度上说
Spring的单例bean是线程安全的。
如果你的bean有多种状态的话(比如 View Model对象),就需要自行保证线程
安全。最浅显的解决办法就是将多态bean的作用由“singleton”变更为
“prototype”。
1.5 Spring自动装配 bean 有哪些方式?
spring的自动装配功能的定义:无须在Spring配置文件中描述javaBean之间的依
赖关系(如配置 <property>、<constructor-arg> )。
自动装配模式:
1、no:这是 Spring 框架的默认设置,在该设置下自动装配是关闭的,开发者需
要自行在 bean 定义中用标签明确的设置依赖关系 。
2、byName:该选项可以根据bean名称设置依赖关系 。 当向一个bean中自动装
配一个属性时,容器将根据bean的名称自动在在配置文件中查询一个匹配的
bean。 如果找到的话,就装配这个属性,如果没找到的话就报错 。
3、byType:该选项可以根据 bean 类型设置依赖关系 。 当向一个 bean 中自动
装配一个属性时,容器将根据 bean 的类型自动在在配置文件中查询一个匹配的
bean。 如果找到的话,就装配这个属性,如果没找到的话就报错 。
4、constructor :构造器的自动装配和byType模式类似,但是仅仅适用于与有构
造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean ,
那么将会抛出异常 。
5、default:该模式自动探测使用构造器自动装配或者byType自动装配 。 首先会
尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在
bean内部没有找到相应的 构造器或者是无参构造器,容器就会自动选择 byTpe
的自动装配方式 。
比如如下注入 :
可以改造为:
1.6 你用过哪些重要的Spring注解?
1、@Component- 用于服务类
@Service
@Repository
@Controller
2、@Autowired - 用于在 spring bean 中自动装配依赖项。通过类型来实现自动
注入bean。和@Qualifier注解配合使用可以实现根据name注入bean。
3、@Qualifier - 和@Autowired一块使用,在同一类型的bean有多个的情况下可
以实现根据name注入的需求。
4、@Scope - 用于配置 spring bean 的范围。
5、@Configuration,@ComponentScan 和 @Bean - 用于基于 java 的配置。
6、@Aspect,@Before,@After,@Around,@Pointcut - 用于切面编程(AOP)
1.7 Spring中的事务是如何实现的
Spring支持编程式事务管理和声明式事务管理两种方式!
编程式事务控制:需要使用TransactionTemplate来进行实现,这种方式实现对业
务代码有侵入性,因此在项目中很少被使用到。
声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,
对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在
目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者
回滚事务。
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需
在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可
以将事务规则应用到业务逻辑中。
1.8 Spring中事务失效的场景?
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有
是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是目标对象调用, 那
么@Transactional会失效
同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是
基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的
利⽤代理,也会导致@Transactianal失效
如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常,
@Transactional也会失效
@Transactional中的配置的Rollback的默认是异常是:runTimeException,更改为
Spring的事务传播行为 Exception
1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当
前存在事务,就 加入该事务,该设置是最常用的设置。
2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事
务,如果当前不 存在事务,就以非事务执行。
3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该
事务,如果当前 不存在事务,就抛出异常。
4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创
建新事务。
5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事
务,就把当前 事务挂起。
6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异
常。
7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当
前没有事务,则 按REQUIRED属性执行
1.10 什么是AOP , 你们项目中有没有使用到AOP
AOP一般称为面向切面编程,作为面向对象的一种补充,用于 将那些与业务无
关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模
块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块
间的耦合度,同时 提高了系统的可维护性。
在我们的项目中我们自己写AOP的场景其实很少 , 但是我们使用的很多框架的功
能底层都是AOP , 例如 :
1、统一日志处理
2、spring中内置的事务处理
记录操作日志
需求:是谁,在什么时间,修改了数据(修改之前和修改之后),删除了什
么数据,新增了什么数据
要求:方法命名要规范
实现步骤:
1,定义切面类
2,使用环绕通知,根据方法名和参数,记录到表中
1.11 JDK动态代理和CGLIB动态代理的区别
Spring中AOP底层的实现是基于动态代理进行实现的。
常见的动态代理技术有两种:JDK的动态代理和CGLIB。
两者的区别如下所示:
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
进行增强,但是因为采用的是继承,所以该类或方法最好不要声明为final,对于
final类或方法,是无法继承的。
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,会用cglib实现
3、可以强制使用cglib
在springboot项目可以配置以下注解,强制使用cglib
@EnableAspectJAutoProxy(proxyTargetClass = true)
spring的bean的生命周期
bean 的生命周期从调用 beanFactory 的 getBean 开始,到这个 bean 被销毁,可
以总结为以下七个阶段:
1. 处理名称,检查缓存
2. 处理父子容器
3. 处理 dependsOn
4. 选择 scope 策略
5. 创建 bean (关键阶段)
6. 类型转换处理
7. 销毁 bean
spring框架在创建的bean的时候都会调用AbstractBeanFactory类中的doGetBean
方法
1. 处理名称,检查缓存
这一步会处理别名,将别名解析为实际名称
对 FactoryBean 也会特殊处理,如果以 & 开头表示要获取 FactoryBean 本身,否则表示要获取其产品
这里针对单例对象会检查一级、二级、三级缓存
singletonFactories 三级缓存,存放单例工厂对象
earlySingletonObjects 二级缓存,存放单例工厂的产品对象
如果发生循环依赖,产品是代理;无循环依赖,产品是原始对象
singletonObjects 一级缓存,存放单例成品对象
2. 处理父子容器
如果当前容器根据名字找不到这个 bean,此时若父容器存在,则执行父容器的 getBean 流程
父子容器的 bean 名称可以重复
3. 处理 dependsOn
如果当前 bean 有通过 dependsOn 指定了非显式依赖的 bean,这一步会提前创建这些 dependsOn 的 bean
所谓非显式依赖,就是指两个 bean 之间不存在直接依赖关系,但需要控制它们的创建先后顺序
4. 选择 scope 策略
对于 singleton scope,首先到单例池去获取 bean,如果有则直接返回,没有再进入创建流程
对于 prototype scope,每次都会进入创建流程
对于自定义 scope,例如 request,首先到 request 域获取 bean,如果有则直接返回,没有再进入创建流程
5.1 创建 bean - 创建 bean 实例
要点 | 总结 |
AutowiredAnnotationB eanPostProcessor |
① 优先选择带 @Autowired 注解的构造;② 若有 唯一的带参构造,也会入选 |
采用默认构造 | 如果上面的后处理器和 BeanDefiniation 都没找到构 造,采用默认构造,即使是私有的 |
5.2 创建 bean - 依赖注入
要点 | 总结 |
AutowiredAnnotation BeanPostProcessor |
识别 @Autowired 及 @Value 标注的成员,封装为 InjectionMetadata 进行依赖注入 |
CommonAnnotationBe anPostProcessor |
识别 @Resource 标注的成员,封装为 InjectionMetadata 进行依赖注入 |
AUTOWIRE_BY_NAME | 根据成员名字找 bean 对象,修改 mbd 的 propertyValues,不会考虑简单类型的成员 |
AUTOWIRE_BY_TYPE | 根据成员类型执行 resolveDependency 找到依赖注入 的值,修改 mbd 的 propertyValues |
要点 | 总结 |
applyPropertyValues | 根据 mbd 的 propertyValues 进行依赖注入(即xml中 <property name ref|value/>) |
5.3 创建 bean - 初始化
要点 | 总结 |
内置 Aware 接口的装配 |
包括 BeanNameAware,BeanFactoryAware 等 |
扩展 Aware 接口的装配 |
由 ApplicationContextAwareProcessor 解析,执行时机在 postProcessBeforeInitialization |
@PostConstr uct |
由 CommonAnnotationBeanPostProcessor 解析,执行时机在 postProcessBeforeInitialization |
InitializingBe an |
通过接口回调执行初始化 |
initMethod | 根据 BeanDefinition 得到的初始化方法执行初始化,即 <bean init-method> 或 @Bean(initMethod) |
创建 aop 代 理 |
由 AnnotationAwareAspectJAutoProxyCreator 创建,执行时机 在 postProcessAfterInitialization |