Spring

IOC

目录

IOC

finishBeanFactoryInitialization(beanFactory)

  doCreateBean  

 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事务定义(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法
PlatformTransactionManagerSpring提供的平台事务管理器,主要用于管理事务。
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_COMMITTEDISOLATION_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>
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值