文章目录
- 一、Spring中的IOC、DI、AOP
- 二、什么是Spring beans?
- 三、BeanFatory和ApplicationContext有什么区别?
- 四、Spring基于xml注入bean的几种方式
- 五、spring中bean的生命周期?
- 六、 解释Spring支持的几种bean的作用域
- 七、 spring框架中的单例Beans是线程安全的吗?
- 八、 spring如何处理线程并发问题?
- 九、 spring的自动装配
- 十、Spring 框架中都用到了哪些设计模式
- 十一、@Component和@Bean的区别是什么?
- 十二、Spring中的事务
- 十三、Spring中的bean的生成过程?
- 十四、FactoryBean和BeanFactory的区别
- 十五、Spring中的循环依赖问题
一、Spring中的IOC、DI、AOP
1 、 I O C \color{green}{1、IOC} 1、IOC
IOC:就是控制反转,通俗的说就是我们不用自己创建实例对象,这些都交给Spring的bean工厂帮我们创建管理,通过面向接口编程的方式来实现对业务组件的动态依赖。
优点:降低了代码的耦合度,将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入,这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。
2
、
D
I
\color{green}{2、DI}
2、DI
DI:就是依赖注入,组件之间的依赖关系由容器在运行期决定,形象的说即容器动态的将某个依赖关系注入到组件之中。我们只需要简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处。
有哪些不同类型的依赖注入实现方式?
依赖注入是时下最流行的IOC实现方式,依赖注入分为接口注入、Setter方法注入、构造器注入三种方式。
Spring IOC的实现机制
Spring中的IOC的实现原理就是工厂模式加反射机制。
interface Fruit {
public abstract void eat();
}
class Apple implements Fruit {
public void eat(){
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat(){
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance(String ClassName) {
Fruit f=null;
try {
f=(Fruit)Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class Client {
public static void main(String[] a) {
Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");
if(f!=null){
f.eat();
}
}
}
3 、 A O P \color{green}{3、AOP} 3、AOP
AOP:面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用行,同时提高了开发的效率。核心思想:**不修改源代码的前提下,对程序进行增强。**可用于权限认证、日志、事务处理。
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理,静态代理的代表未AspectJ,动态代理则以Spring AOP为代表。
- 静态: 由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了。
- 动态:在程序运行时运用反射机制动态创建而成。(InvocationHandler的应用)
SpringAOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。
- JDK动态代理:通过反射来接受被代理的类,并且要求被代理的类必须实现一个接口。JDK动态dialing的核心是InvocationHandler接口和Proxy类。
- CGLIB动态代理:CGLIB是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类表标记为final,那么它是无法使用CGLIB做动态代理的。
二、什么是Spring beans?
spring中的bean:我们一般获取对象的方法有两种,一种是手动直接new,另一种是直接交给spring来管理,交给spring来管理的就是bean容器先会实例化bean,然后自动注入,实例化的过程就需要依赖BeanDefinition。
Spring beans是那些形成Spring应用的主干的java对象,他们被Spring IOC容器初始化、装配、和管理,这些beans通过容器中配置的元数据创建,
三、BeanFatory和ApplicationContext有什么区别?
两者都是通过xml配置文件加载bean。
1 、 B e a n F a c t o r y \color{green}{1、BeanFactory} 1、BeanFactory
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
2 、 A p p l i c a t i o n C o n t e x t \color{green}{2、ApplicationContext} 2、ApplicationContext
ApplicationContext它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
四、Spring基于xml注入bean的几种方式
1 、 S e t 方 法 注 入 \color{green}{1、Set方法注入} 1、Set方法注入
2 、 构 造 器 注 入 \color{green}{2、构造器注入} 2、构造器注入
通过index设置参数的位置,通过type设置参数类型
3 、 静 态 工 厂 注 入 \color{green}{3、静态工厂注入} 3、静态工厂注入
就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过“工程类.静态方法()”来获取对象,而是依然通过spring注入的形式获取。
4 、 实 例 工 厂 \color{green}{4、实例工厂} 4、实例工厂
实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法。
五、spring中bean的生命周期?
1 、 实 例 化 B e a n \color{green}{1、实例化Bean} 1、实例化Bean
- 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
- 对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
BeanDefinition:用于保存bean的相关信息,包括属性、构造方法参数、依赖的bean实例名称以及是否延迟加载、单例等。它是实例化bean的原材料,spring就是根据BeanDefintion中的信息实例化bean。
( 2 ) 设 置 对 象 属 性 ( 依 赖 注 入 ) \color{green}{(2)设置对象属性(依赖注入)} (2)设置对象属性(依赖注入)
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
( 3 ) 处 理 A w a r e 接 口 \color{green}{(3)处理Aware接口} (3)处理Aware接口
xxxAware:xxxAware接口的出现就是为了让xxx感受到自身的一些属性。比如BeanFactoryAware接口就是为了能够获取到BeanFactory对象。
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
-
①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
-
②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
-
③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
( 4 ) B e a n P o s t P r o c e s s o r : \color{green}{(4)BeanPostProcessor:} (4)BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
( 5 ) I n i t i a l i z i n g B e a n 与 i n i t − m e t h o d : \color{green}{(5)InitializingBean 与 init-method:} (5)InitializingBean与init−method:
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
(
6
)
如
果
这
个
B
e
a
n
实
现
了
B
e
a
n
P
o
s
t
P
r
o
c
e
s
s
o
r
接
口
,
将
会
调
用
p
o
s
t
P
r
o
c
e
s
s
A
f
t
e
r
I
n
i
t
i
a
l
i
z
a
t
i
o
n
(
O
b
j
e
c
t
o
b
j
,
S
t
r
i
n
g
s
)
方
法
;
\color{green}{(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;}
(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Objectobj,Strings)方法; 由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
以上几个步骤完成之后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
( 7 ) D i s p o s a b l e B e a n \color{green}{(7)DisposableBean} (7)DisposableBean
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的**destroy()**方法;
( 8 ) d e s t r o y − m e t h o d : \color{green}{(8)destroy-method:} (8)destroy−method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
六、 解释Spring支持的几种bean的作用域
( 1 ) s i n g l e t o n : \color{green}{(1)singleton:} (1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
( 2 ) p r o t o t y p e : \color{green}{(2)prototype:} (2)prototype:为每个bean请求提供一个实例。该bean在每次被注入的时候,都要重现创建一个实例。
( 3 ) r e q u e s t : \color{green}{(3)request:} (3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
( 4 ) s e s s i o n : \color{green}{(4)session:} (4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
( 5 ) g l o b a l − s e s s i o n : \color{green}{(5)global-session:} (5)global−session:类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义,Protlet规范定义了全局Session的概念,它被所有构成某个portlrt web应用的各种不同的portlet所共享。
Portlet:是基于java的web组件,有portlet容器管理,并由容器处理请求,生产动态内容。
七、 spring框架中的单例Beans是线程安全的吗?
Spring框架并没有对单例进行任何多线程的封装处理,关于bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态,所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如View Model对象),就需要自行保证线程安全。
解决的办法就是:将多态bean的作用域由“singleton”变更为“prototype”或者使用ThreadLocal进行处理。
如果Bean配置为singleton,当有两个用户访问都调用到了该Bean,会出现资源共享的问题。
八、 spring如何处理线程并发问题?
-
在一般情况下,只有无状态的Bean才可以在多线程环境下共享。
-
绝大多数都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThredLocal进行处理,解决线程安全问题。
ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
九、 spring的自动装配
在Spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。
在Spring中xml配置中共有5种自动装配:
- no:默认的方式是不进行自动装配,通过手动设置ref属性来进行装配bean。
- byName:通过bean的名称进行自动装配,如果一个bean的property与另一bean的name相同,就进行自动装配。
- byType:通过参数的数据类型进行自动装配。
- constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
- autodetect:自动探测,如果有构造方法,通过construct的方式自动装配,否则使用byType的方式自动装配。
@Autowired可用于:构造函数、成员变量、Setter方法
@Autowired和@Resource的区别
- @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在。
- @Resource默认是按照名称来装配注入的,只有当找不到名称匹配的bean才会按照类型来装配注入。
十、Spring 框架中都用到了哪些设计模式
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
十一、@Component和@Bean的区别是什么?
1
、
作
用
对
象
不
同
\color{green}{1、作用对象不同}
1、作用对象不同
@Component注解作用于类,而@Bean注解作用于方法。
2
、
@
C
o
m
p
o
n
e
n
t
注
解
通
常
是
通
过
类
路
劲
扫
描
来
自
动
侦
测
以
及
自
动
装
配
到
S
p
r
i
n
g
容
器
中
\color{green}{2、@Component注解通常是通过类路劲扫描来自动侦测以及自动装配到Spring容器中}
2、@Component注解通常是通过类路劲扫描来自动侦测以及自动装配到Spring容器中,(可以使用@ComponentScan注解定义要扫描的路劲)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。
3
、
@
B
e
a
n
注
解
比
@
C
o
m
p
o
n
e
n
t
注
解
的
自
定
义
性
更
强
,
而
且
很
多
地
方
只
能
通
过
@
B
e
a
n
来
注
册
b
e
a
n
\color{green}{3、@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean来注册bean}
3、@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean来注册bean,比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解实现。
十二、Spring中的事务
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库底层的事务提交和回滚是通过binlog或者redo log实现的。
Spring支持编程式事务管理和声明式事务管理两种方式:
- 编程式事务管理使用TransactionTemplate
- 声明式事务管理建立在AOP上的,其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
s p r i n g 的 事 务 传 播 行 为 \color{green}{spring的事务传播行为} spring的事务传播行为
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
-
PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
-
PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
-
PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果不存在,就抛出异常。
-
PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建事务。
-
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务则事务挂起。
-
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
-
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
S p r i n g 中 的 隔 离 级 别 \color{green}{Spring中的隔离级别} Spring中的隔离级别
- ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。
- ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
- ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。
- ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
- ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。
十三、Spring中的bean的生成过程?
1、Bean的加载和解析—BeandefinationReader
在Spring中,Bean有多种定义方式,比如XML形式,Annotaion注解形式以及SpringBoot中的配置类等形式,BeandefinationReader该接口的作用就是加载和解析Bean的配置信息,每种形式都有对应的一种BeandefinationReade来进行解析,比如XML文件是XMLBeandefinationReade,他们最终都会解析为BeanDefinition对象,在IOC内部维护着一个CourrentHashMap,每个bean都对应着一个BeanDefinition对象。
2、Bean的注册—BeanDefinitionRegistry
BeanDefinitionRegistry是一个接口,实现了AliasRegistry接口,定义了一些对bean的常用操作,大概有如下功能:
- 以Map<String,BeanDefinition>的形式注册bean
- 根据beanName删除和获取beanDefiniation
- 得到持有的beanDefiniation
- 根据beanName判断是否包含beanDefiniation
一般注册bean的方法是registerBeanDefinition,DefaultListableBeanFactory 通过实现 BeanDefinitionRegistry 接口,重写该方法。
3、功能扩展和Bean的实例化
(1)第一种情况使用BeanFactoryProcessors直接扩展
Spring是一个框架,需要考虑扩展性,这里就是显示扩展性中的一环。它可以做什么事呢?
- 注册一些BeanDefinition到注册中心
- 修改注册中心内已注册过的BeanDdfinition信息。
(2)注册自定义BeanPostProcessors的进行扩展或者使用Spring固有的BeanPostProcessors进行扩展。
- 想让Bean做一些自定义的处理,就可以实现BeanPostProcessors这个接口来做扩展,注意创建实例前后都可以进行扩展。
- 根据BeanDefinition获取合适的Constructor进行反射创建实例,根据BeanDefinition注入依赖,并调用init方法进行实例的初始化。
- 操作完成的对象可以放在实例缓存池中,比如普通单实例、FactoryBean实例、Spring系统实例等。
十四、FactoryBean和BeanFactory的区别
BeanFactory
BeanFactory是IOC最基本的容器,负责生产和管理bean,提供了IOC容器应遵守的最基本的接口。
FactoryBean
FactoryBean是一个接口,挡在IOC容器中的Bean实现了
一般情况下,Spring通过发射机制利用bean的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统方式,则需要在bena中提供大量的配置信息,并且配置方式的灵活性受限。这时采用编码的方式可能会得到一个简单的方案,所以Spring位次提供了一个FactoryBean的工厂类接口,用户可以是实现该接口定制是实例化Bean的逻辑。
总结:BeanFactory和FactoryBean其实没有什么可比性,只是两者的名称比较接近。BeanFactory是提供了IOC容器最基本的心事,给具体的IOC容器的实现提供了规范;FactoryBean可以说为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以再getObject()方法灵活配置。
十五、Spring中的循环依赖问题
spring中的循环依赖:其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,而B依赖于C,C却依赖于A。
1、原型模式(protoType)循坏依赖—无法解决
2、单例Bean循环依赖—构造参数产生依赖—无法解决
这两种spring都无法解决,只能抛出异常:
Sprin容器会将每一个正在创建Bean的标识(BeanName)符放在一个“当前创建Bean池(CurrrentCreationSet)”中,Bean标识符在创建过程中将一直保持在这个池中。
因此如果在创建Bean过程中发现自己已经在“ 当前创建Bean池 ”里将抛出BeanCurrentlyInCreationException异常表示发生循环依赖,
而对于创建完毕的Bean将从“当前创建完毕Bean池中清理掉。”
3、单例Bean循环依赖-setter产生依赖—可以解决
使用三级缓存+提前曝光的方式来解决。
三级缓存:用来保存不同时期对象的三个集合。
- 一级缓存:singletonObjects指单例对象的缓存。
- 二级缓存:singletonFactories:单例对象工厂的缓存。
- 三级缓存:earlySingletonObjects:提前曝光的单例对象的缓存。
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
以A,B两个对象的相互引用为例讲解setter单例的循环依赖的解决过程。
- getBean(A)时,先获取A对应的BeanDafiniton信息, 先到一级缓存中查询,因为该对象从未初始化过,一定拿不到,然后记录当前BeanName到CreationSet中,通过Class拿到无参构造利用反射创建早期对象。
什么是早期对象?
1、未进行属性注入
2、未进行对象init方法调用
3、未进行后处理器处理
但是早期对象和处理完后的对象内存地址是一样的,都是同一个对象,不包括AOP。
- 执行MerageBeanDefinitionPostProcessor操作,处理@Autowried等注解,将早期对象封装到ObjectFactory内,放入三级缓存。
- 进行依赖注入,发现依赖了类型B,进行getBean(B)操作。
- 通过反射创建B的早期对象,执行MerageBeanDefinitionPostProcessor操作,处理@Autowried等注解,将早期对象封装到ObjectFactory内,放入三级缓存。
- 进行依赖注入发现类型A,进行getBean(A)操作。
- 获取A的BeanDefinition信息,先从一级缓存中获取,发现没有,检查CurrentlyCreationSet发现A在创建中,从三级缓存中获取到A的早期对象,并且将A对象升级到二级缓存。
- B完成依赖注入,将B对象存放至一级缓存,并且将二三级缓存中关于B的数据清理。
- A完成依赖注入,将A存放至一级缓存,并且将二三级缓存中有关A的数据清理。
为什么这种机制无法解决原型模式下的setter依赖?
scope=“prototype”:意思是每次请求都会创建一个实例对象。而对于prototype作用域的Bean,Spring容器不进行缓存Bean对象,根本无法提前暴露一个创建中的Bean。