敖丙思维导图-Spring

敖丙思维导图系列目录

这些知识整理都是自己查阅帅丙资料(当然还有其他渠道)加以总结滴~ 每周都会更新知识进去。
如有不全或错误还请大家在评论中指出~


  1. 敖丙思维导图-集合
  2. 敖丙思维导图-多线程之synchronized\ThreadLocal\Lock\Volatitle\线程池
  3. 敖丙思维导图-JVM知识整理
  4. 敖丙思维导图-Spring
  5. 敖丙思维导图-Redis
  6. 敖丙思维导图-RocketMQ+Zookeeper
  7. 敖丙思维导图-Mysql数据库

本文章目录

  • 敖丙思维导图系列目录
    • Spring Bean的生命周期(doCreateBean)
      • 概述
      • 详细步骤
      • 循环依赖情况(- 不支持prototype 应为bean实例不唯一)
      • 解决循环依赖(A-B-A)
      • Spring Bean的作用域
    • 父子容器
    • Spring事务
      • 事务的7种传播机制
      • @Transactional失效场景
    • IOC容器实现(避免在各处new来创建类,做到统一维护)
    • Bean
    • AOP
      • 为什么JDK动态代理要实现接口
      • BeanFactory与FactoryBean
      • BeanFactory和ApplicationContext的区别
    • SpringBoot - 开箱即用 约定大于配置
      • SpringApplication的初始化:
      • run()方法


![在这里插入图片描述](https://img-blog.csdnimg.cn/2020061616100412.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbnRMaWdodA==,size_16,color_FFFFFF,t_70) ## 设计模式 - Spring结合了工厂模式和反射机制实现IOC容器。 - `单例模式`,提供了全局的访问点BeanFactory;。 - Spring中的BeanFactory就是`简单工厂模式`的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。 - `观察者模式`:listener的实现。如ApplicationListener。 - `模板方法`: xxxApplicationContext 的refush()。 - `代理`:(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。 - `装饰器模式`:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。(比如依赖注入就需要使用BeanWrapper) - `策略模式`:Bean的实例化的时候决定采用何种方式初始化bean实例(反射或者CGLIB动态字节码生成)

策略模式:抽象了出了接口,将业务逻辑封装成一个一个的实现类,任意地替换。利用java8的Map与函数式接口来实现。

Spring Bean的生命周期(doCreateBean)

概述

进入createBean方法,如果有代理方法(AOP)则调用postProcessBeforeInstantiation,换原本的Bean作为代理。进入doCreateBean方法,调用createBeanInstance创建bean实例。(这里判断了Spring的循环依赖问题)
这时进入populateBean方法,调用postProcessAfterInstantiation方法,如果调用返回false,表示不必继续进行依赖注入,直接返回。如果返回true,向 bean 的成员变量注入自定义的信息。最后通过BeanWrapper提供的设置属性的接口完成属性依赖注入(DI)。
下一步进入initializeBean(),首先判断是否实现了Aware接口,有的话就注入相关属性。接着调用初始化的前置(BeanPostProcessor)操作,接着执行初始化的方法,最后调用bean初始化的后置(BeanPostProcessor)操作。
注册Bean的销毁逻辑。当Bean不再需要时,销毁Bean。

详细步骤

InstantiationAwareBeanPostProcessor作用于实例化阶段的前后。

  1. 实例化 createBeanInstance()
    Ioc容器通过获取BeanDefinition对象中的信息进行实例化,实例化对象被包装在BeanWrapper对象中
  • postProcessBeforeInstantiation在doCreateBean之前调用,该方法的返回值会替换原本的Bean作为代理,这也是Aop等功能实现的关键点。
  • postProcessAfterInstantiation该方法在属性赋值方法内,但是在真正执行赋值操作之前。其返回值为boolean,返回false时可以阻断属性赋值阶段

Spring为bean提供了两种初始化bean的方式,实现InitializingBean接口,实现afterPropertiesSet方法(我的策略模式就是这么干的),或者在配置文件中通过init-method指定,两种方式可以同时使用。

  1. 属性赋值 populateBean()
    通过BeanWrapper提供的设置属性的接口完成属性依赖注入(DI);

BeanPostProcessor作用于初始化阶段的前后,它也会注册为Bean

  1. 初始化 initializeBean()
    注入Aware接口+自定义的处理 (所有的Aware方法都是在初始化阶段之前调用的)

  2. 销毁 容器关闭时调用

// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
     
   //bean实例包装类
   BeanWrapper instanceWrapper = null;
   if (instanceWrapper == null) {
     
       //创建bean的时候,这里创建bean的实例有三种方法
			//1.工厂方法创建
			//2.构造方法的方式注入
			//3.无参构造方法注入
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
     
       //填充bean实例的属性
      populateBean(beanName, mbd, instanceWrapper);
       //初始化bean,过程如下:
			//1:判断是否实现了BeanNameAware,BeanClassLoaderAware,
			//   BeanFactoryAware方法,如果有,则设置相关的属性
			//2: 调用bean初始化的前置(BeanPostProcessor)操作
			//3: 执行初始化的方法。
			//	如果有initializingBean,则调用afterPropertiesSet
			//	如果有InitMethod,则调用初始方法
			//4: 调用bean初始化的后置(BeanPostProcessor)操作
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }  
 }

敖丙思维导图-Spring_第1张图片
第一大类:影响多个Bean的接口

  • BeanPostProcessor (SentinelBeanPostProcessor)
  • InstantiationAwareBeanPostProcessor
  1. postProcessBeforeInstantiation在doCreateBean之前调用,也就是在bean实 化之前调用的,该方法的返回值会替换原本的Bean作为代理,这也是Aop等功能实现的关键点
  2. postProcessAfterInstantiation在真正执行赋值操作之前,返回false时可以阻断属性赋值阶段。

执行顺序
BeanPostProcessor有很多个,而且每个BeanPostProcessor都影响多个Bean,其执行顺序至关重要,必须能够控制其执行顺序才行。
1 PriorityOrdered是一等公民,首先被执行,PriorityOrdered公民之间通过接口返回值排序
2 Ordered是二等公民,然后执行,Ordered公民之间通过接口返回值排序
3 都没有实现是三等公民,最后执行

第二大类:只调用一次的接口

  • Aware类型的接口
  • 生命周期接口
  1. Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。Aware都是在初始化阶段之前调用

Aware Group1
1 BeanNameAware
2 BeanClassLoaderAware
3 BeanFactoryAware
Aware Group2
1 EnvironmentAware
2 EmbeddedValueResolverAware 这个知道的人可能不多,实现该接口能够获取Spring EL解析器,用户的自定义注解需要支持spel表达式的时候可以使用,非常方便。
3 ApplicationContextAware

  1. 两个生命周期接口
  • InitializingBean 对应生命周期的初始化阶段。(在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式)
  • DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy()方法 。

循环依赖情况(- 不支持prototype 应为bean实例不唯一)

  • 构造器循环依赖(singleton prototype-通过@Scope()指定 ) 都不支持 (因为你一上来就要属性注入)
  • Setter注入循环依赖(singleton prototype) - 只支持singleton Setter注入

scope=singleton(默认,单例,生成一个实例) 不是线程安全,性能高
scope=prototype(原型多线程, 生成多个实例)

解决循环依赖(A-B-A)

  1. 初次创建A(getBean())时,调用addsingletonFactory将A实例对应的ObjectFactoy放到三级缓存中,调用populateBean赋值属性B;(addsingletonFactory属性没被注入,populateBean方法是用来赋值的
  2. 因为A依赖B,调用getBean获取B。将B的ObjectFactoy放到三级缓存,调用populateBean赋值B,此时因为B依赖A,此时进入getSingleton()方法, 判断到三级缓存singletonFactories中存在A(单例工厂实例),就调用单例工厂的getObject方法返回对象实例;将实例A放入二级缓存;从三级缓存中移除A。
  3. getBean(A),从三级缓存中找到A对应的实例,赋值到B
  4. B实例放入一级缓存,并清空其它级缓存。彻底完成B的创建,返回给A
  5. A放入一级缓存,并清空其它级缓存
一级缓存singletonObjects(ConcurrentHashMap):用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
二级缓存earlySingletonObjects(HashMap):提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
三级缓存singletonFactories(HashMap):单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖

Spring Bean的作用域

作用域 描述
singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
application 限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

敖丙思维导图-Spring_第2张图片

父子容器

Spring和SpringMVC的容器具有父子关系,Spring容器为父容器,SpringMVC为子容器,子容器可以引用父容器中的Bean,而父容器不可以引用子容器中的Bean。(例子:ssm框架,spring可以管理service,mapper,springmvc管理controller,mybatis编写mapper,controller就需要调用service,service调用mapper,因为springmvc容器是spring的子容器,可以通过父容器找到service和mapper,但是在service中却是找不到controller的。保证一种资源的局部性。)

Spring事务

Spring支持编程式事务管理以及声明式(基于AOP)事务管理两种方式。

事务的传播性:
@Transactional(propagation=Propagation.REQUIRED)
事务的隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读,
不可重复读) 基本不使用
只读: (一次执行多条查询语句,也需要回滚)
@Transactional(readOnly=true)
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
事务的超时性:
@Transactional(timeout=30)
回滚:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class,
Exception.class})

事务的7种传播机制

一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行

  • PROPAGATION_REQUIRED (默认)
    如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
  • PROPAGATION_REQUES_NEW
    该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
  • PROPAGATION_SUPPORT
    如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
  • PROPAGATION_NOT_SUPPORT(只读的时候在设置个ReadOnly)
    该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
  • PROPAGATION_NEVER
    该传播机制不支持外层事务,即如果外层有事务就抛出异常
  • PROPAGATION_MANDATORY
    与NEVER相反,如果外层没有事务,则抛出异常
  • PROPAGATION_NESTED
    该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。

@Transactional失效场景

  1. @Transactional 应用在非 public 修饰的方法上
  2. @Transactional 注解属性 propagation 设置错误
  3. @Transactional 注解属性 rollbackFor 设置错误
  4. 同一个类中方法调用,导致@Transactional失效
    (方法A没有声明注解事务,而B方法有。则外部调用方法A)
  5. 方法中的异常被自己catch
  6. 数据库引擎不支持事务
  7. spring、springMVC重复扫描,父子上下文重叠
    (实例理应由父容器进行初始化以保证事务的增强处理)

IOC容器实现(避免在各处new来创建类,做到统一维护)

  1. 创建注解 (Controller、Service、Repository、Component)
  2. 提取标记对象(URL提取、获取类加载器、获取Class对象)
  3. 依赖注入(Autowired、成员变量实例的注入(解决多个实现类))
  4. 实现容器(单例模式(饿汉懒汉)、创建容器载体、实现容器加载)

Spring 解决了关键的问题:将对象之间的关系转而用配置管理

  • 依赖注入—依赖关系在IOC容器里管理
  • 通过把对象包装在Bean中以达到管理对象和进行格外操作的目的

Bean

  • Bean的本质就是java对象,只是这个对象的生命周期由容器来管理
  • 不需要再java类上添加额外限制(低侵入)
  • spring根据配置生成描述bean的beanDefinition。(jdk用java.lang.class描述) 这里需要注意Spring框架的5种作用域(@Scope)、懒加载(@lazy)、首选(@Primary)设置为true的bean是优先的实现类、factory-bean/method(@Bean和@Configuration)
    容器初始化:解析配置->定位与注册对象

AOP

静态代理:实现类
动态代理:

  • JDK动态代理 (实现接口),java反射机制生成一个代理接口的匿名类,调用具体方法的时候调用invokeHandler

通过bind方法建立代理与真实对象关系,通过Proxy.newProxyInstance(target)生成代理对象。
代理对象通过反射invoke方法实现调用真实对象的方法

  • CGLIB (asm字节码编辑技术创建类,基于classLoad装载,修改字节码生成子类处理)

为什么JDK动态代理要实现接口

  • 动态代理产生的类需要继承proxy类获得有关方法和InvocationHandler构造方法传参。但java不能同时继承两个类,我们需要和想要代理的类建立联系,只能实现一个接口
  • 需要反射获得代理类的有关参数,必须要通过某个类,反射获取有关方法
  • 成功返回的是object类型,要获取原类,只能继承/实现,或者就是那个代理类
  • 对具体实现的方法内部并不关心,这个交给InvocationHandler.invoke那个方法里去处理就好了,我只想根据你给我的接口反射出对我有用的东西。
  • 考虑到设计模式,以及proxy编者编写代码的逻辑使然

静态代理: 程序运行前就已存在的编译好的代理类。实现步骤:
1、定义业务接口。
2、实现业务接口。
3、定义代理类并实现业务接口,最后通过客户端调用。
动态代理:程序运行期间根据需要动态创建代理类及其实例已完成功能

BeanFactory与FactoryBean

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

BeanFactory和ApplicationContext的区别

  • BeanFactory:
    是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
    BeanFactory在启动的时候不会去实例化Bean,中有从容器中拿Bean的时候才会去实例化;

  • ApplicationContext:
    应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;
    ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化;

  1. 国际化(MessageSource)
  2. 访问资源,如URL和文件(ResourceLoader)
  3. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层
  4. 消息发送、响应机制(ApplicationEventPublisher)
  5. AOP(拦截器)

SpringBoot - 开箱即用 约定大于配置

SpringBoot的自动装配原理,其实就是在项目启动的时候去加载META-INF下的spring.factories文件。
敖丙思维导图-Spring_第3张图片

  • 开箱即用
    SpringBoot所有自动配置类都是在启动的时候进行扫描并加载,通过spring.factories可以找到自动配置类的路径,但是不是所有存在于spring,factories中的配置都进行加载,而是通过@ConditionalOnClass注解进行判断条件是否成立(只要导入相应的stater,条件就能成立),如果条件成立则加载配置类,否则不加载该配置类。
  • 约定大于配置
    我们的配置文件(.yml)应该放在哪个目录下,配置文件的命名规范,项目启动时扫描的Bean,组件的默认配置是什么样的(比如SpringMVC的视图解析器)等等等等这一系列的东西,都可以被称为约定,

SpringApplication的初始化:

  1. 推断web应用类型 ( none, servlet, reactive响应式非阻塞)
  2. 初始化应用上下文
    从Map中根据org.springframework.context.ApplicationContextInitializer的类型拿到需要的类初始化类,进入getOrDefault把加载到的需要初始化的类进行实例化添加到一个集合中等待备用
  3. 初始化监听器类
    初始化监听类的时候和上面初始化应用上下文是一样的代码。
    唯一不同的是getSpringFactoriesInstances(ApplicationListener.class))传进去的是ApplicationListener.class,而初始化应用上下文传入的参数是ApplicationContextInitializer.class
  4. 推演出主程序类

run()方法

  1. 加载运行监听器
  2. 根据Web应用类型来创建对应的环境
  3. 根据Web应用类型来创建容器
  4. 准备应用上下文 prepareContext
  5. 刷新应用上下文 (这时创建了Tomcat对象)
  6. 发布监听应用启动事件
  7. 执行Runner
  8. 发布上下文准备完成的事件

在SpringBoot中启动tomcat的工作在刷新应用上下文上下这一步。而tomcat的启动主要是实例化两个组件:Connector、Container,一个tomcat实例就是一个Server,一个Server包含多个Service,也就是多个应用程序,每个Service包含多个Connector和一个Container,而一个Container下又包含多个子容器。

你可能感兴趣的:(面试复习,spring,spring,java)