1. 什么的是bean的自动装配,有哪些方式
Spring 容器能够自动装配相互合作的bean,这意味着容器不需要< constructor-arg >和< property >配置,能通过Bean工厂自动处理bean之间的协作。
开启自动装配,只需要在xml配置文件< bean >中定义“autowire”属性
<bean id = "cutomer" class = "com.xxx.Customer" auowire = "">
auowire属性有5种装配方式:
(一):no - 缺省情况下,自动装配是通过“ref”属性手动设定
手动转配:以value或ref的方式明确指定属性值都是手动装配
需要通过“ref”属性来连接bean
(二):byName - 根据bean的属性名称进行自动装配
Customer的属性名称是person,Spring会将bean id为person的bean通过setter方法进行自动装配
<bean id = "cutomer" class = "com.xxx.Customer" auowire = "byName"/>
<bean id = "person" class = "com.xxx.Person"/>
(三):byType - 根据bean的类型进行自动装配
Customer的属性person的类型为Person,Spring会将Person类型通过setter方法进行自动装配
<bean id = "cutomer" class = "com.xxx.Customer" auowire = "byType"/>
<bean id = "person" class = "com.xxx.Person"/>
(四):constructor - 类似byType,不过是应用于构造器的参数。如果一个bean与构造器参数的类型相同,则进行自动装配,否则导致异常。
Customer的构造函数的参数person的类型为Person,Spring会将Person类型通过构造方法进行自动装配
<bean id = "cutomer" class = "com.xxx.Customer" auowire = "byType"/>
<bean id = "person" class = "com.xxx.Person"/>
(五):autodetect:如果有默认的构造器,则通过constructor 方法进行自动装配,否则使用byType方式进行自动装配
@Autowire自动装配bean,可以在字段,setter方法,构造函数上使用。
2. 如何理解springboot的starter
使用Spring + SpringMVC,如果需要引入mybatis等框架,需要到xml中定义mybatis需要的bean。
starter就是定义一个starter的jar包,写一个@configuration配置类,将这些bean定义在里面,然后在starter包的META-INF/spring.factories中写入该配置类,SpringBoot会按照约定来加载该配置类。
开发人员只需要将相应的starter包依赖进应用,进行相应的属性配置(使用默认配置时,不需要配置)。就可以直接进行代码开发,使用对应的功能了。比如mybatis-spring-boot-starter,spring-boot-starter-redis等。
3. 如何实现一个IOC容器
- 配置文件配置包扫描路径
- 递归包扫描获取.class文件
- 反射,确定需要交给IOC容器管理的类
- 对需要注入的类进行依赖注入
- 配置文件中指定需要扫描的包路径
- 定义一些注解,分别表示访问控制层,业务服务层,数据持久层,依赖注入注解,获取配置文件注解
- 从配置文件中获取需要扫描的包路径,获取到当前路径下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个Set集合进行存储
- 遍历这个Set集合,获取在类上有指定注解的类,并将其交给IOC容器,定义一个安全的Map用来存储这些对象
- 遍历这个IOC容器,获取到每一个类的实例,判断里面是否有依赖其他类的实例,然后进行递归注入
4. Spring中Bean是线程安全的吗
Spring本身并没有针对Bean做线程安全处理,所以:
- 如果Bean是无状态的,那么Bean则是线程安全的
- 如果Bean是有状态的,那么Bean则不是线程安全的
另外,Bean是不是线程安全,跟Bean的作用域没有关系,Bean的作用域只是表示Bean的生命周期范围,对于任何生命周期的Bean都是一个对象,这个对象是不是线程安全的,还是得看Bean对象本身。
5. 介绍一下Spring,读过源码介绍一下大致流程
- Spring是一个快速开发框架,Spring帮助程序员来管理对象
- Spring的源码实现是非常优秀的,设计模式的应用,并发安全的实现,面向接口的设计等
- 在创建Spring容器,也就是启动Spring时:
a :首先会进行扫描,扫描得到所有的BeanDefinition对象,并存在一个Map中
b: 然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要在启动过程中去进行创建,对于多例Bean会在每次获取Bean时利用BeanDefinition去创建
c: 利用BeanDefinition创建Bean就是Bean的创建生命周期,这期间包括了合并BeanDefinition,推断构造方法,实例化,属性填充,初始化前,初始化,初始化后等步骤,其中AOP就是发生在初始化后这一步骤 - 单例Bean创建完之后,Spring会发布一个容器启动事件
- Spring启动结束
- 在源码中会更复杂,比如源码中会提供一些模板方法,让子类来实现,比如源码中还涉及到一些BeanFactoryPostProcessor和BeanPostProcessor的注册,Spring的扫描就是通过BeanFactoryPostProcessor来实现的,依赖注入就是通过BeanPostProcessor来实现的
- 在Spring启动过程中还会处理@Import等注解
6. 简述spring bean的生命周期
- 解析类得到BeanDefinition
- 如果有多个构造方法,则要推断构造方法
- 确定好构造方法后,进行实例化得到一个对象
- 对对象中的加了@Autowired注解属性进行属性填充
- 回调Aware方法,比如BeanNameAware,BeanFactoryAware
- 调用BeanPostProcessor的初始化前的方法
- 调用初始化方法
- 调用BeanPostProcessor的初始化后的方法,在这里会进行AOP
- 如果当前创建的bean是单例的则会把bean放入单例池
- 使用bean
- Spring容器关闭时调用DisposableBean中destory()方法
7. Spring用到了哪些设计模式
- 工厂模式:BeanFactory,FactoryBean
- 适配器模式:AdvisorAdapter接口,对Advisor进行适配
- 访问者模式:PropertyAccessor接口,属性访问器,用来设置和访问某个对象的某个属性
- 装饰器模式:BeanWrapper
- 代理模式: AOP
- 观察者模式:事件监听机制
- 策略模式:InstantiationStrategy,根据不同的情况进行实例化
- 模板模式:JdbcTemplate
- 委派模式:BeanDefinitionParserDelegate
- 责任链模式: BeanPostProcessor
8. 说一下Spring的事务机制
- Spring事务底层是基于数据库事务和AOP机制的
- 首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
- 当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解
- 如果加了,那么则利用事务管理器创建一个数据库连接
- 并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步
- 然后执行当前方法,方法中会执行sql
- 执行完当前方法后,如果没有出现异常就直接提交事务
- 如果出现了异常,并且这个异常想需要回滚的就会回滚事务,否则依然提交事务
- Spring事务的隔离级别对应就是数据库的隔离级别
- Spring事务的传播机制就是Spring事务自己实现的,也是Spring事务中最复杂的
- Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要断开一个事务,那么实际上就是先做一个数据库连接,在此新数据库连接上执行sql
9. 说说常用的SpringBoot注解,及其实现
- @SpringBootApplication:标识了一个SpringBoot工程,它实际是另外三个注解的组合,这三个注解是:
a:@SpringBootConfiguration:实际就是一个@Configuration,表示启动类是一个配置类
b:@EnableAutoConfiguration:想Spring容器中导入了一个Selector,用来加载ClassPath下的SpringFactories中所定义的自动配置类,将这些自动加载为配置bean
c:@CompoentScan:标识扫描路径,因为默认是没有配置实际扫描路径,所以SpringBoot扫描的路径是启动类所在的当前目录 - @Bean:用来定义Bean,类似XML中的< bean >标签,Spring在启动时,会对加了@Bean注解的方法进行解析,将方法的名字改为beanName,并通过执行方法得到bean对象
10. 什么时候@Transactional失效
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的方法只有是被代理对象调用时,那么这个注解才会生效的,如果是被代理对象来调用这个方法,那么@Transactional是不会生效的。
同时如果某个方法是private的,那么@Transactional也会失效,因为cglib是基于父子类来实现的,子类是不能重载父类的private方法,所以无法很好的利用代理,也会导致@Transactional失效。
总结:
- 发生自调用,类里面使用this调用本类的方法,由于这个this对象不是代理类,而是对象本身。
- 方法不是public的(@Transactional只能用于public方法上,否则事务不会失效,如果非要用在非public方法上,可以开启AspectJ代理模式)
- 数据库不支持事务
- 没有被Spring管理
- 异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)
11. Spring如何处理循环依赖问题
循坏依赖:多个对象之间存在循坏的引用关系,在初始化过程当中,就会出现“先有蛋还是先有鸡的问题”。
一种是使用@Lazy注解:解决构造方法造成的循坏依赖问题
另一种是使用三级缓存:
- 一级缓存 : 缓存最终的单例池对象
- 二级缓存:缓存初始化对象
- 三级缓存:缓存对象动态代理信息
对于对象之间的普通引用,二级缓存会保存new出来的不完整对象,这样当单例池中找到不依赖的属性时,就可以先从二级缓存中获取到不完整对象,完成对象创建,在后续的依赖注入过程中,将单例池对象的引用关系调整完成。
三级缓存:如果引用的对象配置了AOP,那在单例池中最终就会需要注入动态代理对象,而不是原对象,而生成动态代理是要在对象初始化之后才开始的。于是Spring增加三级缓存,保存所有对象的动态代理配置信息。在发现有循坏依赖时,将这个对象的动态代理信息获取出来,提前进行AOP,生成动态代理。
12. Spring中后置处理器的作用
Spring中后置处理器分为BeanFactory后置处理器和Bean后置处理器,它们是Spring底层源码架构设计中非常重要的一种机制,同时开发者也可以利用这两种后置处理器来进行扩展。
BeanFactory后置处理器表示针对BeanFactory的处理器,Spring启动过程中,会先创建出BeanFactory实例,然后利用BeanFactory处理器来加工BeanFactory,比如Spring的扫描就是基于BeanFactory后置处理器来实现的,而Bean后置处理器也类似,Spring在创建一个Bean的过程中,首先会实例化得到一个对象,然后再利用Bean后置处理器来对该实例对象进行加工,比如我们常说的依赖注入就是基于一个Bean后置处理器来实现的,通过该Bean后置处理器来给实例对象加了@Autowired注解的属性自动赋值,还比如我们常说的AOP,也是利用一个Bean后置处理器来实现的,基于原实例对象,判断是否需要进行AOP,如果需要,那么就基于原实例对象进行动态代理,生成一个代理对象。
13. Spring如何处理事务
Spring当中支持编程式事务管理和声明式事务管理:
- 编程式事务管理:TransactionTemplate
- 声明式事务:Spring在AOP的基础上提供的事务实现机制。最大的优点就是不需要在业务代码中添加事务管理的代码,只需要在配置文件中做相关的事务规则声明就可以了。但是声明式事务只能针对方法级别,无法控制代码级别的事务管理。
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
事务隔离级别:
DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别;
- 未提交读(read uncommited):脏读,不可重复读,虚读都有可能发生
- 已提交读(read commited):避免脏读。但是不可重复读和虚读都有可能发生(Oracle默认)
- 可重复读(repeatable read):避免脏读和不可重复读,但是虚读有可能发生(MySQL默认)
- 串行化的(serializable):避免以上所有读问题
14. Spring支持的bean作用域
- singleton : bean 在每个 Spring ioc 容器中只有一个实例
- prototype:一个 bean 的定义可以有多个实例
- request:每次 http 请求都会创建一个 bean,该作用域仅在基于 web的 Spring ApplicationContext 情形下有效
- session:在一个 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效
- global-session:在一个全局的 HTTP Session 中,一个 bean 定义对应一个实例。该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效。
缺省的 Spring bean 的作用域是 Singleton(默认情况)
15. Spring框架中Bean的创建过程
简单来说,Spring中的Bean经过了四个阶段:实例化、属性赋值、初始化、销毁
- 实例化:当客户端向容器申请一个Bean时;当容器在初始化一个Bean时发现还需要依赖另一个Bean时。都会new一个Bean对象,BeanDefinition对象保存。
- 设置对象属性(依赖注入):Spring通过BeanDefinition找到对象依赖的其他对象,并且将这些对象赋予当前对象
- 处理Aware接口:Spring会检测对象是否实现xxxAware接口,如果实现了,就会调用对应的方法(BeanNameAware、BeanClassLoaderAware、BeanFactoryAware、ApplicationContentAware)
- BeanPostPreocessor前置处理:调用BeanPostPreocessor的postPreocessorBeforeInitialization方法
- InitializingBean:Spring检测对象如果实现了这个接口,就会执行它的afterPostPropertiesSet方法,定制初始化逻辑
- init-method:< bean init-method = “xxx”> 如果Spring发现Bean配置了这个属性,就会调用它的配置方法,执行初始化逻辑
- BeanPostProcessor后置处理:调用BeanPostProcessor的postProcessAfterInitialization方法
到这一步,Bean的创建过程就完成了,Bean可以正常使用。
- DisposableBean:当Bean实现了这个接口,在对象销毁前就会调用destory方法
- destory-method:< bean destory-method = “xxx”>