Spring面试总结
文章目录
- Spring面试总结
- 一、Spring创建Bean的过程
- 二、Bean的生命周期
- 三、Spring IOC
- 四、@Import注解
- 五、@Controller, @Service, @Repository, @Component
- 六、@Configuration注解
- 七、Spring中的设计模式
- 八、BeanFactory和FactoryBean的区别
- 九、BeanPostProcessor和BeanFactoryPostProcessor区别
- 十、BeanPostProcessor和InstantiationAwareBeanPostProcessor区别
- 十一、Spring中的循环依赖
- 十二、BeanNameAware、BeanFactoryAware、ApplicationContextAware
- 十三、Spring支持的几种Bean作用域
- 十四、Spring中的AOP
- 十五、Spring事务的传播特性
- 十六、Spring声明式事务的原理或流程
一、Spring创建Bean的过程
Spring容器初始化bean的大概过程,文字总结一下:
- 实例化一个ApplicationContext的对象;
- 调用BeanFactoryPostProcessor完成扫描;
- 循环解析扫描出类信息;
- 实例化BeanDefinition对象来存储解析出来的类信息;
- 把实例化好的BeanDefinition对象put到beanDefinitionMap当中缓存起来,以便后面实例化Bean;
- 再次调用BeanFactoryPostProcessor;
- 当然Spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等。Spring调用finishBeanFactoryInitialization方法来实例化单例的bean,实例化之前Spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等等;
- 如果验证完成Spring在实例化一个bean之前需要推断构造方法,因为Spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
- 推断完构造方法之后Spring调用构造方法反射实例化一个对象;这时对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;
- spring处理合并后的beanDefinition(合并是指在配置文件中,有继承关系的配置,将父配置合并到子配置);
- 判断是否支持循环依赖,如果支持则提前把singletonFactory存入singletonFactories——map;
- 判断是否需要完成属性注入。如果需要,则开始注入属性;
- 判断bean的类型回调Aware接口;
- 调用生命周期回调方法;如@postConstruct
- 如果需要代理则完成代理;
- put到单例池——bean创建完成,并存入spring容器当中。
如图所示:
二、Bean的生命周期
- 实例化Bean对象;
- 设置Bean属性;
- 如果通过各种Aware接口声明了依赖关系,则会将相关容器的相关基础设施类注入Bean中。Aware接口具体包括BeanNameAware、BeanFactoryAware、ApplicationContextAware,它们分别注入Bean的ID、BeanFactory、ApplicationContext;
- 如果实现了BeanPostProcessor,调用BeanPostProcessor的前置初始化方法postProcessBeforeInitialization( );
- 如果实现了InitializingBean接口,则会调用afterPropertiesSet( )方法;
- 调用Bean自身定义的init( )方法;
- 如果实现了BeanPostProcessor,调用BeanPostProcessor的后置初始化方法 postProcessAfterInitialization( );
三、Spring IOC
IOC就是控制反转,是指创建对象的控制权的转移。以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。
DI依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
四、@Import注解
@Import通过快速导入的方式实现把实例加入spring的IOC容器中,但@Import只能用在类上。@Import注解有三种用法:
- 直接填class数组方式;
- ImportSelector方式;
- ImportBeanDefinitionRegistrar方式。
4.1、直接填class数组方式
@Import({ 类名.class , 类名.class... })
public class TestDemo {
}
对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名 ,比如com.yc.类名。
4.2、ImportSelector方式
这种方式要求类需要实现ImportSelector接口。
public class Myclass implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 要导入到容器中的组件全类名.
return new String["com.ft.Test.Demo1","com.ft.Test.Demo2"];
}
}
@Configuration
@Import(value={Myclass.class})
public class Config {
}
4.3、ImportBeanDefinitionRegistrar方式
这种方式要求类需要实现ImportBeanDefinitionRegistrar接口。
public class Myclass implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//指定bean定义信息(包括bean的类型、作用域...)
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo.class);
//注册一个bean指定bean名字(id)
beanDefinitionRegistry.registerBeanDefinition("testDemo",rootBeanDefinition);
}
}
@Configuration
@Import(value={Myclass.class})
public class Config {
}
五、@Controller, @Service, @Repository, @Component
- @Controller: 表明一个注解的类是一个"Controller",也就是控制器,可以把它理解为MVC 模式的Controller 这个角色。这个注解是一个特殊的@Component,允许实现类通过类路径的扫描到。它通常与@RequestMapping 注解一起使用。
- @Service: 表明这个带注解的类是一个"Service",也就是服务层,可以把它理解为MVC 模式中的Service层这个角色,这个注解也是一个特殊的@Component,允许实现类通过类路径扫描到。
- @Repository: 表明这个注解的类是一个"Repository",团队实现了JavaEE 模式中像是作为"Data Access Object" 可能作为DAO来使用。这个注解也是@Component 的一个特殊实现,允许实现类能够被自动扫描到
- @Component: 表明这个注释的类是一个组件,当使用基于注释的配置和类路径扫描时,这些类被视为自动检测的候选者。
从源代码可以看到@Controller, @Service, @Repository这三个注解上都有@Component这个注解,四个注解最大的区别就是使用的场景和语义不一样,按类的角色选择相应的注解即可。
六、@Configuration注解
@Configuration注解提供了全新的bean创建方式。这个注解搭配@Bean、@Autowired等注解,可以完全不依赖xml配置,在运行时完成bean的创建和初始化工作。例如:
//@Configuration申明了AppConfig是一个配置类
@Configuration
public class AppConfig {
// 自动注入Demo
@Autowired
public Demo demo;
//@Bean注解申明了一个bean,bean名称默认为方法名testBean
@Bean
TestBean testBean(){
return new TestBean();
}
}
从@Configuration注解的源码里面可知,@Configuration 标记了@Component元注解,因此可以被@ComponentScan扫描并处理,在Spring容器初始化时Configuration类会被注册到Bean容器中,最后还会实例化。
七、Spring中的设计模式
- BeanFactory中使用了工厂模式;
- Bean的创建使用了单例模式和原型模式;
- AOP中使用了代理模式;
- 事件监听器中使用了观察者模式;
- 类似JdbcTemplate类中使用了模板模式。
八、BeanFactory和FactoryBean的区别
在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的;但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂bean,它的实现与设计模式中的工厂模式、修饰模式类似。
BeanFactory:
定义了IOC容器的最基本形式,并提供了IOC容器应遵守的最基本的接口。Spring中有很多种不同类型的BeanFactory,比如ApplicationContext,ClassPathXmlApplicationContext、AnnotationConfigApplicationContext等等。
FactoryBean:
FactoryBean是Spring容器中的一个对象,专门用来创建对象特殊的对象,一般情况下Spring都是通过反射来创建对象的,但是如果某个对象的创建过程过于复杂或无法按照传统的方式实例化,就可以使用FactoryBean。需要实现FactoryBean中的getObject( )、getObjectType( )。
九、BeanPostProcessor和BeanFactoryPostProcessor区别
Spring提供了两种后置处理Bean的扩展接口,分别为BeanPostProcessor和BeanFactoryPostProcessor。这两者在使用上是有区别的。
BeanPostProcessor:后置处理Bean,它是bean级别的,可以在Spring Bean初始化之前和之后对Bean或者程序进行增强。有两个方法:
- postProcessBeforeInitialization( );
- postProcessAfterInitialization( )。
BeanFactoryPostProcessor:主要用于增强工厂的功能,可以在创建Spring Bean之前修改相关Bean的元信息。有一个方法:
- postProcessBeanFactory( )
十、BeanPostProcessor和InstantiationAwareBeanPostProcessor区别
两者都是接口,同时,InstantiationAwareBeanPostProcessor继承于BeanPostProcessor。
BeanPostProcessor有两个方法:
- postProcessBeforeInitialization( );
- postProcessAfterInitialization( )。
InstantiationAwareBeanPostProcessor有两个方法:
- postProcessBeforeInstantiation( );
- postProcessAfterInstantiation( )。
十一、Spring中的循环依赖
Spring对单例对象支持循环依赖,其实现原理在于使用了三级缓存:
- singletonObjects;
- singletonFactories;
- earlySingletonObjects。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory =
this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
假设A依赖B,B依赖A,Spring循环依赖的流程图如下所示:
- 对象A开始创建,执行正常的Bean创建流程,实例化成功后执行populateBean(),开始注入依赖B,进入对象B的创建流程;
- 对象B开始创建,执行正常的Bean创建流程,实例化成功后执行populateBean(),开始注入依赖A,进入新的对象A的创建流程;
- 在新的对象A的创建流程中,执行第一个getSingleton()方法时,会在第二级缓存中获取A对应的singletonFactory,并由它创建一个对象A,此时可以拿到对象A,由此返回B的创建流程。
- 在B创建流程中继续执行后续bean的创建流程,最后返回第一次A的创建流程,并完善后续操作。
十二、BeanNameAware、BeanFactoryAware、ApplicationContextAware
在Spring开发中,程序员需要进行二次开发,那么程序员就需要获取到Spring内部的组件,这时就需要用到XXXAware接口。比如BeanNameAware、BeanFactoryAware、ApplicationContextAware,就可以为自定义的类分别注入Bean的ID、BeanFactory、ApplicationContext。程序员使用这些信息,进行功能开发。
// TestDemo就可以获得TestDemo在Spring中的nameID。
public class TestDemo implements BeanNameAware {
private String nameId;
@Override
public void setBeanName(String name) {
nameId = name;
}
}
// TestDemo就可以获得BeanFactory。
public class TestDemo implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
// TestDemo就可以获得ApplicationContext。
public class TestDemo implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
十三、Spring支持的几种Bean作用域
Spring容器中的bean可以分为5个范围:
- singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护;
- prototype:为每一个bean请求提供一个实例。
- request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
- session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
十四、Spring中的AOP
与OOP对比,AOP是处理一些横切性问题,这些横切性问题不会影响到主逻辑的实现,但是会散落到代码的各个部分,难以维护。AOP就是把这些横切问题和主逻辑分开,达到与主业务逻辑解耦的目的。
在配置类上开启AOP功能@EnableAspectJAutoProxy,
- 若不使用proxyTargetClass=true,则表示当目标对象实现了接口,则使用JDK动态代理;当目标对象未实现接口,则使用cglig动态代理;
- 若使用了proxyTargetClass=true,则目标对象使用cglib动态代理。
当配置了@EnableAspectJAutoProxy时,Spring就会注册一个代理的Creator对象(AspectJAwareAdvisorAwareProxyCreator),在创建目标对象前,找到切面,并将其缓存起来,创建对象时就会判断目标对象是否满足切点的要求,满足则会产生代理对象,不满足则返回原对象。
十五、Spring事务的传播特性
事务的传播特性指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
Spring总共给出了7中事务传播特性:
- PROPAGATION_REQUIRED:默认事务类型,如果没有,就新建一个事务;如果有,就加入当前事务。适合绝大多数情况。
- PROPAGATION_REQUIRES_NEW:如果没有,就新建一个事务;如果有,就将当前事务挂起。
- PROPAGATION_NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
- PROPAGATION_SUPPORTS:如果没有,就以非事务方式执行;如果有,就使用当前事务。
- PROPAGATION_NOT_SUPPORTED:如果没有,就以非事务方式执行;如果有,就将当前事务挂起。即无论如何不支持事务。
- PROPAGATION_NEVER:如果没有,就以非事务方式执行;如果有,就抛出异常。
- PROPAGATION_MANDATORY:如果没有,就抛出异常;如果有,就使用当前事务。
总结:
-
死活不要事务的
PROPAGATION_NEVER:没有就非事务执行,有就抛出异常
PROPAGATION_NOT_SUPPORTED:没有就非事务执行,有就直接挂起,然后非事务执行
-
可有可无的
PROPAGATION_SUPPORTS: 有就用,没有就算了
-
必须有事务的
PROPAGATION_REQUIRES_NEW:有没有都新建事务,如果原来有,就将原来的挂起。
PROPAGATION_NESTED: 如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
PROPAGATION_REQUIRED: 如果没有,就新建一个事务;如果有,就加入当前事务。
PROPAGATION_MANDATORY: 如果没有,就抛出异常;如果有,就使用当前事务。
十六、Spring声明式事务的原理或流程
通过AOP方式实现一个代理对象,通过TransactionInterceptor对事务方法进行拦截,在拦截的过程中会解析出事务的属性,然后把事务的属性存在缓存里,当执行这些方法的时候,拦截器就会自动找到对应方法的事务属性,再根据事务的隔离级别去判断是否需要开启新事务、是否在原有事务中进行、还是抛出异常等,作出相应正确的处理,最后执行目标方法,目标方法执行结束后,会选择回滚事务或提交事务。
在这个过程中会涉及到事务挂起,事务挂起是把之前的事务信息存在一个对象中,并放在当前事务状态对象中,等待新事务执行完之后,会选择恢复挂起的事务。