spring 事务
- 事务的四个特性:原子性,一致性,隔离性,永久性
- 事务的配置方式
- 编程式,就是在配置文件中配置,使事务生效
- 声明式,使用注解使事务生效,这种方式是用aop实现的
- 事务的传播机制 PROPAGATION_*
- REQUIRED required
- spring的默认传播机制,当方法被调用时,如果外层方法有事务则延用此事务,如果外层没有事务则新建事务
- REQUES_NEW reques_new
- 每次都会新生成一个事务,同时把外层事务挂起,当当前事务执行完毕后,在继续执行外层事务。如果外层事务则执行当前事务即可。
- SUPPORT
- 如果外层有事务就加入外层事务,如果外层没有事务就按无事务执行,完全依赖外部事务。
- NOT_SUPPORT
- 此种传播机制不支持事务,当外层有事务时,先挂起外层事务,处理当前任务,无论是否有异常都不回滚。
- NEVER
- 不支持事务,如果外层有事务就抛异常
- MANDATORY mandatory
- 如果外层没有事务就抛出异常。
- NESTED nested
- 如果外层没有事务就创建要给事务,如果外层有事务就嵌套到外层事务中,当当前事务出现异常时,只回滚当前事务,当外层事务出现异常时会将当前事务及外层事务一同回滚。
- REQUIRED required
- 事务超时:如果事务执行时间太长则将其自动回滚,以免占用浪费数据库资源
spring中事务的实现
- Spring事务底层是基于数据库事务和AOP机制的
- 首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean
- 当调用代理对象方法时,会先判断该方法上是否加了@Transactional注解
- 如果加了,那么则利用事务管理器创建一个数据库链接
- 数据库链接是存储在一个ThreadLocal中,根据数据源获取数据库连接,同时也保证了在同一个线程下获取的数据库链接是相同的。
- 如果当A@Transactional方法去调用一个没有被spring管理的B方法时,B方法在执行数据库操作时无法从ThreadLocal中获取数据库链接,所以这也是为什么这种情况下会导致事务失效的原因。
- 并且修改数据库链接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步。
- 然后执行当前方法,方法中会执行sql
- 执行完成当前方法后,如果没有出现异常就直接提交事务。
- 如果出现了异常,并且这个异常时需要回滚的就会回滚事务,否则仍然提交事务。
- Spring事务的隔离级别对应的就是数据库的隔离级别
- Spring事务的传播机制时Spring事务自己实现的,也是Spring事务中最复杂的。
- Spring事务的传播机制基于数据库链接来实现的,一个数据库链接一个事务,如果传播机制配置为reques_new需要开启一个新的事务,那么实际上就是先建立一个数据库链接,在此数据库链接上执行sql。
spring事务失效的几种情况
- 数据库引擎不支持事务,如使用MyISAM数据引擎。
- 类没有被spring管理,比如service没有加@Service注解,那么这个类就不会被加载成一个Bean,那那这个类就不会被Spring管理,事务自然就失效了。
- 方法不是public的,因为spring事物是通过动态代理实现的,在对一个bean进行初始化过程中会目标方法进行判断,如果不是public修饰,那么TransactionAttribute返回null,即不支持事务。
- 方法内部调用,比如用this.method调用事务方法,由于spring的事务是通过代理实现的,那么使用this调用无法获取到TheadLocal中的数据库链接,这是会新生成一个数据库链接,因为不在同一个事务中,所以事务失效。这个情况可以在当前类中注入当前类。
- 方法用了final修饰,因为spring事务底层使用了aop,也就是jdk动态代理或者cglib,帮我们生成代理类,使用了final修饰后,那么在代理类中就无法重写该方法,从而无法实现事务功能。
- 关于异常的失效
- 在代码中进行了异常捕获,又没有手动抛出异常,这样spring会认为程序是正常执行的,不会回滚。
- 异常的抛出不正确,spring事务也不会回滚。spring事务默认情况下只会回滚RuntimeException和Error,对于普通的Exception它不会回滚。
- 在@Transactional注解中自定义了回滚异常,比如自定义的异常,在方法中抛出非自定义异常时,因为抛出的异常不属于自定义异常,此时也不会回滚。
- 多线程,因为spring事务中的数据链接是存储在ThreadLocal中的,所以不同线程获取到的数据库链接不同,所以事务会失效。
- 不支持事务的传播机制,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循环依赖
- 场景:Aservice使用了Bservice:
- 在spring启动时,首先会对Aservice进行创建,然后将一个lambda表达式存入三级缓存中,然后对Aservice的属性进行填充。
- 由于Aservice中使用了Bservice,所以会去一级缓存(singletonObjects)中查找,在一级缓存中没找到,就会去创建Bservice。
- 创建Bservice,对Bservice进行属性填充,发现Bservice需要Aservice,去一级缓存和二级缓存中查找,发现没有Aservice,然后再去三级缓存中查找,通过Aservice的beanName找到了对应的lambda表达式,执行lambda表达式得到Aservice对象的代理对象,并将Aservice对象的代理对象存入二级缓存中,回填Bservice的属性。
- Bservice属性填充完毕,将Bservice存入一级缓存同时remove掉二级缓存和三级缓存的存储,至此Bservice初始化完成。
- 继续创建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容器的创建过程
- AnnotionConfigApplicationContext,通过注解的方式启动spring容器,进入AnnotionConfigApplicationContext的构造方法
- 方法内部结构代码为
- this(); 初始化bean的读取器和扫描器
- register();注册bean配置类
- refresh();刷新上下文
- this方法调用构造方法 创建reader、scanner,同时调用父类的构造方法创建DefaulListTableBeanFactory
- register方法中调用doRegisterBean方法,主要功能如下:
- 判断condition注册类是否需要跳过
- 设置bean的作用域
- 获取beanName
- 分析bean的定制化注解@Lazy,@Primary,@DependsOn,@Role,@Description,并添加这些定制化配置
- 注册bean
- refresh() 主要工作流程
- prepareRefresh() 刷新前的预准备工作
- 记录启动时间
- 设置启动标识
- 校验属性的合法性
- 保存容器中的早期事件
- beanFactory = obtionFreshBeanFactory() 获取刷新后的beanFactory
- 为beanFactory设置唯一的序列化id
- prepareBeanFactory(beanFactory) beanFactory的预先准备工作,主要是设置一些参数
- 设置类加载器
- 设置bean表达式解析器
- 设置忽略的自动装配接口,表示这些接口的实现类不允许自动注册
- 设置可以自动装配的接口
- 添加编译时的aspectJ
- prepareRefresh() 刷新前的预准备工作
- 方法内部结构代码为
到此beanFactory的准备工作已完成,还需要进行一些后置的处理
-
-
- postProcessorBeanFactory(beanFactory) 空方法,留给子类扩展
- invokeBeanFactoryPostProcessor(beanFactory) 执行beanFactory的后置处理器
- 执行实现了BeanDefinitionRegisterPostProcessor和BeanFactoryPostProcessor这两个接口的postProcessor
- 根据实现的接口优先级按顺序执行priorityOrderd->orderd->nonOrderd
- registerBeanPostProcessor(beanFactory) 注册bean的后置处理器
- 获取所有beanPostProcessor
- 根据beanPostProcessor实现的接口进行分类
- priorityOrderd
- Orderd
- nonOrderd
- mergedBeanDefinitionPostProcessor
- 按顺序把四种后置处理器注册到容器中
- 注册一个特殊的后置处理器applicationListenerDetector
- initMessageSource() 进行国际化处理,消息绑定,消息解析
- initApplicationEventMulticaster() 事件派发
- onRefresh() 空方法,用于子类扩展
- registerListener() 注册事件监听器
- finishBeanFactoryInitialization(beanFcatory) 初始化所有非懒加载单实例bean
- finishRefresh() 完成beanFactory的初始化操作,IOC容器创建完成
- 清楚上下文资源缓存
- 初始化生命周期有关的后置处理器
- 回调生命周期的后置处理器
- 发布容器刷新完成事件
-
beanDefinition的底层是一个什么
Map beanDefinitionMap = new ConcurrentHashMap<>(256);
jdk proxy和cglib的区别
- jdk proxy 是由JDK的动态代理实现的,代理的对象必须实现接口。jdk动态代理的实现,要实现了InvcationHandler接口,通过执行Proxy.newProxyInstance(classLoader,interface,this);创建一个代理对象,然后调用invoke方法执行代理对象的方法。
- cglib 是基于ASM 框架使用字节码技术生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。cglib所代理的类不需要实现接口。
Spring的Bean是如果保障线程安全的
- 首先Bean分为多例和单例两种作用域,多例模式下是不存在线程安全问题的。
- 当Bean作用域设置为单例时,这个时候也是分情况的,要看这个Bean是有状态的,还是无状态的,有状态的就是指在使用过程中对Bean的属性进行了修改,那么这种Bean是存在线程安全的;无状态Bean相当于静态方法只用来调用,不会涉及到修改属性,这种无状态Bean不存在线程安全问题。
- 2q'q解决线程安全的方式通常有两种,一种是synchronized,另一种是threadlocal。
- synchronized是利用锁的机制保证同一时间下只有一个线程可以访问代码块
- threadlocal是为每一个线程创建了一个独立的变量副本,那是一个threadlocalmap,每个线程估计独自使用这个map,不受其他线程干扰。
- 后面就给他详细说threadlocal是咋实现的,弱引用可能会导致内存泄漏,用完了一定要remove。