整理学习笔记-spring

35 阅读8分钟

Spring

Spring是什么,解决了哪些问题,怎么解决的

Spring 是一组简化企业级应用开发,协助程序员开发的一组框架

主要解决了对象创建和对象依赖管理(ioc), 提供一种代码解耦的方式(aop)

Ioc

ioc的定义,Spring 对ioc的实现

Ioc 的名称为控制反转, 这里的控制指的是依赖对象的控制,反转指控制权限反转。

传统开发模式中,对象所依赖的对象都是由自己管理的,自己负责创建对象。这样可能会导致对象与对象之间的耦合过重, 如果对象的创建方式发生变更,每个依赖他对象几乎都会受到影响。 ioc是一种设计思想,解决了对象创建及依赖管理的问题。 基本思想是有一个外部容器负责对象的创建及依赖管理。 ioc的实现方式有两种, 一种是依赖查找另一种为依赖注入, 主要区别是容器主动注入依赖对象还是被动等着对象去查询依赖对象后使用。 Spring 使用的是依赖注入的方式, 容器会负责对象的创建, 以及依赖对象的创建和注入。

依赖注入的方式,bean的作用域,bean的装配方式,bean的生命周期

依赖注入的方式常用的有两种,构造器注入和set注入, 还有一些xml时期p标签和c标签的注入方式

bean的作用域,一般有单例和原型模式, 在服务器模式中还有request,Session,application模式

Bean 的装配方式有byName 和 byType两种,依赖bean的名称和类型查找bean注入, 我们经常用的autowrite 是 byType方式注入的, 如果有多个type 会报冲突, 可以使用注解指定当冲突时使用名字。resource注解可以指定byName 和 byType 如果不指定默认先按name 再按type查找

bean的生命周期, 加载beanDefinition, 实例化bean,填充属性,初始化bean, 使用bean, 销毁bean。

Spring bean的加载过程

不管是xml方式、注解方式、还是spring boot的自动装配, 都会走到AbstractApplicationContext类的refresh方法

  1. 先进行beanFactory 的创建, 后续的bean创建都是基于bean工厂来的,beanFactory的默认实现是defaultLisatableBeanFactory。 beanFactory创建的核心是完成bean的扫描, 创建过程会扫描xml配置的bean, 而componentScan注解指定的会通过beanFactoryPostProcess扩展类完成扫描, 最终扫描到的bean会以beanDefinition的方式存储, 这个数据结构完整的定义了bean的所有信息, 包括beanName、classType、scope、需要等待前置初始化的类、initMethod、destroyMethod、包含的属性信息, 依赖哪些bean等。
  2. 其次是bean的创建, bean的创建分成了三步, 分别是实例化bean, 填充属性,初始化bean。 spring提供了大量的BeanPostProcess实现类和aware实现类,几乎每一步的前后都有扩展点, 使用者可以在bean初始化的任意阶段加入自己的逻辑,读源码的时候这些扩展点还挺影响主脉络的。
  3. 实例化bean主要是创建一个bean的实例, spring 也提供了多种初始化方式, 可以指定suppler方法创建,也可以继承beanPostProcess直接返回, 如果都未使用,默认使用反射来创建对象, 如果使用构造器注入的bean,会在这时候创建。这也是为啥spring不能解决构造器注入导致的循环依赖。
  4. 填充属性主要是完成spring依赖的bean注入
  5. 初始化主要是调用一些初始化方法,如果实现了initMethod就调用, 还有一个常见的扩展是当前bean如果继承了initializatingBean,会在这调用afterPropertiesSetf方法,正常情况下动态代理的创建也在这个阶段进行

autowrite注解的实现原理

在 populateBean 阶段 使用autowriteBeanpost process 处理,查找相关注解的bean并注入。

循环依赖, 三级缓存问题

循环依赖是指对象之间存在依赖环的问题, 就是对象A依赖对象B,同时对象B也依赖对象A。 按照传统的思路,创建对象A的时候发现依赖对象B,回去执行对象B的创建, 创建对象B时又发现依赖对象A。 这样就形成了循环,无法完成对象的创建。

Spring 解决循环依赖的情况使用了缓存机制, 将对象的创建分为了三步, 分别是实例化对象、填充属性和初始化, 当实例化对象完成后,将对象的objectFactory放入缓存中,在填充属性时如果发现依赖对象,会从缓存中取出objectFactory返回bean。 不涉及依赖对象的属性填充,就能避免循环依赖问题。 需要注意的是spring只能解决单例及set注入的bean。 如果因为dependsOn发生的循环依赖也不能解决。

spring的三级缓存主要是已创建完成的单例对象、提前暴露的对象、以及objectsFactorys等三个map。

当执行getBean方法时, 会先从getSingletonObject方法中获取bean, 先从singleonObjects中尝试查找创建完成的bean, 如果找不到就从早期暴露的bean中查找, 如果早期暴露的bean也没有, 会从beanFactorys查找当前bean的beanFactory, 使用beanFactory创建bean, 然后放到早期暴露的bean中。

可以使用二级缓存吗, 可以, 早期暴露bean直接放beanFactory创建好的对象,不影响spring正常运行。 设计三级缓存主要是兼容aop的设计理念, aop的定义是将一些逻辑与bean进行解耦,他的业务逻辑与bean是隔离的, 应该在bean创建完成后再进行代理对象的创建。 如果直接将beanFactory对象放二级,相当于在实例化之后直接进行对象代理了,后续再进行属性填充和初始化,将aop的逻辑耦合进了对象逻辑中, 这与aop的设计理念是不符合的。 所以spring设计了第三级缓存, 只有在出现循环依赖的时候, 才进行代理对象的创建。 可以说第三级缓存延迟了对象提前暴露的时机。 只有出现循环依赖时才提前暴露对象。

可以用一级缓存吗,不可以, 一级缓存的解决方案是实例化完就可以被访问到, 但现在还是半成品, 万一被使用容易出问题。

Beanfactory 和factorybean的区别

他俩只是长得像, 可以说差别很大, beanFactory是创建bean的工厂, FactoryBean是factory类型的bean, 也是spring的一个接口, 定义了getObjects方法。 可以通过它创建对象。

Aop

aop是什么,解决的问题

aop是对oop的一种补充, 传统的oop中,所有业务逻辑都是封装在对象内部的, 对象内部定义了该对象的属性和所有行为, 这种开发模式有一个弊端就是一些公共的业务逻辑可能在每个类中都有, 像日志、安全校验等。 不仅代码冗余,而且一旦有公共逻辑需要修改, 几乎所有的类都会受影响。 aop是一种面向切面的编程思想,给对象切一刀,在切面上增加逻辑。

实现原理

Spring 的aop是通过动态代理方式实现的, 给每个需要增强的类创建代理对象, 代理对象中有着目标对象和一组拦截器, 在不同切点会执行不同的拦截逻辑。 如果代理类实现接口, 优先使用jdk的动态代理, 如果没有接口会使用cglib生成子类。

项目中用过aop吗

标签推荐的时候, 用aop打了每个召回的结果与最终返回的结果, 拼接入参后接到kafka做了trace。

注解类增加Aspect注解, 定义切点 pointcout, 然后在切点上增加各种增强 方法就行了, 例如afterBefore可以在方法返回前增加逻辑。

事务

事务的实现机制, 事务的传播级别

应用启动时为需要使用事务的类生成代理类,以及将事务能力(拦截逻辑)织入进去,在实际调用目标类事务方法的时候,被代理类中

ReflectiveMethodInvocation拦截,然后先调用拦截器中的事务逻辑,然后再调用目标类的业务逻辑,最后处理异常回滚和提交

事务失效的场景

private 方法, 无法代理

内部自调用,不会触发代理

异常没有抛出来, 代理类无法感知异常

Spring boot 自动装配

编写了springboot-start的jar都会维护一个spring.factorys, 里面会有一些configuration配置类,通过该类加载对应的bean,实现自动装配

springbootApplication注解相当于三个注解, 一个是springbootConfiguration , 说明当前类是一个bean,一个是componentScan, 指定扫描哪些包,默认是当前类所在包及其子路径下的类。 一个是enableAutoConfiguration, 开启自动装配。