二月份工作复盘


在家办公的一个月

1.主要工作内容

1.1 简化Feign客户端的初始化:团队原本使用的是GitHub下的open-feign项目实现的微服务之间的远程调用,使用Feign进行调用的时候采用原的Feign客户端声明方式,在配置中声明一个客户端@Bean,利用Feign.builder()创建一个Feign的客户端进行http的远程调用,如:

...

@Bean

public LebronJames lebronJames() {

    return Feign.builer().decoder(decoder).encoder(encoder).requestInterceptor(interceptor).

        target(LebronJames.class,targetUrl);

}

...

每个调用者都需要在项目中声明若干个这样的Feign客户端并引入相关的一些配置项和一些配置文件,这样增加了开发的复杂性,因此设计一个方法简化Feign的调用方式。

1.2 搭建一个前端脚手架,这里主要是为了以后团队每个微服务提供一个独立的前端页面使用,即每个独立的微服务架构的基础服务拥有自己的管理页面,不在统一的页面进行维护。

这里领导要求使用nodejs技术栈进行搭建,因此我就选择了element-ui + vue + vue-router + axios + webpack进行搭建,由于之前有过vue开发的经历,所以这个工作应该是很简单的哈哈。

2. 工作思路

2.1 利用Spring生命周期完成Feign客户端的声明和注入

对于第一个任务,其实这里有个坑,因为最初搭建微服务的时候技术选型是我做的,在使用Feign的时候,最初是有两个选择的,一个GitHub的open-feign项目(项目地址:https://github.com/OpenFeign/feign)和SpringCloud生态系统下的NetFlix Feign,最后种种原因就选择了GitHub下的open-feign项目,它的初始化就如第一部分所示,采用链式builder的方式声明。

由于是需要将一个接口代理在初始化的时候声明成一个Bean交给IOC容器管理,很容易就联想到了利用Bean的生命周期在Bean初始化的时候完成Feign客户端的装配和后面的接口注入,由于项目是基于SpringBoot,所以这里直接实现BeanPostProcessor接口并重写一下postProcessBeforeInitialization(Object bean,String beanName)方法,在获取自定义注解的接口代理后完成对应的注入工作。

实现BeanPostProcessor
重写postProcessBeforeInitialization(Object bean,String beanName)

我们在feignBuilder里完成对Feign客户端实例的初始化,这里应该注意builder()所使用的其他Bean应该优先加载,使用@Order()控制加载顺序。

Feign实例的初始化

通过injectFeign()方法完成Bean的注入。

Feign客户端的注入

至此总得步骤大概完成了。

2.2 搭建Vue脚手架

其实这个比较简单,网上教程一大堆,在安装好node环境后:

1. 命令

npm config set registry https://registry.npm.taobao.org

2. 验证命令

npm config get registry

如果返回https://registry.npm.taobao.org,说明镜像配置成功。

3.安装cnpm

npm install -g cnpm --registry=https://registry.npm.taobao.org

4.安装vue-cli4

cnpm install -g @vue/cli --> 检查vue -V

注:windows请使用cmd,不要使用gitbash或者power shell

5.使用vue-cli创建项目

vue create xxxxx -m cnpm

创建项目的时候选择自定义创建,勾选上vue-router、eslint、axios等组件

6.配置并启动项目

IDEA中进入terminal选项进入前端项目中,执行cnpm install命令安装依赖。

在IDEA中配置npm,选择当前项目的package.json,script输入dev。

修改config下的index.js文件:修改host和port文件。使用localhost作为启动地址的时候应该修改本地hosts文件,可以使用switchhosts工具。配置proxyTable反向代理地址。

启动

完成这些后顺手写了几个组件和页面,给团队小伙伴做个参考,非常喜欢写前端代码,比后端的好玩哈哈。

3. 知识整理

这里主要整理下Spring中Bean的生命周期吧,以前面试总被问道,步骤很多容易记不住,没有实际使用也感受不到这个的重要性,通过这次实际使用意识到Bean生命周期的重要性,重新在这里整理下。

这里其实可以将Bean的创建大概分成三个阶段,首先是获取Bean,然后是创建Bean,最后是销毁Bean。因此这里我们按照三个阶段介绍Bean的生命周期。

以下内容转自公众号:石杉的架构笔记

3.1 获取Bean


获取Bean的流程图

这里的流程图的入口在AbstractBeanFactory类的doGetBean方法。主要流程就是

1、先处理Bean 的名称,因为如果以“&”开头的Bean名称表示获取的是对应的FactoryBean对象;

2、从缓存中获取单例Bean,有则进一步判断这个Bean是不是在创建中,如果是的就等待创建完毕,否则直接返回这个Bean对象

3、如果不存在单例Bean缓存,则先进行循环依赖的解析

4、解析完毕之后先获取父类BeanFactory,获取到了则调用父类的getBean方法,不存在则先合并然后创建Bean

3.2 创建Bean

3.2.1 创建Bean之前

在真正创建Bean之前逻辑

这个流程图对应的代码在AbstractAutowireCapableBeanFactory类的createBean方法中。

1、这里会先获取RootBeanDefinition对象中的Class对象并确保已经关联了要创建的Bean的Class

2、这里会检查3个条件

(1)Bean的属性中的beforeInstantiationResolved字段是否为true,默认是false。

(2)Bean是原生的Bean

(3)Bean的hasInstantiationAwareBeanPostProcessors属性为true,这个属性在Spring准备刷新容器前调用BeanPostProcessors的时候会设置,如果当前Bean实现了InstantiationAwareBeanPostProcessor则这个就会是true。

当三个条件都存在的时候,就会调用实现的InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法,然后获取返回的Bean,如果返回的Bean不是null还会调用实现的BeanPostProcessor接口的postProcessAfterInitialization方法,这里用代码说明:

代码模拟Bean创建方法的调用

3、如果上面3个条件其中一个不满足就不会调用实现的方法。默认这里都不会调用的这些 BeanPostProcessors的实现方法。然后继续执行后面的 doCreateBean方法。

3.2.2 真正的创建Bean,doCreateBean

创建Bean的真正流程

doCreateBean方法逻辑

这个代码的实现还是在AbstractAutowireCapableBeanFactory方法中。流程是

1、先检查instanceWrapper变量是不是null,这里一般是null,除非当前正在创建的Bean在factoryBeanInstanceCache中存在这个是保存还没创建完成的FactoryBean的集合。

2、调用createBeanInstance方法实例化Bean,这个方法在后面会讲解

3、如果当前RootBeanDefinition对象还没有调用过实现了的MergedBeanDefinitionPostProcessor接口的方法,则会进行调用

4、 当满足以下三点

(1)是单例Bean

(2)尝试解析bean之间的循环引用

(3)bean目前正在创建中

则会进一步检查是否实现了SmartInstantiationAwareBeanPostProcessor接口如果实现了则调用是实现的getEarlyBeanReference方法 

5、 调用populateBean方法进行属性填充,这里后面会讲解 

6、 调用initializeBean方法对Bean进行初始化,这里后面会讲解

3.2.3 实例化Bean,createBeanInstance

createBeanInstance流程图

实例化Bean

这里的逻辑稍微有一点复杂,这个流程图已经是简化过后的了。简要根据代码说明一下流程

createBeanInstance流程图

1、先检查Class是否已经关联了,并且对应的修饰符是否是public的

2、如果用户定义了Bean实例化的函数,则调用并返回

3、如果当前Bean实现了 FactoryBean接口则调用对应的 FactoryBean接口的 getObject方法

4、根据getBean时候是否传入构造参数进行处理

4.1 如果没有传入构造参数,则检查是否存在已经缓存的无参构造器,有则使用构造器直接创建,没有就会调用 instantiateBean方法先获取实例化的策略默认是 CglibSubclassingInstantiationStrategy,然后实例化Bean。最后返回

4.2 如果传入了构造参数,则会先检查是否实现了 SmartInstantiationAwareBeanPostProcessor接口,如果实现了会调用 determineCandidateConstructors获取返回的候选构造器。

4.3 检查4个条件是否满足一个

(1)构造器不为null,

(2)从RootBeanDefinition中获取到的关联的注入方式是构造器注入(没有构造参数就是setter注入,有则是构造器注入)

(3)含有构造参数

(4)getBean方法传入构造参数不是空

满足其中一个则会调用返回的候选构造器实例化Bean并返回,如果都不满足,则会根据构造参数选则合适的有参构造器然后实例化Bean并返回

5、如果上面都没有合适的构造器,则直接使用无参构造器创建并返回Bean。

3.2.4 填充Bean,populateBean

填充Bean的流程图

1、检查当前Bean是否实现了 InstantiationAwareBeanPostProcessor的 postProcessAfterInstantiation方法则调用,并结束Bean的填充。

2、将按照类型跟按照名称注入的Bean分开,如果注入的Bean还没有实例化的这里会实例化,然后放到 PropertyValues对象中。

3、如果实现了 InstantiationAwareBeanPostProcessor类的 postProcessProperties则调用这个方法并获取返回值,如果返回值是null,则有可能是实现了过期的 postProcessPropertyValues方法,这里需要进一步调用 postProcessPropertyValues方法

4、进行参数填充

3.2.5 初始化Bean,initializeBean

初始化Bean流程图

初始化Bean

同时这里根据代码跟流程图来说明

1、如果Bean实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware则调用对应实现的方法 。

2、Bean不为null并且bean不是合成的,如果实现了BeanPostProcessor的postProcessBeforeInitialization则会调用实现的postProcessBeforeInitialization方法。在ApplicationContextAwareProcessor类中实现了postProcessBeforeInitialization方法。而这个类会在Spring刷新容器准备beanFactory的时候会加进去,这里就会被调用,而调用里面会检查Bean是不是EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware的实现类。这里就会调用对应的实现方法。代码如下:

初始化Bean流程代码

1、实例化Bean然后,检查是否实现了InitializingBeanafterPropertiesSet方法,如果实现了就会调用

2、Bean不为null并且bean不是合成的,如果实现了BeanPostProcessorpostProcessBeforeInitialization则会调用实现的postProcessAfterInitialization方法。

到此创建Bean 的流程就没了,剩下的就是容器销毁的时候的了

3.3 destory方法跟销毁Bean

Bean在创建完毕之后会检查用户是否指定了 destroyMethodName以及是否实现了 DestructionAwareBeanPostProcessor接口的 requiresDestruction方法,如果指定了会记录下来保存在 DisposableBeanAdapter对象中并保存在bean的 disposableBeans属性中。代码在 AbstractBeanFactory的 registerDisposableBeanIfNecessary中

在销毁Bean的时候最后都会调用AbstractAutowireCapableBeanFactory的destroyBean方法。

这里是创建一个 DisposableBeanAdapter对象,这个对象实现了Runnable接口,在实现的 run方法中会调用实现的 DisposableBean接口的 destroy方法。并且在创建 DisposableBeanAdapter对象的时候会根据传入的bean是否实现了 DisposableBean接口来设置 invokeDisposableBean变量,这个变量表实有没有实现 DisposableBean接口

Bean的销毁

3.4 Bean的生命周期总结


Bean的生命周期总结
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容