Spring基础
yatbfm

注:大多数内容参考了JavaGuide

Spring

spring ioc的实现原理

https://javabetter.cn/springboot/ioc.html#%E6%98%AF%E4%BD%95
https://pdai.tech/md/spring/spring-x-framework-ioc-source-1.html#spring%E8%BF%9B%E9%98%B6--spring-ioc%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3%E4%B9%8Bioc%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84%E8%AE%BE%E8%AE%A1
IOC(控制反转)是一种设计思想,而不是一个具体的技术实现。将原本在应用程序中手动创建对象的控制权交由Spring来管理
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。

bean的生命周期

如果该bean是代理对象,则在执行InstantiationAwareBeanPostProcesor生成代理对象后会发生短路,不会按照正常的生命周期进行。

  1. 创建 Bean 的实例:Bean 容器首先会找到配置文件中的 Bean 定义,然后使用 Java 反射 API 来创建 Bean 的实例。
  2. Bean 属性赋值/填充:为 Bean 设置相关属性和依赖,例如@Autowired 等注解注入的对象、@Value 注入的值、setter方法或构造函数注入依赖和值、@Resource注入的各种资源
  3. Bean 初始化
  • 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
  • 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
  • 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
  • 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
  • 如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
  • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。
  1. 销毁 Bean:销毁并不是说要立马把 Bean 给销毁掉,而是把 Bean 的销毁方法先记录下来,将来需要销毁 Bean 或者销毁容器的时候,就调用这些方法去释放 Bean 所持有的资源。
  • 如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  • 如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的 Bean 销毁方法。或者,也可以直接通过@PreDestroy 注解标记 Bean 销毁之前执行的方法。

spring aop的

https://javaguide.cn/system-design/framework/spring/spring-knowledge-and-questions-summary.html#spring-aop

理解

AOP面向切面编程能够将那些与业务无关,却为业务模块所共同使用的逻辑(事务处理、日志管理、权限控制、限流等)封装起来,便于减少重复代码,降低模块间的耦合度,利于维护。
Spring中的AOP基于动态代理,分为JDK的动态代理和CGLib的动态代理。这两个的区别是:

  • JDK的动态代理要求被代理的对象实现了某个接口,然后会生成一个同样实现了这个接口的代理对象。
  • 如果没有实现某个接口,CGLib会生成一个被代理对象的子类作为代理对象。

image.png
image.png
spring aop是运行时增强,aspectj属于编译时增强。spring aop基于代理,而aspectj基于字节码操作

aspectj定义的通知类型有哪些

  • Before(前置通知):目标对象的方法调用之前触发
  • After (后置通知):目标对象的方法调用之后触发
  • AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
  • AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值
  • Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后操作,甚至不调用目标对象的方法

多个切面的执行顺序如何控制

  1. 使用@Order注解
  2. 实现Ordered接口重写getOrder方法

springboot利用了spring的什么特性进行封装的

spring怎么解决循环依赖的

https://javaguide.cn/system-design/framework/spring/spring-knowledge-and-questions-summary.html#spring-%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E4%BA%86%E8%A7%A3%E5%90%97-%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3

  1. 三级缓存

一级缓存:存放最终形态的Bean(已经实例化、属性填充、初始化)
二级缓存:存放过渡Bean(半成品,属性未填充),存放的是三级缓存产生的对象
三级缓存:存放ObjectFactory,调用其getObject方法生成对象。
A依赖B,B依赖A。当创建A时,发现依赖B,然后会去创建B,然后发现依赖A,此时会从三级缓存中获得A的ObjectFactory,通过这个对象调用getObject方法获取A的前期暴露对象,没有初始化完成,但是在堆中存在内存地址了,然后将ObjectFactory从三级缓存移除放到二级缓存中。

  1. @Lazy注解

如果一个bean被标记为懒加载,在容器启动时不会立即实例化,而是在第一次请求时才会创建。
如果A和B产生了循环依赖,在A的构造器添加@Lazy后(延迟B的实例化),加载流程:

  • 首先创建A的Bean,发现需要注入B
  • 在A上标注了@Lazy注解,因此spring会去创建一个B的代理对象,将这个代理对象注入到A的B属性中
  • 之后实例化B,在注入B中的A属性时,A已经创建完毕,就可以将A注入进去。

SpringBoot 2.6之后官方不推荐编写有循环依赖的代码,建议减少不必要的循环依赖。

由 Hexo 驱动 & 主题 Keep
访客数 访问量