spring

spring 事务

 

  • 事务的四个特性:原子性,一致性,隔离性,永久性
  • 事务的配置方式
    • 编程式,就是在配置文件中配置,使事务生效
    • 声明式,使用注解使事务生效,这种方式是用aop实现的
  • 事务的传播机制 PROPAGATION_*
    • REQUIRED required
      • spring的默认传播机制,当方法被调用时,如果外层方法有事务则延用此事务,如果外层没有事务则新建事务
    • REQUES_NEW reques_new
      • 每次都会新生成一个事务,同时把外层事务挂起,当当前事务执行完毕后,在继续执行外层事务。如果外层事务则执行当前事务即可。
    • SUPPORT
      • 如果外层有事务就加入外层事务,如果外层没有事务就按无事务执行,完全依赖外部事务。
    • NOT_SUPPORT
      • 此种传播机制不支持事务,当外层有事务时,先挂起外层事务,处理当前任务,无论是否有异常都不回滚。
    • NEVER
      • 不支持事务,如果外层有事务就抛异常
    • MANDATORY mandatory
      • 如果外层没有事务就抛出异常。
    • NESTED nested
      • 如果外层没有事务就创建要给事务,如果外层有事务就嵌套到外层事务中,当当前事务出现异常时,只回滚当前事务,当外层事务出现异常时会将当前事务及外层事务一同回滚。
  • 事务超时:如果事务执行时间太长则将其自动回滚,以免占用浪费数据库资源

spring中事务的实现

  1. Spring事务底层是基于数据库事务和AOP机制的
  2. 首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
  3. 当调用代理对象方法时,会先判断该方法上是否加了@Transactional注解
  4. 如果加了,那么则利用事务管理器创建一个数据库链接
    1. 数据库链接是存储在一个ThreadLocal中,根据数据源获取数据库连接,同时也保证了在同一个线程下获取的数据库链接是相同的。
    2. 如果当A@Transactional方法去调用一个没有被spring管理的B方法时,B方法在执行数据库操作时无法从ThreadLocal中获取数据库链接,所以这也是为什么这种情况下会导致事务失效的原因。
  5. 并且修改数据库链接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步。
  6. 然后执行当前方法,方法中会执行sql
  7. 执行完成当前方法后,如果没有出现异常就直接提交事务。
  8. 如果出现了异常,并且这个异常时需要回滚的就会回滚事务,否则仍然提交事务。
  9. Spring事务的隔离级别对应的就是数据库的隔离级别
  10. Spring事务的传播机制时Spring事务自己实现的,也是Spring事务中最复杂的。
  11. Spring事务的传播机制基于数据库链接来实现的,一个数据库链接一个事务,如果传播机制配置为reques_new需要开启一个新的事务,那么实际上就是先建立一个数据库链接,在此数据库链接上执行sql。

spring事务失效的几种情况

  1. 数据库引擎不支持事务,如使用MyISAM数据引擎。
  2. 类没有被spring管理,比如service没有加@Service注解,那么这个类就不会被加载成一个Bean,那那这个类就不会被Spring管理,事务自然就失效了。
  3. 方法不是public的,因为spring事物是通过动态代理实现的,在对一个bean进行初始化过程中会目标方法进行判断,如果不是public修饰,那么TransactionAttribute返回null,即不支持事务。
  4. 方法内部调用,比如用this.method调用事务方法,由于spring的事务是通过代理实现的,那么使用this调用无法获取到TheadLocal中的数据库链接,这是会新生成一个数据库链接,因为不在同一个事务中,所以事务失效。这个情况可以在当前类中注入当前类。
  5. 方法用了final修饰,因为spring事务底层使用了aop,也就是jdk动态代理或者cglib,帮我们生成代理类,使用了final修饰后,那么在代理类中就无法重写该方法,从而无法实现事务功能。
  6. 关于异常的失效
    1. 在代码中进行了异常捕获,又没有手动抛出异常,这样spring会认为程序是正常执行的,不会回滚。
    2. 异常的抛出不正确,spring事务也不会回滚。spring事务默认情况下只会回滚RuntimeException和Error,对于普通的Exception它不会回滚。
    3. 在@Transactional注解中自定义了回滚异常,比如自定义的异常,在方法中抛出非自定义异常时,因为抛出的异常不属于自定义异常,此时也不会回滚。
  7. 多线程,因为spring事务中的数据链接是存储在ThreadLocal中的,所以不同线程获取到的数据库链接不同,所以事务会失效。
  8. 不支持事务的传播机制,NOT_SUPPORTS、NEVER

Spring 的注入方式:setter、构造方法、注解

bean的生命周期

spring中bean的生命周期_IT阿豪的博客-CSDN博客_springbean 生命周期?

Spring中Bean的生命周期是怎样的?_哔哩哔哩_bilibili

Spring中Bean的声明周期_秃头大魔王_的博客-CSDN博客_springbean的声明周期

  • 实例化Bean对象,通过bean工厂或构造方法,构造方法的查找先by Type再by name
  • 设置对象属性,通过setter方法注入
  • 如果Bean实现了BeanNameAware,则工厂调用setBeanName()方法,传递bean的id (student)
  • 如果Bean实现了BeanNameAware,则工厂调用setBeanFactory(),方法传入工厂本身
  • 将bean传递给bean的前置处理器PostProcessorBeforeInitialization(Object bean,String beanName)
  • 调用bean的初始化方法 init-method
  • 将bean传递给bean的后置处理器PostProcessorAfterInitialization(Object bean ,String beanName)
  • 使用bean
  • 容器销毁前,调用bean的销毁方法 destory-method。

spring循环依赖

  1. 场景:Aservice使用了Bservice:
  2. 在spring启动时,首先会对Aservice进行创建,然后将一个lambda表达式存入三级缓存中,然后对Aservice的属性进行填充。
  3. 由于Aservice中使用了Bservice,所以会去一级缓存(singletonObjects)中查找,在一级缓存中没找到,就会去创建Bservice。
  4. 创建Bservice,对Bservice进行属性填充,发现Bservice需要Aservice,去一级缓存和二级缓存中查找,发现没有Aservice,然后再去三级缓存中查找,通过Aservice的beanName找到了对应的lambda表达式,执行lambda表达式得到Aservice对象的代理对象,并将Aservice对象的代理对象存入二级缓存中,回填Bservice的属性。
  5. Bservice属性填充完毕,将Bservice存入一级缓存同时remove掉二级缓存和三级缓存的存储,至此Bservice初始化完成。
  6. 继续创建Aservice,初始化完成的Bservice填充到了Aservice中,把Aservice对象添加到一级缓存中,remove二级缓存和三级缓存。

beanfactory 和 factoryBean 的区别

  • beanFactory:负责生产和管理Bean的一个工厂接口,提供了springIOC的规范
    • getBean(String name): Spring容器中获取对应Bean对象的方法,如存在则返回该对象
    • containsBean(String name):Spring容器中是否存在该对
    • isSingleton(String name):通过beanName是否为单例对象
  • factoryBean:一种bean的创建方式,对bean的一种扩展。对于复杂的Bean对象初始化,建议使用factoryBean创建,它可以封装对象创建的细节。
    • AbstractBeanFactory:抽象Bean工厂,绝大部分的实现类,都是继承于他
    • DefaultListableBeanFactory:Spring默认的工厂类
    • XmlBeanFactory:前期使用XML配置用的比较多的时候用的Bean工厂

spring aop

  • aop,面向切面编程,我们的程序是线程执行的,aop可以在执行的某一个点横向切入,这个切入点就是joinpoint,aop的底层使用代理模式实现的,当没有配置代理模式是默认的代理模式为jdk,可以通过修改proxy-target-class=true更改为cglib代理模式。
  • aop提供了五种通知类型分别是:
    • 前置通知:在方法执行前通知
    • 后置通知:在方法执行后通知,无论方法是否发生异常
    • 返回通知:当方法正常执行完毕后进行返回通知
    • 异常通知:当方法出现异常时进行异常通知
    • 环绕通知:环绕通知可以自定义方法调用的时机,环绕通知实现了一个prossed方法用来执行方法。
    • 正常情况下通知的执行顺序
      • 前置->环目前代码->目标方法->环目后代码->后置->返回通知
    • 异常情况下通知的执行顺序.
      • 前置->环目前代码->目标方法->环目后代码->后置->异常
    • aop在调用通知方法时使用的是拦截器链的方式执行的,进入拦截器链的顺序如下:
      • 异常通知->最终通知->后置通知->前置通知,按照这个顺序依次向下调用,当调用到前置通知时,执行前置通知方法,然后执行目标方法,再调用后置方法,如果目标方法发生异常则直接执行异常执行,如果没有发生异常则进行返回通知。

spring ioc

  • ioc,控制反转,在没有ioc之前我们要在一个对象中使用另一个对象时,只能自己显式的去创建,而有了ioc以后,在对象中使用另一个对象时,ioc会帮助我们去创建,实现方式就是DI依赖注入。Spring提供的依赖注入的方式有构造方法注入、setter方法注入。使用依赖注入时有可能会导致循环依赖的问题。
  • ioc的核心主要有两个:
    • BeanFactory:BeanFactory为ioc提供了基础功能,spring文档中提到,beanFactory是用来兼容老版本的,除非有更好的原因否则应该使用appactionContext
    • appactionContext:appactionContext是BeanFactory的子接口,在原来的基础上支持了更多的功能,appactionContext的实现方式有四种:
      • fileSystemXmlAppactionContext:根据项目路径加载配置文件
      • classPathXmlAppactionContext:根据classpath加载配置文件
      • xmlWebAppactionContext:web初始化加载
      • annotationConfigAppactionContext:通过注解的方式启动spring容器

ioc容器的创建过程

  1. AnnotionConfigApplicationContext,通过注解的方式启动spring容器,进入AnnotionConfigApplicationContext的构造方法
    1. 方法内部结构代码为
      1. this(); 初始化bean的读取器和扫描器
      2. register();注册bean配置类
      3. refresh();刷新上下文
    2. this方法调用构造方法 创建reader、scanner,同时调用父类的构造方法创建DefaulListTableBeanFactory
    3. register方法中调用doRegisterBean方法,主要功能如下:
      1. 判断condition注册类是否需要跳过
      2. 设置bean的作用域
      3. 获取beanName
      4. 分析bean的定制化注解@Lazy,@Primary,@DependsOn,@Role,@Description,并添加这些定制化配置
      5. 注册bean
    4. refresh() 主要工作流程
      1. prepareRefresh() 刷新前的预准备工作
        1. 记录启动时间
        2. 设置启动标识
        3. 校验属性的合法性
        4. 保存容器中的早期事件
      2. beanFactory = obtionFreshBeanFactory() 获取刷新后的beanFactory
        1. 为beanFactory设置唯一的序列化id
      3. prepareBeanFactory(beanFactory) beanFactory的预先准备工作,主要是设置一些参数
        1. 设置类加载器
        2. 设置bean表达式解析器
        3. 设置忽略的自动装配接口,表示这些接口的实现类不允许自动注册
        4. 设置可以自动装配的接口
        5. 添加编译时的aspectJ

到此beanFactory的准备工作已完成,还需要进行一些后置的处理

      1. postProcessorBeanFactory(beanFactory) 空方法,留给子类扩展
      2. invokeBeanFactoryPostProcessor(beanFactory) 执行beanFactory的后置处理器
        1. 执行实现了BeanDefinitionRegisterPostProcessor和BeanFactoryPostProcessor这两个接口的postProcessor
        2. 根据实现的接口优先级按顺序执行priorityOrderd->orderd->nonOrderd
      3. registerBeanPostProcessor(beanFactory) 注册bean的后置处理器
        1. 获取所有beanPostProcessor
        2. 根据beanPostProcessor实现的接口进行分类
          1. priorityOrderd
          2. Orderd
          3. nonOrderd
          4. mergedBeanDefinitionPostProcessor
        3. 按顺序把四种后置处理器注册到容器中
        4. 注册一个特殊的后置处理器applicationListenerDetector
      4. initMessageSource() 进行国际化处理,消息绑定,消息解析
      5. initApplicationEventMulticaster() 事件派发
      6. onRefresh() 空方法,用于子类扩展
      7. registerListener() 注册事件监听器
      8. finishBeanFactoryInitialization(beanFcatory) 初始化所有非懒加载单实例bean
      9. finishRefresh() 完成beanFactory的初始化操作,IOC容器创建完成
        1. 清楚上下文资源缓存
        2. 初始化生命周期有关的后置处理器
        3. 回调生命周期的后置处理器
        4. 发布容器刷新完成事件

beanDefinition的底层是一个什么

Map beanDefinitionMap = new ConcurrentHashMap<>(256);

jdk proxy和cglib的区别

  1. jdk proxy 是由JDK的动态代理实现的,代理的对象必须实现接口。jdk动态代理的实现,要实现了InvcationHandler接口,通过执行Proxy.newProxyInstance(classLoader,interface,this);创建一个代理对象,然后调用invoke方法执行代理对象的方法。
  2. cglib 是基于ASM 框架使用字节码技术生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。cglib所代理的类不需要实现接口。

Spring的Bean是如果保障线程安全的

  1. 首先Bean分为多例和单例两种作用域,多例模式下是不存在线程安全问题的。
  2. 当Bean作用域设置为单例时,这个时候也是分情况的,要看这个Bean是有状态的,还是无状态的,有状态的就是指在使用过程中对Bean的属性进行了修改,那么这种Bean是存在线程安全的;无状态Bean相当于静态方法只用来调用,不会涉及到修改属性,这种无状态Bean不存在线程安全问题。
  3. 2q'q解决线程安全的方式通常有两种,一种是synchronized,另一种是threadlocal。
    1. synchronized是利用锁的机制保证同一时间下只有一个线程可以访问代码块
    2. threadlocal是为每一个线程创建了一个独立的变量副本,那是一个threadlocalmap,每个线程估计独自使用这个map,不受其他线程干扰。
      1. 后面就给他详细说threadlocal是咋实现的,弱引用可能会导致内存泄漏,用完了一定要remove。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值