写点什么

Spring

用户头像
ltc
关注
发布于: 3 小时前

Bean 生命周期

  1. 实例化 Instantiation

  2. 属性赋值 Populate

  3. 初始化 Initialization

  4. 销毁 Destruction



Bean 加载过程

以 ClassPathXmlApplicationContext 的启动为例,展开大致的分析:

  1. 调用 AbstractApplicationContext 的 refresh 方法,完成内部容器的加载,初始化,其中会调用 finishBeanFactoryInitialization 方法来初始化所有的 singleton bean

  2. 再调用 DefaultListableBeanFactory 的 preInstantiateSingletons 方法,其中重点是getBean

  3. getBean 本身调用的是 AbstractBeanFactory 的 doGetBean 方法,该方法我们仅仅分析获取 singleton 的相关逻辑步骤如下:

(1)先从缓存中获取 singleton bean(可能是 eagerly 类型的)

(2)如果获取的是 null,则需要进行创建,先加载 dependsOn 的 bean

(3)如果需要获取的是 singleton bean,则调用 DefaultSingletonBeanRegistry 的 getSingleton 方法

  1. getSingleton 会先对要创建的 bean 进行标记,然后调用传入的 singletonFactory 的 getObject 方法获取 bean,最后将新创建的 bean 放进缓存中

  2. singletonFactory 实际是一个 ObjectFactory 的子类,其中的 getObject 方法调用的 AbstractAutowireCapableBeanFactory 的 createBean 方法,该方法的核心是 doCreateBean

  3. doCreateBean 内部是 bean 创建的核心,包含了实例化,属性赋值,初始化,下面也大致分析下流程:

(1)createBeanInstance 方法创建 bean 实例,其方法内部会根据构造器类型选择如何进行创建 bean

(2)如果支持循环依赖,就会使用 DefaultSingletonBeanRegistry 的 addSingletonFactory 更新缓存

(3)populateBean 方法根据 bean 名称或类型进行依赖注入,其中会再调getBean方法获取依赖 bean

(4)initializeBean 方法会对 bean 实例进行初始化,会调用对应的 BeanPostProcessors 的方法


经过以上大致的步骤,bean 基本就加载完成了。

Bean 循环依赖


循环依赖其实就是循环引用,也就是两个或则两个以上的 bean 互相持有对方,最终形成闭环。


Spring 中循环依赖场景有:

(1)构造器的循环依赖

(2)field 属性的循环依赖。


检测循环依赖相对比较容易,Bean 在创建的时候可以给该 Bean 进行标记,如果递归调用回来发现正在创建中的话,即说明了循环依赖了。


Spring 怎么解决循环依赖

  • Spring 是通过递归的方式获取目标 bean 及其所依赖的 bean 的;

  • Spring 实例化一个 bean 的时候,是分两步进行的,首先实例化目标 bean,然后为其注入属性。


Spring 为了解决单例的循环依赖问题,使用了三级缓存。


这三级缓存分别指:

singletonFactories : 单例对象工厂的 cache

earlySingletonObjects :提前暴光的单例对象的 Cache

singletonObjects:单例对象的 cache


创建 bean 的时候,首先是从 cache 中获取这个单例的 bean,这个缓存就是 singletonObjects。主要调用方法就就是:


  • isSingletonCurrentlyInCreation()判断当前单例 bean 是否正在创建中,也就是没有初始化完成

  • allowEarlyReference 是否允许从 singletonFactories 中通过 getObject 拿到对象


分析 getSingleton 的整个过程:

  • Spring 首先从一级缓存 singletonObjects 中获取。

  • 如果获取不到,并且对象正在创建中,就再从二级缓存 earlySingletonObjects 中获取。

  • 如果还是获取不到且允许 singletonFactories 通过 getObject()获取,就从三级缓存 singletonFactory.getObject(三级缓存)获取,如果获取到了则移除该 singletonFactory,并将 bean 放入 earlySingletonObjects 中。其实也就是从三级缓存移动到了二级缓存。


实际上,Spring 解决循环依赖的诀窍就在于 singletonFactories 这个三级 cache。

当使用 createBeanInstance 方法创建 bean 的实例(调用了构造器,还未发生属性赋值以及初始化)后,会进一步调用 addSingletonFactory 方法更新缓存(会将对应的 singleton bean 的 ObjectFactory 存放起来),所以 Spring 此时将这个对象提前曝光出来让其他的 bean 引用。

Spring 动态代理

JDK 反射动态代理

(一)实现原理

  JDK的动态代理是基于反射实现。JDK通过反射,生成一个代理类,这个代理类实现了原来那个类的全部接口,并对接口中定义的所有方法进行了代理。当我们通过代理对象执行原来那个类的方法时,代理类底层会通过反射机制,回调我们实现的InvocationHandler接口的invoke方法。这就是JDK动态代理大致的实现方式。并且这个代理类是 Proxy 类的子类

(二)优点

  1. JDK动态代理是JDK原生的,不需要任何依赖即可使用;

  2. 通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;

(三)缺点

  1. 如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;

  2. JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。

  3. JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;

CGLib 动态代理

(一)实现原理

  CGLib实现动态代理的原理是,底层采用了ASM字节码生成框架,直接对需要代理的类的字节码进行操作,生成这个类的一个子类,并重写了类的所有可以重写的方法,在重写的过程中,将我们定义的额外的逻辑(简单理解为Spring中的切面)织入到方法中,对方法进行了增强。而通过字节码操作生成的代理类,和我们自己编写并编译后的类没有太大区别。

(二)优点

  1. 使用CGLib代理的类,不需要实现接口,因为CGLib生成的代理类是直接继承自需要被代理的类;

  2. CGLib生成的代理类是原来那个类的子类,这就意味着这个代理类可以为原来那个类中,所有能够被子类重写的方法进行代理;

  3. CGLib生成的代理类,和我们自己编写并编译的类没有太大区别,对方法的调用和直接调用普通类的方式一致,所以CGLib执行代理方法的效率要高于JDK的动态代理;

(三)缺点

  1. 由于CGLib的代理类使用的是继承,这也就意味着如果需要被代理的类是一个final类,则无法使用CGLib代理;

  2. 由于CGLib实现代理方法的方式是重写父类的方法,所以无法对final方法,或者private方法进行代理,因为子类无法重写这些方法;

  3. CGLib生成代理类的方式是通过操作字节码,这种方式生成代理类的速度要比JDK通过反射生成代理类的速度更慢;

SpringMVC 架构


第一步:发起请求到前端控制器(DispatcherServlet)

第二步:前端控制器请求 HandlerMapping 查找 Handler

第三步:处理器映射器 HandlerMapping 向前端控制器返回 Handler

第四步:前端控制器调用处理器适配器去执行 Handler

第五步:处理器适配器去执行 Handler

第六步:Handler 执行完成给适配器返回 ModelAndView

第七步:处理器适配器向前端控制器返回 ModelAndView

第八步:前端控制器请求视图解析器去进行视图解析

第九步:视图解析器向前端控制器返回 View

第十步:前端控制器进行视图渲染

第十一步:前端控制器向用户响应结果

Springboot Starter

SpringBoot 拥有很多方便使用的 starter(Spring 提供的 starter 命名规范 spring-boot-starter-xxx.jar,第三方提供的 starter 命名规范 xxx-spring-boot-starter.jar),比如 spring-boot-starter-log4j、mybatis-spring-boot-starter.jar 等,各自都代表了一个相对完整的功能模块。


SpringBoot-starter 是一个集成接合器,完成两件事:

  • 引入模块所需的相关 jar 包

  • 自动配置各自模块所需的属性


用户头像

ltc

关注

ltc 2019.07.04 加入

还未添加个人简介

评论

发布
暂无评论
Spring