1.Springä¹FactoryBean
2.6. Spring源码篇之FactoryBean
3.Spring源码系列-BeanPostProcessor与BeanFactoryPostProcessor
4.springbean初始化和实例化?
5.Spring Cloud OpenFeign源码FeignClientFactoryBean原理
6.从源码层面带你实现一个自动注入注解
Springä¹FactoryBean
ä¸è¬æ åµä¸ï¼Spring éè¿åå°æºå¶å©ç¨bean ç classå±æ§æå®å®ç°ç±»æ¥å®ä¾åbeanãå¨æäºæ åµä¸ï¼å®ä¾å bean çè¿ç¨ä¸å«å¤æï¼å¦ææç §ä¼ ç»çæ¹å¼ï¼åéè¦å¨ <bean> ä¸æä¾å¤§éçé 置信æ¯ï¼é ç½®ççµæ´»åº¦åéï¼è¿æ¶åéç¨ç¼ç çæ¹å¼å¯è½ä¼å¾å°ä¸ä¸ªç®åçæ¹æ³ãSpringæä¾äºä¸ä¸ªorg.springframework.bean.FactoryBeançå·¥åæ¥å£ï¼ç¨æ·å¯ä»¥éè¿å®ç°è¯¥æ¥å£å®å¶å®ä¾åçbeanãFactoryBeanæ¥å£å¯¹äºSpringæ¡æ¶æ¥è¯´å ç¨éè¦çå°ä½ï¼Springæ¬èº«å°±æä¾äºç¹å«å¤çFactoryBeançå®ç°ãå®ä»¬éèäºå®ä¾åå¤æbeançç»èï¼ç»ä¸å±åºç¨å¸¦æ¥äºä¾¿å©ãFactoryBeançæºç å¦ä¸ï¼
å¨è¯¥æ¥å£ä¸å®ä¹äºä¸ä¸ªæ¹æ³ï¼
å½é ç½®æä»¶ä¸ <bean> çclasså±æ§é ç½®å®ç°çæ¯FactoryBeanæ¶ï¼éè¿getBean()æ¹æ³è¿åçä¸æ¯ FactoryBean æ¬èº«ï¼èæ¯ FactoryBean ç getObject() æ¹æ³è¿åç对象ã
ä¾å¦ä½¿ç¨ä¼ ç»çæ¹å¼é ç½®ä¸é¢çStudentç <bean> çæ¶ï¼Studentçæ¯ä¸ä¸ªå±æ§é½ä¼å¯¹åºä¸ä¸ª <property> å ç´ çæ ç¾ã
å¦æ使ç¨FactoryBeançæ¹å¼å®ç°å°±æ´çµæ´»ä¸äºï¼éè¿ä¸é¢çéå·åé符çæ¹å¼ä¸æ¬¡æ§å°ä¸ºStudentçææå±æ§æå®é ç½®å¼ï¼
æäºè¿ä¸ä¸ªStudentFactoryBeanåï¼å°±å¯ä»¥å¨é ç½®æ件ä¸ä½¿ç¨ä¸é¢çè¿ç§æ¹æ³é ç½®StudentBeanäºã
å½è°ç¨ getBean("student") æ¶ï¼Springéè¿åå°æºå¶åç° StudentFactoryBean#getObject() æ¹æ³çè¿åãå¦æå¸æè·å StudentFactoryBean çå®ä¾ï¼åéè¦ä½¿ç¨ getBean(beanName) æ¹æ³æ¶å¨beanNameåæ¾ç¤ºçå ä¸ "&" åç¼ï¼ä¾å¦ getBean("&student") ã
6. Spring源码篇之FactoryBean
FactoryBean是源码Spring提供的一个功能强大的小型工厂,用于灵活创建所需Bean。实例在框架与Spring整合时,源码尤其是实例Mybatis-plus中,通过注解可以自动生成Spring Bean,源码而FactoryBean的实例phpvod系统源码怎样安装功能正是实现批量动态生成Bean。下面详细介绍FactoryBean的源码源码解析。
首先,实例我们来看看如何判断一个对象是源码否为FactoryBean。在Spring的实例实例化过程中,如果类实现了FactoryBean接口,源码则会被识别为FactoryBean。实例而获取FactoryBean时,源码通常在Bean名称前加上"&"符号。实例
接下来,源码我们深入分析FactoryBean的接口。
FactoryBean接口定义了如何创建Bean,包含两个主要方法:getObject和isInstance。getObject用于返回创建的Bean实例,isInstance用于判断一个对象是否由FactoryBean创建。
SmartFactoryBean是FactoryBean的子接口,它提供了额外的特性,允许决定是否提前实例化对象。
在实际使用中,FactoryBean的实例化过程较为关键。如果不希望立即实例化某个非懒加载单例Bean,则需要确保它未被识别为FactoryBean。例如,UserBean的实例化代码在正常情况下不会打印任何输出,表明并未实例化。而通过将UserBean实现为SmartFactoryBean,并使isEagerInit返回true,就能在控制台中观察到UserBean的实例化过程。
获取FactoryBean创建的Bean有多种方式。通过在Bean名称前加"&",可以获取到由getObject方法生成的Bean。此外,若需要获取FactoryBean本身,则可以使用多个"&"符号,Spring会循环遍历,直至获取到实际的Bean。
在Spring实例化完成后,通常会调用getObjectForBeanInstance方法来获取真正的Bean实例。这一过程包括了共享实例(sharedInstance)的引用和Bean名称的处理。最终,通过调用getObject方法,我们能够获取到由FactoryBean生成的实际Bean。
以Mybatis-plus中的MapperFactoryBean为例,说明了如何在实际项目中应用FactoryBean。MapperFactoryBean是Mybatis-plus提供的一个FactoryBean,用于自动注册Mapper接口为Spring Bean。
总结而言,FactoryBean在Spring中扮演着灵活创建和管理Bean的重要角色,尤其在需要动态生成或自定义Bean创建逻辑的场景中。通过理解其源码和使用方法,unbuntu源码安装lnmp开发者可以更高效地整合各类框架与Spring,实现更为灵活和高效的系统构建。
Spring源码系列-BeanPostProcessor与BeanFactoryPostProcessor
在Spring框架中,BeanPostProcessor与BeanFactoryPostProcessor各自承担着不同的职责,它们在IoC容器的工作流程中起着关键作用。
BeanFactoryPostProcessor作用于BeanDefinition阶段,对容器中Bean的定义进行处理。这个过程发生在BeanFactory初始化时,对BeanDefinition进行修改或增强,提供了一种在不修改源代码的情况下定制Bean的机制。相比之下,BeanPostProcessor则在Bean实例化之后生效,对已经创建的Bean对象进行进一步处理或替换,提供了更晚、更灵活的扩展点。
以制造杯子为例,BeanFactoryPostProcessor相当于在选择材料和形状阶段进行定制,而BeanPostProcessor则在杯子制造完成后,进行诸如加花纹、抛光等深加工。
在Spring框架中,BeanPostProcessor的使用场景较为广泛,尤其在实现AOP(面向切面编程)时,通过使用代理类替换原始Bean,实现如日志记录、事务管理等功能。
此外,容器在启动后,还会进行消息源初始化、广播器初始化及监听器初始化,为Bean实例化做好准备。完成这些准备工作后,容器会调用registerBeanPostProcessors方法注册BeanPostProcessor,对已创建的Bean进行进一步处理。同时,初始化消息源、广播器和监听器,为后续事件处理做好基础。
总结,BeanFactoryPostProcessor与BeanPostProcessor在Spring IoC容器中的作用各有侧重。前者侧重于对BeanDefinition的定制,后者则是在Bean实例化后的进一步加工,两者共同为构建灵活、可扩展的IoC容器提供了强大的支持。
在深入分析Spring框架的源码时,我们发现refresh()方法的实现中包含了对BeanFactoryPostProcessor和BeanPostProcessor的注册与处理。这些处理步骤确保了容器能够在启动时对Bean进行正确的配置和初始化。
文章中通过一个例子展示了如何使用BeanFactoryPostProcessor替换已注册Bean的实现,以及对其源码的分析。通过例子和源码的结合,读者能够更直观地理解这些后置处理器在Spring框架中的应用和工作原理。
springbean初始化和实例化?
spring配置bean实例化有哪些方式
1.实例化bean的三种方法:
(1)构造器
!--体验1--
beanid="personService"class="com.persia.PersonServiceBean"
!--index代表方法的参数序号,由0开始,基本的人脸识别源码系统类型Type可以不声明--
constructor-argindex="0"value="构造注入的name"/
constructor-argindex="1"type="com.persia.IDaoBean"ref="personDao"/
/bean
对应类
publicPersonServiceBean(Stringname,IDaoBeanpersonDao){
this.name=name;
this.personDao=personDao;
}
!--体现2--
beanid="personDao"class="cn.itcast.dao.impl.PersonDaoBean"/
beanid="personServiceBean"class="cn.itcast.service.impl.PersonServiceBean"
lazy-init="true"init-method="init"destroy-method="destory"
!--ref属性对应idpersonDao值name属性对应接口的getter方法名称--
propertyname="personDao"ref="personDao"/
!--体验3--
!--注入属性值--
propertyname="name"value=""/
!--Set的注入--
propertyname="sets"
set
valuesets:第一个值/value
valuesets:第二个值/value
valuesets:第三个值/value
/set
/property
!--List的注入--
propertyname="lists"
list
valuelists:第一个值/value
valuelists:第二个值/value
valuelists:第三个值/value
/list
/property
!--Properties的注入--
propertyname="properties"
props
propkey="props-key1":第一个值/prop
propkey="props-key2":第二个值/prop
propkey="props-key3":第三个值/prop
/props
/property
!--Map的注入--
propertyname="maps"
map
entrykey="maps-key1"value=":第一个值"/
entrykey="maps-key2"value=":第二个值"/
entrykey="maps-key3"value=":第三个值"/
/map
/property
/bean
(2)静态工厂:
!--静态工厂获取bean--
beanid="personService2"class="com.persia.PersonServiceBeanFactory"factory-method="createInstance"/
对应类
publicstaticPersonServiceBeancreateInstance(){
returnnewPersonServiceBean();
}
(3)实例工厂:
没有静态方法,因此配置时,先实例化工厂,在实例化需要的bean。
!--实例工厂获取bean,先实例化工厂再实例化bean--
beanid="fac"class="com.persia.PersonServiceBeanInsFactory"/
beanid="personService3"factory-bean="fac"factory-method="createInstance"/
对应类
publicPersonServiceBeancreateInstance(){
returnnewPersonServiceBean();
}
2.bean的作用域
默认情况为单例方式:scope=”singleton”
singleton
单实例作用域,这是Spring容器默认的作用域,使用singleton作用域生成的是单实例,在整个Bean容器中仅保留一个实例对象供所有调用者共享引用。单例模式对于那些无会话状态的Bean(如辅助工具类、DAO组件、业务逻辑组件等)是最理想的选择。
prototype
原型模式,这是多实例作用域,针对每次不同的请求,Bean容器均会生成一个全新的Bean实例以供调用者使用。prototype作用域非常适用于那些需要保持会话状态的Bean实例,有一点值得注意的就是,Spring不能对一个prototype
Bean的整个生命周期负责,容器在初始化、装配好一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。因此,客户端要负责prototype实例的生命周期管理。
request
针对每次HTTP请求,Spring容器会根据Bean的定义创建一个全新的Bean实例,
且该Bean实例仅在当前HTTPrequest内有效,因此可以根据需要放心地更改所建实例的内部状态,
而其他请求中根据Bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。
当处理请求结束,request作用域的Bean实例将被销毁。该作用域仅在基于web的Spring
ApplicationContext情形下有效。
session
针对某个HTTP
Session,Spring容器会根据Bean定义创建一个全新的Bean实例,且该Bean实例仅在当前HTTPSession内有效。
与request作用域一样,我们可以根据需要放心地更改所创建实例的内部状态,而别的HTTPSession中根据Bean定义创建的实例,
将不会看到这些特定于某个HTTPSession的状态变化。当HTTPSession最终被废弃的时候,在该HTTP
Session作用域内的Bean实例也会被废弃掉。该作用域仅在基于Web的SpringApplicationContext情形下有效。
globalsession
global
session作用域类似于标准的HTTP
Session作用域,不过它仅仅在基于portlet的Web应用中才有意义。portlet规范定义了全局Session的概念,它被所有构成某个portlet
Web应用的各种不同的portlet所共享。在globalsession作用域中定义的Bean被限定于全局portlet
Session的生命周期范围内。如果我们是在编写一个标准的基于Servlet的Web应用,并且定义了一个或多个具有global
session作用域的Bean,系统会使用标准的HTTPSession作用域,并且不会引起任何错误。该作用域仅在基于Web的Spring
ApplicationContext情形下有效。
3.bean的ssm租车系统源码生命周期
(1)什么时候实例化?
对于单例的形式,在容器实例化的时候对bean进行实例化的。
ApplicationContextctx=newClassPathXmlApplicationContext(newString[]{ "applicationContext.xml"});
单实例可以通过lazy-init=”true”,在getBean时进行实例化。
在beans里面default-lazy-init=”true”对所有bean进行延迟处理。
对于prototype,则是在getBean的时候被实例化的。
(2)在bean被实例化之后执行资源操作等方法:
Init-method=””
(3)在bean销毁之前执行的方法:
Destroy-method=””
什么时候被销毁?随着spring容器被关闭时被销毁。
调用spring容器的close方法来正常关闭。以前是随着应用程序执行完而关闭。
在Spring装载配置文件后,Spring工厂实例化完成,开始处理
(1)使用默认构造方法或指定构造参数进行Bean实例化。
(2)根据property标签的配置调用Bean实例中的相关set方法完成属性的赋值。
(3)如果Bean实现了BeanNameAware接口,则调用setBeanName()方法传入当前Bean的ID。
(4)如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory()方法传入当前工厂实例的引用。
(5)如果Bean实现了ApplicationContextAware接口,则调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
(6)如果有BeanPostProcessor与当前Bean关联,则与之关联的对象的postProcess-BeforeInitialzation()方法将被调用。
(7)如果在配置文件中配置Bean时设置了init-method属性,则调用该属性指定的初始化方法。
(8)如果有BeanPostProcessor与当前Bean关联,则与之关联的对象的postProcess-AfterInitialzation()方法将被调用。
(9)Bean实例化完成,处于待用状态,可以被正常使用了。
()当Spring容器关闭时,如果Bean实现了DisposableBean接口,则destroy()方法将被调用。
()如果在配置文件中配置Bean时设置了destroy-method属性,则调用该属性指定的方法进行销毁前的一些处理。
()Bean实例被正常销毁。
Spring系列(一)SpringMVCbean解析、注册、实例化流程源码剖析
最近在使用SpringMVC过程中遇到了一些问题,网上搜索不少帖子后虽然找到了答案和解决方法,但这些答案大部分都只是给了结论,并没有说明具体原因,感觉总是有点不太满意。
更重要的是这些所谓的结论大多是抄来抄去,基本源自一家,真实性也有待考证。
那作为程序员怎么能知其所以然呢?
此处请大家内心默读三遍。
用过Spring的人都知道其核心就是IOC和AOP,因此要想了解Spring机制就得先从这两点入手,本文主要通过对IOC部分的机制进行介绍。
在开始阅读之前,先准备好以下实验材料。
IDEA是一个优秀的开发工具,如果还在用Eclipse的建议切换到此工具进行。
IDEA有很多的快捷键,在分析过程中建议大家多用Ctrl+Alt+B快捷键,qgis源码编译linux可以快速定位到实现函数。
Springbean的加载主要分为以下6步:
查看源码第一步是找到程序入口,再以入口为突破口,一步步进行源码跟踪。
JavaWeb应用中的入口就是web.xml。
在web.xml找到ContextLoaderListener,此Listener负责初始化SpringIOC。
contextConfigLocation参数设置了bean定义文件地址。
下面是ContextLoaderListener的官方定义:
翻译过来ContextLoaderListener作用就是负责启动和关闭SpringrootWebApplicationContext。
具体WebApplicationContext是什么?开始看源码。
从源码看出此Listener主要有两个函数,一个负责初始化WebApplicationContext,一个负责销毁。
继续看initWebApplicationContext函数。
在上面的代码中主要有两个功能:
进入CreateWebAPPlicationContext函数
进入determineContextClass函数。
进入configureAndReFreshWebApplicaitonContext函数。
WebApplicationContext有很多实现类。但从上面determineContextClass得知此处wac实际上是XmlWebApplicationContext类,因此进入XmlWebApplication类查看其继承的refresh()方法。
沿方法调用栈一层层看下去。
获取beanFactory。
beanFactory初始化。
加载bean。
读取XML配置文件。
XmlBeanDefinitionReader读取XML文件中的bean定义。
继续查看loadBeanDefinitons函数调用栈,进入到XmlBeanDefinitioReader类的loadBeanDefinitions方法。
最终将XML文件解析成Document文档对象。
上一步完成了XML文件的解析工作,接下来将XML中定义的bean注册到webApplicationContext,继续跟踪函数。
用BeanDefinitionDocumentReader对象来注册bean。
解析XML文档。
循环解析XML文档中的每个元素。
下面是默认命名空间的解析逻辑。
不明白Spring的命名空间的可以网上查一下,其实类似于package,用来区分变量来源,防止变量重名。
这里我们就不一一跟踪,以解析bean元素为例继续展开。
解析bean元素,最后把每个bean解析为一个包含bean所有信息的BeanDefinitionHolder对象。
接下来将解析到的bean注册到webApplicationContext中。接下继续跟踪registerBeanDefinition函数。
跟踪registerBeanDefinition函数,此函数将bean信息保存到到webApplicationContext的beanDefinitionMap变量中,该变量为map类型,保存Spring容器中所有的bean定义。
Spring实例化bean的时机有两个。
一个是容器启动时候,另一个是真正调用的时候。
相信用过Spring的同学们都知道以上概念,但是为什么呢?
继续从源码角度进行分析,回到之前XmlWebApplication的refresh()方法。
可以看到获得beanFactory后调用了finishBeanFactoryInitialization()方法,继续跟踪此方法。
预先实例化单例类逻辑。
获取bean。
doGetBean中处理的逻辑很多,为了减少干扰,下面只显示了创建bean的函数调用栈。
创建bean。
判断哪种动态代理方式实例化bean。
不管哪种方式最终都是通过反射的形式完成了bean的实例化。
我们继续回到doGetBean函数,分析获取bean的逻辑。
上面方法中首先调用getSingleton(beanName)方法来获取单例bean,如果获取到则直接返回该bean。方法调用栈如下:
getSingleton方法先从singletonObjects属性中获取bean对象,如果不为空则返回该对象,否则返回null。
那singletonObjects保存的是什么?什么时候保存的呢?
回到doGetBean()函数继续分析。如果singletonObjects没有该bean的对象,进入到创建bean的逻辑。处理逻辑如下:
下面是判断容器中有没有注册bean的逻辑,此处beanDefinitionMap相信大家都不陌生,在注册bean的流程里已经说过所有的bean信息都会保存到该变量中。
如果该容器中已经注册过bean,继续往下走。先获取该bean的依赖bean,如果镩子依赖bean,则先递归获取相应的依赖bean。
依赖bean创建完成后,接下来就是创建自身bean实例了。
获取bean实例的处理逻辑有三种,即Singleton、Prototype、其它(request、session、globalsession),下面一一说明。
如果bean是单例模式,执行此逻辑。
获取单例bean,如果已经有该bean的对象直接返回。如果没有则创建单例bean对象,并添加到容器的singletonObjectsMap中,以后直接从singletonObjects直接获取bean。
把新生成的单例bean加入到类型为MAP的singletonObjects属性中,这也就是前面singletonObjects()方法中获取单例bean时从此Map中获取的原因。
Prototype是每次获取该bean时候都新建一个bean,因此逻辑比较简单,直接创建一个bean后返回。
从相应scope获取对象实例。
判断scope,获取实例函数逻辑。
在相应scope中设置实例函数逻辑。
以上就是Springbean从无到有的整个逻辑。
从源码角度分析bean的实例化流程到此基本接近尾声了。
回到开头的问题,ContextLoaderListener中初始化的WebApplicationContext到底是什么呢?
通过源码的分析我们知道WebApplicationContext负责了bean的创建、保存、获取。其实也就是我们平时所说的IOC容器,只不过名字表述不同而已。
本文主要是讲解了XML配置文件中bean的解析、注册、实例化。对于其它命名空间的解析还没有讲到,后续的文章中会一一介绍。
希望通过本文让大家在以后使用Spring的过程中有“一切尽在掌控之中”的感觉,而不仅仅是稀里糊涂的使用。
SpringBean的初始化本文基于上一篇文章进行续写
上一篇文章地址:SpringBean实例化及构造器选择
1.BeanPostProcessor
查看源码发现BeanPostProcessor提供了两个初始化前后的方法,新建一个接口并重写该接口的这两个方法
1.新建一个InstantiationAwareBeanPostProcessorSpring方法并实现InstantiationAwareBeanPostProcessor接口
InstantiationAwareBeanPostProcessor实现了BeanPostProcessor,所以此处使用InstantiationAwareBeanPostProcessorSpring也可以调用上述2个接口方法
2.UserService类实现InitializingBean接口,并重写afterPropertiesSet方法
3.利用客户端进行调用
4.运行结果
spring的bean到底在什么时候实例化spring的bean在被依赖的时候实例化;
分为以下几种Bean:
1.如果指定的是convertrService,beanPostProcessor等实例的时候,则会在ApplicationContext初始化的时候就实例化;
2.如果指定的是自定义的Bean,那么会在第一次访问的时候实例化;
[被依赖的时候实例化,更明确的说是第一次访问]
springioc容器之Bean实例化和依赖注入 spring中的bean对象和java对象是有些许差别的,spring中的bean包含了java对象,并且是基于java对象在spring中做了一些列的加工,所以说spring中的bean比java对象更加丰富。在spring中bean的实例化有2个时机:下面从springioc容器初始化的时候,预实例化的bean为线索来追溯bean的实例化和依赖注入过程,这个过程涵盖了getBean方法。在springioc容器初始化的时候,触发了所有预实例化的bean的加载,这里必须是非抽象、单例和非懒加载的bean才符合条件进行预实例化。具体bean的实例化是在getBean方法中。这里通过getSingleton先从缓存中获取bean实例。从缓存中获取很好理解,分别从spring容器的一级缓存singletonObjects、二级缓存earlySingletonObjects和三级缓存singletonFactories中获取bean实例。在初次获取bean的时候,这里的缓存肯定为空的,但是对于存在循环依赖的bean,这里的一级或二级缓存就不是空的。在有循环依赖的bean中,这里一级缓存会存在不为空的情况,这个时候通过singletonFactory.getObject的时候,返回的可能是一个bean实例,也有可能是一个提前进行aop的代理对象(正常情况下aop是发生在bean初始化的时候完成的),对于有循环依赖并且需要进行aop的bean,在这里会进行提前aop代理对象的生成。当缓存中没有找到bean实例的时候:通过singletonFactory.gSpring Cloud OpenFeign源码FeignClientFactoryBean原理
Spring Cloud OpenFeign的FeignClientFactoryBean在实例化过程中,通过FactoryBean接口实现,GetObject方法的关键步骤包括获取FeignContext、配置Feign.Builder、创建HardCodedTarget和调用loadBalance方法。这些步骤涉及自动配置、FeignClientSpecification的使用、Logger和Builder组件的定制以及动态代理的生成。最后,getObject方法返回的是一个接口的代理类,用于执行远程调用。
详细分析:
FeignClientFactoryBean在Spring容器中,通过getObject方法转化为实际的FeignClient实例。首先,它从FeignContext获取相关配置,这个配置在引入OpenFeign依赖时自动注入。接下来,通过getTarget方法,FeignClientFactoryBean配置了Builder组件,如Logger(非Slf4j)、RequestInterceptor、Encoder和Decoder等,同时考虑了用户自定义组件的配置。之后,创建了HardCodedTarget,基于FeignClient接口、注解值和完整URL构建,然后通过loadBalance方法,整合了LoadBalancerFeignClient和HystrixTargeter,进行负载均衡和目标URL定位。
在newInstance方法中,解析了接口方法的注解,生成了MethodHandler,并用FeignInvocationHandler封装,这个InvocationHandler在代理类实例化时被调用,实现了远程调用。最终,通过Proxy.newProxyInstance动态生成了代理类,完成FeignClientFactoryBean的实例化过程。
总的来说,FeignClientFactoryBean实例化是通过一系列配置和代理生成,实现了Spring Cloud OpenFeign的远程调用功能。如果你对源码的深入理解感兴趣,下期文章将继续解析调用源码细节。
从源码层面带你实现一个自动注入注解
首先,需要了解到的是。SpringBean的生命周期在生命周期中。注入bean属性的位置是在以下代码:populateBean位置中
那么我们在项目中使用注解产生一个bean的时候必定会经过以下代码进行一个bean的创建流程
/**省略代码**///开始初始化bean实例对象ObjectexposedObject=bean;try{ //<5>对bean进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性populateBean(beanName,mbd,instanceWrapper);//<6>调用初始化方法exposedObject=initializeBean(beanName,exposedObject,mbd);}catch(Throwableex){ if(exinstanceofBeanCreationException&&beanName.equals(((BeanCreationException)ex).getBeanName())){ throw(BeanCreationException)ex;}else{ thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"Initializationofbeanfailed",ex);}}/**省略代码**/在生命周期中populateBean进行填充bean数据。把其他依赖引入进来
BeanPostProcessor是一个bean创建时候的一个钩子。
以下代码是循环调用实现了BeanPostProcessor子类InstantiationAwareBeanPostProcessor#postProcessProperties方法
Spring在以下代码中有自动注入的拓展点。关键就是实现InstantiationAwareBeanPostProcessor#postProcessProperties
/**省略代码**/for(BeanPostProcessorbp:getBeanPostProcessors()){ if(bpinstanceofInstantiationAwareBeanPostProcessor){ InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor)bp;//对所有需要依赖检查的属性进行后处理PropertyValuespvsToUse=ibp.postProcessProperties(pvs,bw.getWrappedInstance(),beanName);if(pvsToUse==null){ //从bw对象中提取PropertyDescriptor结果集//PropertyDescriptor:可以通过一对存取方法提取一个属性if(filteredPds==null){ filteredPds=filterPropertyDescriptorsForDependencyCheck(bw,mbd.allowCaching);}pvsToUse=ibp.postProcessPropertyValues(pvs,filteredPds,bw.getWrappedInstance(),beanName);if(pvsToUse==null){ return;}}pvs=pvsToUse;}}/**省略代码**/我们展开来讲一下@Autowired的实现是怎么样的吧:
实现类为AutowiredAnnotationBeanPostProcessor.java
从上面可以得知,填充bean的时候。时调用了方法ibp.postProcessPropertyValues()
那么AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues()则会被调用
调用findAutowiringMetadata获取class以及父类带有@Autowired或者@Value的属性或者方法:
/**省略代码**/publicPropertyValuespostProcessProperties(PropertyValuespvs,Objectbean,StringbeanName){ //获取所有可以注入的元数据InjectionMetadatametadata=findAutowiringMetadata(beanName,bean.getClass(),pvs);try{ //注入数据metadata.inject(bean,beanName,pvs);}catch(BeanCreationExceptionex){ throwex;}catch(Throwableex){ thrownewBeanCreationException(beanName,"Injectionofautowireddependenciesfailed",ex);}returnpvs;}privateInjectionMetadatafindAutowiringMetadata(StringbeanName,Class<?>clazz,@NullablePropertyValuespvs){ //缓存名字获取StringcacheKey=(StringUtils.hasLength(beanName)?beanName:clazz.getName());InjectionMetadatametadata=this.injectionMetadataCache.get(cacheKey);//获取是否已经读取过这个class类的InjectionMetadata有的话直接从缓存中获取出去if(InjectionMetadata.needsRefresh(metadata,clazz)){ synchronized(this.injectionMetadataCache){ //双重检查metadata=this.injectionMetadataCache.get(cacheKey);if(InjectionMetadata.needsRefresh(metadata,clazz)){ if(metadata!=null){ metadata.clear(pvs);}//构建自动注入的元数据metadata=buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey,metadata);}}}returnmetadata;}privateInjectionMetadatabuildAutowiringMetadata(finalClass<?>clazz){ if(!AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)){ returnInjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement>elements=newArrayList<>();Class<?>targetClass=clazz;do{ finalList<InjectionMetadata.InjectedElement>currElements=newArrayList<>();//循环targetClass的所有field并执FieldCallback逻辑(函数式编程接口,传入的是一个执行函数)ReflectionUtils.doWithLocalFields(targetClass,field->{ //获得字段上面的Annotation注解MergedAnnotation<?>ann=findAutowiredAnnotation(field);if(ann!=null){ //判断是否为静态属性如果是,则不进行注入if(Modifier.isStatic(field.getModifiers())){ if(logger.isInfoEnabled()){ logger.info("Autowiredannotationisnotsupportedonstaticfields:"+field);}return;}//注解是否为必须依赖项booleanrequired=determineRequiredStatus(ann);currElements.add(newAutowiredFieldElement(field,required));}});//循环targetClass的所有Method并执MethodCallback逻辑(函数式编程接口,传入的是一个执行函数)ReflectionUtils.doWithLocalMethods(targetClass,method->{ MethodbridgedMethod=BridgeMethodResolver.findBridgedMethod(method);if(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method,bridgedMethod)){ return;}MergedAnnotation<?>ann=findAutowiredAnnotation(bridgedMethod);if(ann!=null&&method.equals(ClassUtils.getMostSpecificMethod(method,clazz))){ //判断是否为静态方法如果是,则不进行注入if(Modifier.isStatic(method.getModifiers())){ if(logger.isInfoEnabled()){ logger.info("Autowiredannotationisnotsupportedonstaticmethods:"+method);}return;}//判断静态方法参数是否为0if(method.getParameterCount()==0){ if(logger.isInfoEnabled()){ logger.info("Autowiredannotationshouldonlybeusedonmethodswithparameters:"+method);}}booleanrequired=determineRequiredStatus(ann);PropertyDescriptorpd=BeanUtils.findPropertyForMethod(bridgedMethod,clazz);currElements.add(newAutowiredMethodElement(method,required,pd));}});//数据加到数组最前方父类的的注解都放在靠前的位置elements.addAll(0,currElements);//如果有父类则设置targetClass为父类。如此循环targetClass=targetClass.getSuperclass();}while(targetClass!=null&&targetClass!=Object.class);returnInjectionMetadata.forElements(elements,clazz);}/**省略代码**/真正注入数据的是metadata.inject(bean,beanName,pvs);
调用的是InjectionMetadata#inject方法
publicvoidinject(Objecttarget,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ Collection<InjectedElement>checkedElements=this.checkedElements;//带有注解的方法或者属性列表Collection<InjectedElement>elementsToIterate=(checkedElements!=null?checkedElements:this.injectedElements);if(!elementsToIterate.isEmpty()){ for(InjectedElementelement:elementsToIterate){ element.inject(target,beanName,pvs);}}}循环调用之前加入的带有注解的方法或者属性构建的对象AutowiredFieldElement#inject,AutowiredMethodElement#inject
/***属性上有注解构建的处理对象*/privateclassAutowiredFieldElementextendsInjectionMetadata.InjectedElement{ privatefinalbooleanrequired;privatevolatilebooleancached;@NullableprivatevolatileObjectcachedFieldValue;publicAutowiredFieldElement(Fieldfield,booleanrequired){ super(field,null);this.required=required;}@Overrideprotectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ //获取属性名Fieldfield=(Field)this.member;Objectvalue;//Bean不是单例的话,会重复进入注入的这个操作,if(this.cached){ try{ value=resolvedCachedArgument(beanName,this.cachedFieldValue);}catch(NoSuchBeanDefinitionExceptionex){ //Unexpectedremovaloftargetbeanforcachedargument->re-resolvevalue=resolveFieldValue(field,bean,beanName);}}else{ //首次创建的时候进入该方法value=resolveFieldValue(field,bean,beanName);}if(value!=null){ //属性如果不为public的话,则设置为可访问ReflectionUtils.makeAccessible(field);field.set(bean,value);}}@NullableprivateObjectresolveFieldValue(Fieldfield,Objectbean,@NullableStringbeanName){ //构建DependencyDescriptor对象DependencyDescriptordesc=newDependencyDescriptor(field,this.required);desc.setContainingClass(bean.getClass());//注入bean的数量。有可能字段上是一个ListSet<String>autowiredBeanNames=newLinkedHashSet<>(1);Assert.state(beanFactory!=null,"NoBeanFactoryavailable");//获得beanFactory类型转换类TypeConvertertypeConverter=beanFactory.getTypeConverter();Objectvalue;try{ //查找依赖关系value=beanFactory.resolveDependency(desc,beanName,autowiredBeanNames,typeConverter);}catch(BeansExceptionex){ thrownewUnsatisfiedDependencyException(null,beanName,newInjectionPoint(field),ex);}synchronized(this){ if(!this.cached){ ObjectcachedFieldValue=null;if(value!=null||this.required){ cachedFieldValue=desc;//填入依赖关系registerDependentBeans(beanName,autowiredBeanNames);//判断如果注入依赖是只有一个if(autowiredBeanNames.size()==1){ StringautowiredBeanName=autowiredBeanNames.iterator().next();if(beanFactory.containsBean(autowiredBeanName)&&beanFactory.isTypeMatch(autowiredBeanName,field.getType())){ cachedFieldValue=newShortcutDependencyDescriptor(desc,autowiredBeanName,field.getType());}}}this.cachedFieldValue=cachedFieldValue;this.cached=true;}}returnvalue;}}/***方法上有注解构建的处理对象*/privateclassAutowiredMethodElementextendsInjectionMetadata.InjectedElement{ privatefinalbooleanrequired;privatevolatilebooleancached;@NullableprivatevolatileObject[]cachedMethodArguments;publicAutowiredMethodElement(Methodmethod,booleanrequired,@NullablePropertyDescriptorpd){ super(method,pd);this.required=required;}@Overrideprotectedvoidinject(Objectbean,@NullableStringbeanName,@NullablePropertyValuespvs)throwsThrowable{ //检查属性是不会在之前就已经注入过了。如果主如果则不进行二次覆盖if(checkPropertySkipping(pvs)){ return;}Methodmethod=(Method)this.member;Object[]arguments;if(this.cached){ try{ arguments=resolveCachedArguments(beanName);}catch(NoSuchBeanDefinitionExceptionex){ //Unexpectedremovaloftargetbeanforcachedargument->re-resolvearguments=resolveMethodArguments(method,bean,beanName);}}else{ //首次创建的时候进入该方法arguments=resolveMethodArguments(method,bean,beanName);}if(arguments!=null){ try{ //属性如果不为public的话,则设置为可访问ReflectionUtils.makeAccessible(method);//调用方法并传入参数method.invoke(bean,arguments);}catch(InvocationTargetExceptionex){ throwex.getTargetException();}}}@NullableprivateObject[]resolveCachedArguments(@NullableStringbeanName){ Object[]cachedMethodArguments=this.cachedMethodArguments;if(cachedMethodArguments==null){ returnnull;}Object[]arguments=newObject[cachedMethodArguments.length];for(inti=0;i<arguments.length;i++){ arguments[i]=resolvedCachedArgument(beanName,cachedMethodArguments[i]);}returnarguments;}@NullableprivateObject[]resolveMethodArguments(Methodmethod,Objectbean,@NullableStringbeanName){ //获取方法上有几个参数intargumentCount=method.getParameterCount();Object[]arguments=newObject[argumentCount];DependencyDescriptor[]descriptors=newDependencyDescriptor[argumentCount];Set<String>autowiredBeans=newLinkedHashSet<>(argumentCount);Assert.state(beanFactory!=null,"NoBeanFactoryavailable");TypeConvertertypeConverter=beanFactory.getTypeConverter();for(inti=0;i<arguments.length;i++){ //方法参数,从方法参数中取出i构造MethodParameter对象MethodParametermethodParam=newMethodParameter(method,i);DependencyDescriptorcurrDesc=newDependencyDescriptor(methodParam,this.required);currDesc.setContainingClass(bean.getClass());descriptors[i]=currDesc;try{ //获取方法中i参数的内容Objectarg=beanFactory.resolveDependency(currDesc,beanName,autowiredBeans,typeConverter);if(arg==null&可以,很强,行代码实现Bean的异步初始化,粘过去就能用。
在阅读本文之前,你可能会对 SOFABoot 有所了解,但本文更关注其异步初始化功能,这项特性在 SpringBoot 的基础上增加了更多实用能力。SOFABoot,一款由蚂蚁金服开源的基于 SpringBoot 的框架,旨在增强 SpringBoot 的功能并提供便捷地使用 SOFA 中间件的方式。本文将聚焦于 SOFABoot 如何通过异步初始化功能加速应用启动过程。
异步初始化功能的引入,使得 Bean 的初始化方法在异步线程中执行,从而显著缩短 Spring 上下文加载时间,提升应用启动速度。这一特性是基于 SOFABoot 的 @SofaAsyncInit 注解实现的,该注解允许开发者指定哪些 Bean 的初始化方法可以异步执行。
在深入理解这一功能之前,我们先通过一个简单的 Demo 来直观感受异步初始化的效果。通过使用 SOFABoot 框架,我们可以轻松地将两个 Java 类添加为 Bean,它们各自包含一个初始化方法。在实际应用中,我们可以在 Bean 的初始化阶段进行数据准备或配置拉取等操作。当启用 @SofaAsyncInit 注解时,Spring 上下文加载时间从 1.s 缩短至仅需几秒,大幅提升了应用启动效率。
接下来,我们将学习如何在项目中引入 SOFABoot 并利用 @SofaAsyncInit 注解。首先,需要将 SpringBoot 项目转换为 SOFABoot 项目,通过官方文档指导完成这一操作。在 pom.xml 文件中添加相应的依赖,并配置 spring.application.name 参数。然后,通过在 Bean 上添加 @SofaAsyncInit 注解来指定哪些初始化方法可以异步执行。
此外,我们还可以将 SOFABoot 的关键类和功能封装为一个自定义的 starter,使其在其他项目中开箱即用。这不仅限于异步初始化功能,更展示了如何在现有框架基础上扩展功能,实现更高效的开发流程。
本文不仅介绍了异步初始化功能的实现细节,还探讨了这一特性在实际开发中的应用与优化。它不仅提供了一种加速应用启动的手段,更是一次对 SpringBoot 框架深入理解与源码探索的实践案例。通过学习本文,读者可以掌握如何在项目中引入和利用这一功能,同时提高对 Spring 和相关框架的理解,为后续开发工作提供更强大的支持。
Spring源码--Bean工厂之getBean方法
Bean实例化与管理是Spring框架的核心功能之一,其中getBean方法作为获取Bean实例的主要手段,具有重要意义。接下来,我们将深入探讨getBean方法及其相关实现,以期更好地理解Spring Bean工厂的工作机制。
一、getBean方法
getBean方法是Spring容器对外提供的一种接口,用于根据指定的Bean名称获取对应Bean实例。该方法会根据配置信息和缓存机制,找到并返回所需的Bean。
二、doGetBean方法
doGetBean方法是getBean方法的内部实现,负责处理Bean的查找、创建和返回工作。其流程分为以下几个关键步骤:
1. getSingleton
若Bean是单例且已存在,则直接返回缓存的实例,无需重新创建。
2. createBean
若非单例或未找到缓存实例,将进入创建Bean的流程。此过程涉及实例化、属性填充和初始化三个主要步骤。
2.1 实例化
通过调用对应的构造函数或使用默认构造函数创建Bean实例。
2.2 三级缓存
在实例化后,新创建的Bean会首先存储于缓存中,随后被添加到Bean作用域的缓存中,以备后续使用。
2.3 属性填充
通过依赖注入或属性设置方法填充Bean的属性值,确保其具有所需的功能。
2.4 初始化
执行Bean的初始化方法,实现任何特定的初始化逻辑,如配置文件加载或数据库连接等。
三、流程图
为了更直观地展示getBean方法的执行流程,以下流程图详细展示了从查找至返回Bean实例的全过程,包括缓存操作、实例化、属性填充和初始化等关键步骤。
四、循环依赖示意图
在处理循环依赖时,Spring容器会采取特定策略以避免无限循环。以下示意图展示了两个单例Bean(A和B)之间循环依赖的处理过程,以及Spring如何通过延迟初始化等机制解决这一问题。
本文通过深入剖析getBean方法及其相关实现,旨在帮助开发者更好地理解Spring Bean工厂的工作机制。通过掌握这些关键概念与流程,可以更高效地利用Spring框架构建可维护且高性能的应用程序。