IOC
目录
finishBeanFactoryInitialization(beanFactory)
instanceWrapper.getWrappedInstance();
populateBean(beanName, mbd, instanceWrapper);
initializeBean(beanName, exposedObject, mbd)
spring的核心就是你IOC与AOP,先来看一下IOC:
这个BeanDefinitionReader主要的作用也是为了扩展用的,也就是出现其他的资源文件也可以使用这个组件进行读取,是一个接口
看这个图可以知道BeanFactory其实可以理解为容器,ApplicationContext实现了BeanFactory接口
我们首先要创建一个BeanFactory,然后将解析的Bean的定义信息放入到BeanFactory中进行对象的创建。
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = (UserService) context.getBean("userService"); System.out.println(userService); }
ApplicationContext的构造方法如下:最重要方法就是refresh()方法
public AnnotationConfigApplicationContext(Class<?>... componentClasses) { this(); register(componentClasses); refresh(); }
refresh()方法中主要的这12个方法,其实有很多都是用来给用户扩展的,核心的只有那么几个
//准备上下文环境 prepareRefresh(); // 获得Bean工厂 DefaultListableBeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 准备BeanFactory(第二个方法虽然创建了BeanFactory但是很多属性都是默认值) prepareBeanFactory(beanFactory); try{ // 对BeanFactory进行增强留给开发者进行扩展 postProcessBeanFactory(beanFactory); // 执行 BeanFactory 的后置处理器 invokeBeanFactoryPostProcessors(beanFactory); // 注册BeanPostProcessor registerBeanPostProcessors(beanFactory); // 国际化的组件 initMessageSource(); // 初始化广播器 initApplicationEventMulticaster(); // 留给开发者拓展的方法 onRefresh(); //初始化监听器组件 registerListeners(); // 完成工厂初始化,实例化所有剩余的(非惰性初始化)单例。(核心) finishBeanFactoryInitialization(beanFactory); //完成容器初始化 finishRefresh();}
finishBeanFactoryInitialization(beanFactory)
这个方法是对Bean生命周期的一个管理是非常重要的:
点进去向下翻可以看到:beanFactory.preInstantiateSingletons();对BeanFactory里的beanname进行遍历 拿到bean 创建bean
for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { getBean(beanName); //getBean()方法 找到doGetBean方法 }
getSingleton(beanName):方法是用来从一级缓存(单例池)中拿bean的
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock Object singletonObject = this.singletonObjects.get(beanName); //如果一级缓存为空 并且当前bean正在创建 就会继续下边的判断逻辑 否则直接返回 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } }
doCreateBean
instanceWrapper.getWrappedInstance();
这个方法会实例化bean,实例化之后会将bean放到三级缓存 (
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
)
synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }
populateBean(beanName, mbd, instanceWrapper);
这个方法是为bean的属性赋值:这里有一个问题就是循环依赖问题 也就是说 创建a对象 但是a对象的一个属性是b怎么办
applyPropertyValues(beanName, mbd, bw, pvs); (populateBean()中调用了这个方法点进去)
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);然后又调用了这个方法 ,点进去
if (value instanceof RuntimeBeanReference) { RuntimeBeanReference ref = (RuntimeBeanReference) value; return resolveReference(argName, ref); 发现返回的是这个方法的返回值 。其实就是在这个方法中调用了getBean方法 然后为a属性赋值 }
initializeBean(beanName, exposedObject, mbd)
初始化的方法:
循环依赖
循环依赖这种现象非常简单就是:a依赖b b依赖a spring是使用三级缓存来解决这个问题的
- singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用 (一级缓存 单例池)
- earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 (二级缓存)
- singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖 (三级缓存)
下边是spring解决三级缓存的流程:
1.拿到A对象的class文件,通过反射根据A对象的空构造函数获取A对象的原始对象并且将A对象的原始对象放入到三级缓存*中 map.put("aService",AService)
2.属性赋值: 获取A对象有一个B对象,给B对象赋值
从单例池中获取B对象
一级缓存中不存在,创建B对象,拿到B对象的class文件
通过反射根据B对象的空构造函数获取B对象的原始对象并且将B对象的原始对象放入到三级缓存中,map.put("bService",BService)
给B对象的aService属性赋值,判断是否提前AOP
需要提前AOP(表示了循环依赖),将三级缓存中的原始对象A取出,进行提前AOP,将AOP过后的A对象的代理对象放到二级缓存中,并且删除三级缓存中的A对象,删除的目的是为了防止重复AOP,此时B对象将二级缓存中的A对象的代理对象赋值给自己的A对象属性
给其他属性赋值
其他事情
放入单例池(将完整的B对象放入单例池中)
3.给其他属性赋值
4.其他事情(此时不需要进行AOP)
5.放入单例池中并且删除二级缓存中的A对象的代理对象
我们可以看到我们将对象从三级缓存中拿出来进行提前aop放到了二级缓存中,这个原因就是:三级缓存中存放的是原始对象,而不是经过aop产生的代理对象, 我们进行提前aop为b对象注入的a对象就是和a对象一样经过aop之后的。
spring中bean是线程安全的嘛?
spring中的bean不是线程安全的,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合Scope(作用域)来分析:
Spring 的 bean 作用域(scope)类型:
- singleton:单例,默认作用域。
- prototype:原型,每次创建一个新对象。 也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
- request:请求,每次Http请求创建一个新对象,适用于WebApplicationContext环境下。
- session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
- global-session:全局会话,所有会话共享一个实例。
单例:对于单例bean,所有线程都贡献一个单例bean,所以存在资源的竞争。
如果单例bean是一个无状态bean,也就是线程中的操作不会对bean的成源执行查询以外的操作,那么这个单例Bean就是线程安全的、比如service,controller等,这些bean大多是无状态的,只关注于方法本身。但是如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域 把 "singleton"改为’‘protopyte’ 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的安全了。
问题与思考:在一次调试责任链管道模式的时候,发现其中使用的管道并不是单例的,这引起了很深的一个思考,为什么要用多例呢?
解答:原因就是多例是在getBean的时候才创建对象到容器中,而单例是在容器初始化的时候创建,getBean的时候可以将构建bean需要的参数传进去。
多例情况下,容器创建完成时不调用方法创建对象到容器中,在程序中获取时,才会将对象加载到容器中,而且每次调用生成的都是不同的对象
单例情况下,容器创建时调用方法创建对象到容器中,在程序中调用bean,直接从容器中拿取,且每次拿取的都是同一个对象。如果上一次对bean里的属性做了修改,那下一次拿取的就是修改过的bean
无状态bean:就是不会保存数据。
有状态bean:有数据存储功能。
controller、service和dao层本身并不是线程安全的,只是如果只是调用里边的方法,而且多线程调用同一个实例的方法,会在内存中复制变量,这是自己线程的工作内存是安全的。
Java虚拟机栈是线程私有的,他的生命周期与线程相同。虚拟机描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
局部变量的固有属性之一就是封闭在执行过程中。他们位于执行线程的栈中,其他线程无法访问这个栈。
所以其实任何无状态单例都是线程安全的。
Spring的根本就是通过大量这种单例构建起系统,以事务脚本的方式提供服务。
在单例模式下可以通过ThreadLocal来定义变量,来保证线程安全。普通变量,静态变量,注入的对象,还是从配置文件中取出的变量,都是线程不安全的。
在多例模式下:注入对象需要为对象的作用域配置成多例。否则只是每次controller对象是不同的,但是注入的对象还是同一个,静态变量仍然线程不安全。
下面总结一下:
1、在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的。
2、尽量不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。
3、默认注入的Bean对象,在不设置scope的时候他也是线程不安全的。
4、一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的
spring注入的方式
构造方法注入:主要与实体类的构造方法的入参有关,配置文件需要与构造方法对应
setter注入:通过set方法注入属性,spring会默认使用无参构造方法来实例化对象,如果实体定义了有参构造而没有无参构造方法,会报错。
注解注入:@Autowired与@Resource
spring事务管理
核心接口
接口 | 描述 |
TransactionDefinition | 事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法 |
PlatformTransactionManager | Spring提供的平台事务管理器,主要用于管理事务。 |
TransactionStatus | 事务的状态 |
PlatformTransactionManager接口是Spring提供的平台事务管理器,主要用于管理事务。该接口中提供了三个事务操作的方法,具体如下:
TransactionStatus getTransaction(TransactionDefinition definition);用于获取事务状态信息
void commit(TransactionStatus status);用于提交事务
void rollback(TransactionStatus status);用于回滚事务
PlatformTransactionManager接口只是代表事务管理的接口,并不知道底层是如何管理事务的,具体如何管理事务由各个平台来完成。
TransactionDefinition接口是事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法,具体如下表所示:其实以下就是基本事务属性,一共有五个维度:
方法 | 描述 |
String getName( ); | 获取事务对象名称 |
int getIsolationLevel( ); | 获取事务的隔离级别 |
int getPropagationBehavior( ); | 获取事务的传播行为 |
int getTimeout( ); | 获取事务的超时时间 |
boolean isReadOnly( ); | 获取事务是否只读 |
TransactionStatus接口:通过PlatformTransactionManager(事务管理器)来获取事务状态实例,事务在提交或者回滚的时候需要用到事务状态。
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事物
boolean hasSavepoint(); // 是否有恢复点
void setRollbackOnly(); // 设置为只回滚
boolean isRollbackOnly(); // 是否为只回滚
boolean isCompleted; // 是否已完成
}
spring定义的七种传播行为
传播行为 | 描述 |
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 |
PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 |
隔离级别
隔离级别 | 描述 |
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED | 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读 |
ISOLATION_READ_COMMITTED | ISOLATION_READ_COMMITTED |
ISOLATION_REPEATABLE_READ | 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE | 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
只读
事务的另一个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
事务超时
为了使应用程序很好地运行,事务不能运行太长时间。因为事务可能涉及对后端数据库的锁定,所有异常事件的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
回滚规则
事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的),但是你可以声明事务在遇到特定的检查型异常时同样回滚。也可以声明遇到特定的异常不回滚,即使这些异常是运行期异常。
声明式事务与编程式事务
Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
编程式事务
spring提供两种方式来实现编程式事务管理,分别是TransactionTemplate和直接使用PlatformTransactionMangger.
使用TransactionTemplate
采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。
TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplate
Object result = tt.execute(
new TransactionCallback(){
public Object doTransaction(TransactionStatus status){
updateOperation();
return resultOfUpdateOperation();
}
}); // 执行execute方法进行事务管理
使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。
使用PlatformTransactionManager
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
try {
// 数据库操作
dataSourceTransactionManager.commit(status);// 提交
} catch (Exception e) {
dataSourceTransactionManager.rollback(status);// 回滚
}
声明式事务
就是使用配置文件或者注解的方式,为dao层配置事务管理器,这种方式使用AOP,对业务代码侵入性低
每个Bean都有一个代理:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean><!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean><!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean><bean id="userDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="userDaoTarget" />
<property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>