Spring Bean的实例化过程
Spring管理对象,分为两部分:
容器启动阶段
Bean的实例化
容器启动阶段
配置元数据
spring需要知道创建对象需要的必要信息,必要的信息可以是xml配置文件,或者是注解、也可以是直接代码硬编码,创建对象必须要的信息称之为配置元信息。
<bean id="student5" class="com.tulun.Spring.IOC.pojo.Student">
<property name="id" value="12"/>
<property name="name" value="Java31"/>
<property name="user" ref="user"/>
</bean>
BeanDefination
将xml或者注解等等配置的元信息,在内存中如何保存呢?Spring在内存中表示元信息使用的方式就是BeanDefinition
配置的元信息被加载到内存中之后是以BeanDefinition的形式存在的。
BeanDefinitionReader
让Spring来理解各种不同类型的元信息,需要靠BeanDefinitionReader。
如果要读取XML配置的元信息,可以使用XMLBeanDefinitionReader。
如果要读取注解配置的元信息,可以使用AnnotationBeanDefinitionReader。
…
也可以自定义BeanDefinitionReader来自己控制配置元数据的加载。
作用就是用来加载配置元信息,将其转化为内存中统一的形式BeanDefinition。
BeanDefinationRegistory
将加载到内存中生成的BeanDefination后,就将其注册到BeanDefinationRegistory中,BeanDefinationRegistory就是存放BeanDefination的集合,是以键值对的形式,通过特定的Bean的定义,映射到BeanDefination。
BeanFactoryPostProcessor
BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistory中的一个个BeanDefination进行一定程度上的修改和替换。
至此,整个容器启动的阶段就完成了。
Bean实例化阶段
容器启动阶段和Bean的实例化阶段是存在时间差,如果选择懒加载形式,在需要依赖的对象时,才会执行响应的Bean的实例化过程
如果是不选择懒加载形式,在容器启动完成之后,会立即启动Bean的实例化阶段,提前将Bean实例化并保存。
对象创建策略
对象的创建采用策略模式,借助于BeanDefinationRegistory中的BeanDefination,可以使用反射的方式创建对象,也可以CGLib字节码创建对象,对象的创建具有灵活性。
BeanWrapper-对象的外衣
IOC容器来管理各种类型的对象,为了统一对不同类型对象的访问,Spring提供了BeanWrapper给Bean在外层进行了一层封装。
设置对象信息
对于基本类型的属性,如果配置元信息中有设置,直接使用配置元信息中设置的值赋值即可,如果没有JVM在对象实例化过程中会给定初始状态。
如果是引用类型属性:Spring中将所有创建好的对象放在一个Map中,此时Spring会检查所依赖的对象是否已经被纳入容器的管理范围,如果在Map中存在引用实例,直接注入当前对象属性,如果没有,SPring会暂停当前对象的实例化过程,转而先去实例化依赖对象,完了后在对该对象继续实例化过程。
检查Aware相关接口
如果想要依赖Spring的相关对象,可以实现相应的Aware接口,Spring 容器检查该接口的实现类完成自动注入相关依赖过程。
ApplicationContext实现Aware接口,容器就会自动将ApplicationContext注入到容器实例化。
BeanPostProcessor前置处理
BeanFactoryPostProcessor存在于容器启动阶段。
BeanPostProcessor存在于对象实例化阶段。
BeanFactoryPostProcessor关注对象被创建之前那些配置的修修改改。
而BeanPostProcessor阶段关注对象已经被创建之后 的功能增强,替换等操作。
BeanPostProcessor与BeanFactoryPostProcessor都是Spring在Bean生产过程中强有力的扩展点。
BeanPostProcessor前置处理就是在要生产的Bean实例放到容器之前,允许对Bean实例进行一定程度的修改,替换等操作。
自定义初始化逻辑
Bean还有一定的初始化逻辑,Spring将允许我们通过两种方式配置我们的初始化逻辑:(1)InitializingBean (2)配置init-method参数一般通过配置init-method方法比较灵活。
BeanPostProcessor后置通知
与前置处理类似,这里是在Bean自定义逻辑也执行完成之后,Spring又留的最后一个扩展点。我们可以在这里在做一些我们想要的扩展。
自定义销毁逻辑
对应自定义初始化逻辑,同样有两种方式:(1)实现DisposableBean接口 (2)配置destory-method参数。
使用
调用回调销毁接口
Spring中事务管理
事务:ACID
隔离级别:读未提交、读已提交、可重复读、串行化
数据问题:脏读、不可重复读、幻读
事务SQL:begin,commit,rollback
提供的事务API接口
PlatformTransactionManager 事务管理器
TransactionDefinition:事务定义信息(隔离,传播、超时、只读)
TransactionStatus:事务具体运行状态
PlatformTransactionManager 在spring中针对不同的持久层框架提供了不同的PlatformTransactionManager 接口实现:
TransactionDefinition:事务定义信息
定义隔离级别,事务传播行为
隔离级别:
默认的隔离级别是可重复读
定义事务传播行为:
Spring中事务管理形式
spring中事务主要提供形式:通过XML配置声明事务、通过注解形式声明事务。
基本xml配置信息:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描注解-->
<context:component-scan base-package="com.tulun.Spring.Transaction"/>
<!--数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--获取会话工厂-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--映射关系-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<!--mybatis的配置-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--通过代理对象获取SQLSession对象-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--注入会话工厂-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<!--注入接口-->
<property name="mapperInterface" value="com.tulun.Spring.Transaction.mapper.UserMapper"/>
</bean>
</beans>
通过XML配置声明事务
事务处理相关操作xml
<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步:配置事务增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--做事务操作-->
<tx:attributes>
<tx:method name="transfer"/>
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.tulun.Spring.Transaction.service.UserService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
通过注解形式声明事务
在配置文件中打开事务注解扫描
<!--第一步:配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步:开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在方法上添加事务注解:
@Transactional//事务注解
//事务注解
public void transfer(){
userMapper.less(1);
int i = 10/0;
userMapper.add(2);
}
Spring中事务传播行为
事务传播行为是Spring框架独有的事务增强特性,不属于事务实际提供方数据库的行为
定义事务传播行为:
事务传播行为用来描述某一个事务传播行为修饰的方法被嵌套到另一个事务的传播行为中该如何传播。
Propagation**.REQUIRED**
在外围方法开启事务的情况下,Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所有Propagation.REQUIRED所修饰的方法和外围方法属于同一个事务,只要一个方法回滚,整个事务回滚。
REQUIRED
1、如果外部方法没有开启事务,使用REQUIRED修饰的内部方法会开启自己的事务,且开启的事务相互独立,互不影响。
2、如果外部方法开启事务并且使用REQUIRED修饰,REQUIRED修饰修饰的内部方法和外部方法同属于同一个事务,只要一个方法回滚,整个事务回滚。
Propagation.REQUIRES_NEW
在外围没有开启事务的情况下,Propagation.REQUIRES_NEW修饰的内部方法会开启自己的事务,且事务相互独立,互不影响。
在外围开启事务情况下,Propagation.REQUIRES_NEW修饰的内部方法依然会单独开启独立事务,且与外部方法事务也独立,内部方法之间,内部与外部方法事务均相互独立,互不影响。
事务注解@Transactional失效的场景
数据库中的事务是在sql语句周围添加begin、rollback、commit等操作实现的,而在spring中的事务不是直接添加sql语句实现,而是通过aop环绕增强技术在invoke方法(invoke方法中调用了所要增强的方法)前后执行begin、rollback、commit等操作。
失效场景:
1、@Transactional注解标注方法修饰符为非public,@Transactional注解将不起作用。(因为方法是非public的,所以invoke方法无法调用到此方法,导致事务失效)
2、在类内部调用类内部的@Transactional修饰的方法,也会导致事务不开启。(方法的增强是需要invoke方法显性的调用所要增强的方法的,所以此操作会导致事务失效)
3、事务方法内部捕获异常,没有抛出新异常,导致事务操作不会进行回滚。(异常在事物内部被捕获,事务会认为操作都是正常执行的,所以此操作会导致事务失效)