Spring相关梳理

Spring

了解 Spring

1、Spring 通过 IoC 容器 把对象以bean的形式进行管 ,提供了访问 IoC 容器的根接口 BeanFactory。早期也被称作为万能胶 不仅管理我们 new 的对象,还可以管理其他框架以及组件,同时也降低了配置的离散度

2、另外它也提供了很多扩展点

  • BeanDefinitionReader 接口定义了约束规范,不光支持Xml、注解等形式,还可以扩展其他的形式
  • PostProccessor 后置处理器也增强了很多扩展功能
    • DI 依赖注入 就是通过子类 BeanFactoryPostProceessor 来扩展的,对依赖注入的不同操作进行相应的处理
    • AOP 面向切面 也是通过子类 BeanPostProceessor 的后置处理中扩展,另外AOP 还依赖了动态代理Jdk/Cglib

3、SpringMVCSpring 基于 Servlet 的 web 端轻量级框架,在父子容器的装配时需要注意:父容器不能直接访问子容器注入的对象,但是子容器可以访问父容器注入的对象。

4、SpringBoot 是基于 Spring注解驱动发展 以及 约定优于配置理念下的产物,SpringCloud 又是基于 SpringBoot 进行的再次扩展。

5、Spring 不光是个框架和容器,他也是一种生态,自身更是这个生态的基石。

Bean的加载过程

截图

扩展点 BeanDefinitionReader 为何使用的是接口而不是抽象类

接口自上而下;抽象类自下而上。 接口更注重用设计来提升扩展,只管设计方案不需要考虑谁来实现;而抽象类着重点在于重构,在已有的子类中来抽取共性。

语法上的区别:

  1. 接口被实现,抽象类被继承;
  2. 接口只能声明方法不能实现,抽象类可以声明也可以实现;
  3. 接口中的变量只能是公共的静态变量,抽象类可以是普通变量;

自定义增强器

//自定义增强器
public class MyBFPP implements BeanFactoryPostProccessor{
  @Override
  public void postProccessBeanFactory(ConfigurableListableBeanFactory beanFactory){
    BeanDefinition person = beanFactory.getBeanDefinition("person");
    person.set(); // 修改 BeanDefinition
  }
}
<!-- 注册到容器 -->
<bean class="com.xxx.Spring.MyBFPP"/>

BeanFactory 与 FactoryBean

  1. BeanFactory 是顶层接口。提供了 IoC容器 最基本的方式,给具体的 IoC 容器实现 提供了规范。并且也是访问 IOC 容器的根接口,提供方法支持外部程序对这些 bean 的访。

  2. FactoryBean 也是接口。 为 IoC容器中 Bean的实现提供了更加灵活的方式,在 IoC 容器的基础上给Bean的实现加上了一个简单的工厂模式和装饰模式。有三个方法isSingleton();getObjectType();getObject()

  3. BeanFactory 创建的对象必须遵循完整的 bean 的生命周期,而 FactoryBean 没有标准的流程。BeanFactory 如果比作是流水线,那 FactoryBean 就是私人订制。

Bean 生命周期

截图

循环依赖

// 相互依赖问题
public class A{ private B b; }
public class B{	private A a; }

截图

//一级缓存 存的是Object:实例话初始化都已完成的对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//二级缓存 存的是Object:实例话完成,初始化未完成的对象
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
//三级缓存 存的是函数式接口:lambda表达式,防止容器同名代理对象被覆盖原始对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//执行顺序:从一级、二级、三级层层查找。

ObjectFactory<?>,函数式接口,有且仅有一个方法并且当作参数传入声明此类型的方法,可以接收 lambda 表达式,在执行的时候不会执行 lambda 表达式,而在调用 getObject 方法的时候才会去调用 lambda 处理的逻辑

一个 Map 可否解决

不能,如果只有一个Map,初始化完成与初始化未完成的对象都要放入一个Map,而未完成初始化的对象不能暴露给外部使用,所以必须要分开存储。

两个 Map 可否解决

,但是有前提条件:不使用 AOP 的時候,或者说在BeanPostProccessor的后置处理方法中没有创建代理对象的时候,两个 Map 可以解决循环依赖。否则会报错:Bean的版本问题。

三级缓存为何可以解决

1、创建代理对象的时候需要有原始对象,同一个容器不能出现同名的两个不同对象。如果需要代理对象,那么代理对象创建完成之后就会覆盖掉原始对象。

2、对外暴露的时候需要准确的对象,要么原始对象要么代理对象。但是代理对象的创建是在BeanPostProccessor的后置处理方法中,解决循环依赖问题的时候还没有执行到那一步,因此需要 lambda 表达式,类似于一种回调机制,就可以准确的暴露对象了。

3、什么时候需要暴露于,程序没办法确定,所以需要通过lambda的方式,在确定对外暴露的时候才去确定

Spring MVC

工作原理图

截图

配置拦截器

两种方式

1、实现 HandlerInterceptor 接口 或者 继承 HandlerInterceptorAdapter 类,配置拦截器的 Bean

2、实现 Spring的WebRequestInterceptor 接口,或者是继承实现了 WebRequestInterceptor的类

Spring Boot

约定优于配置理念下的产物

在 @SpringBootApplication 注解下有三个重要的注解

  • @EnableAutoConfiguration,用于自动装配
  • @SpringBootConfiguration,配置类,内部使用的就是@Configuration
  • @ComponentScan,扫描路径加载IoC容器

自动装配

自动装配的过程

@EnableAutoConfiguration 引入了 @Import(AutoConfigurationImportSelector.class)注解,而AutoConfigurationImportSelector 实现了 ImportSelector 的子接口 DeferredImportSelector,然后通过 selectImport 配合 SPI 机制,来实现自动装配。

SPI 机制

通过SpringFactoriesLoader扫描classpath:META-INF/spring.factories 中所有的文件。

在SpringBoot中分为两种:三方包、官方包

  • 三方包命名规则 mybatis-spring-boot-starter ,在自己引入的包下的META_INF/spring.factories 中配置,扫描到可以进行自动装载。
  • 官方包命名规则 spring-boot-starter-data-redis,在spring-boot-antoconfiguration下的 META_INF/spring.factories 中配置,里面有一堆定义好的配置信息。通过@ConditionelOnClass注解进行条件控制,只有符合条件的配置信息才会进行自动装载。

Starter 组件

Starter 组件是 Spring Boot 提供 开箱即用 的组件

本质上就是通过 maven 编译安装的 JAR 包,依赖于自动装配 遵循 SPI 机制 来实现的 开箱即用

Actuator

就是用于监控的组件,通过management-endpoints去设置一些需要监控的内容。为了安全考虑,默认只开放 info、health

除此之外有个重要的监控 metrics用于监控运行指标的

  • JVM(垃圾回收,内存)
  • 系统(运行时间,平均负载,处理器信息)
  • 线程池 信息
  • tomcat 会话信息
  • ...

可以通过PrometheusGrafana 等工具,进行图表展示。

Spring Boot CLI

是个命令行工具

用于快速开发 Spring 应用

通过 Groovy 脚本运行

posted @ 2022-05-07 15:44  柒徳咙咚呛  阅读(90)  评论(0)    收藏  举报