日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Bean的解析与注册

發布時間:2025/3/15 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Bean的解析与注册 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文探討spring對bean解析,并注冊到IOC容器的過程

一、ClassPathBeanDefinitionScanner類(遍歷bean集合)

//類路徑Bean定義掃描器掃描給定包及其子包protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");//創建一個集合,存放掃描到的BeanDefinition封裝類Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();//遍歷掃描所有給定的包路徑for (String basePackage : basePackages) {//調用父類ClassPathScanningCandidateComponentProvider的方法//掃描給定類路徑,獲取符合條件的Bean定義 10 Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//遍歷掃描到的Beanfor (BeanDefinition candidate : candidates) {//獲取@Scope注解的值,即獲取Bean的作用域 14 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);//為Bean設置作用域candidate.setScope(scopeMetadata.getScopeName());//為Bean生成名稱 18 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//如果掃描到的Bean不是Spring的注解Bean,則為Bean設置默認值,//設置Bean的自動依賴注入裝配屬性等if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}//如果掃描到的Bean是Spring的注解Bean,則處理其通用的Spring注解if (candidate instanceof AnnotatedBeanDefinition) {//處理注解Bean中通用的注解,在分析注解Bean定義類讀取器時已經分析過AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}//根據Bean名稱檢查指定的Bean是否需要在容器中注冊,或者在容器中沖突 30 if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//根據注解中配置的作用域,為Bean應用相應的代理模式 33 definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);//向容器注冊掃描到的Bean 37 registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}

上次主要分析了第10行findCandidateComponents(basePackage)方法, 該方法主要是從給定的包路徑中掃描符合過濾規則的Bean,并存入集合beanDefinitions中。
接下來的步驟可以分為以下幾個方面:

1.遍歷bean集合2.獲取@Scope注解的值,即獲取Bean的作用域3.為Bean生成名稱4.給Bean的一些屬性設置默認值5.檢查Bean是否已在IOC容器中注冊6.根據Bean的作用域,生成相應的代理模式7.把Bean放入IOC容器中

二、獲取@Scope注解的值,即獲取Bean的作用域

首先來看下 獲取Bean作用域的過程,主要是上面第14行ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
這段代碼,我們繼續跟蹤進去:
AnnotationScopeMetadataResolver類:

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {//默認是singletonScopeMetadata metadata = new ScopeMetadata();if (definition instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;//獲取@Scope注解的值AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);//將獲取到的@Scope注解的值設置到要返回的對象中if (attributes != null) {metadata.setScopeName(attributes.getString("value"));//獲取@Scope注解中的proxyMode屬性值,在創建代理對象時會用到ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");//如果@Scope的proxyMode屬性為DEFAULT或者NOif (proxyMode == ScopedProxyMode.DEFAULT) {//設置proxyMode為NOproxyMode = this.defaultProxyMode;}//為返回的元數據設置proxyModemetadata.setScopedProxyMode(proxyMode);}}//返回解析的作用域元信息對象return metadata;}

Bean的作用域是通過@Scope注解實現的,我們先來看下@Scope注解的屬性:

可以看到@Scope注解有三個屬性,

value 屬性就是我們常用的用來設置Bean的單例/多例
scopeName 作用域名稱,如singleton、request
proxyMode 是用來設置代理方式的

這里的AnnotationAttributes是注解屬性key-value的封裝類,繼承了LinkedHashMap,所以也是key-value形式的數據結構。

三、為Bean生成名稱

回到上面ClassPathBeanDefinitionScanner類的doScan()方法中的第18行, 把我們獲取到的Bean的作用域賦值給Bean。
然后為Bean生成名字,代碼String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
跟蹤進去,在AnnotationBeanNameGenerator類中:

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {if (definition instanceof AnnotatedBeanDefinition) {String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);if (StringUtils.hasText(beanName)) {// Explicit bean name found.return beanName;}}// Fallback: generate a unique default bean name.return buildDefaultBeanName(definition, registry);}

這段代碼很好理解,先從注解中獲取Bean的名稱,如果注解中沒有設置,那么生成一個默認的Bean的名稱,通過ClassUtils.getShortName(beanClassName)生成一個類名的小寫開頭駝峰的名字,如studentController。

四、給Bean的一些屬性設置默認值

主要是doScan()中的如下兩個方法:

//如果掃描到的Bean不是Spring的注解Bean,則為Bean設置默認值, //設置Bean的自動依賴注入裝配屬性等 if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);} //如果掃描到的Bean是Spring的注解Bean,則處理其通用的Spring注解 if (candidate instanceof AnnotatedBeanDefinition) { //處理注解Bean中通用的注解,在分析注解Bean定義類讀取器時已經分析過AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}

首先看postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName):
跟蹤進去會到如下方法:

public void applyDefaults(BeanDefinitionDefaults defaults) {//懶加載setLazyInit(defaults.isLazyInit());//加載模式setAutowireMode(defaults.getAutowireMode());//依賴檢查setDependencyCheck(defaults.getDependencyCheck());//bean初始化方法setInitMethodName(defaults.getInitMethodName());setEnforceInitMethod(false);//bean銷毀方法setDestroyMethodName(defaults.getDestroyMethodName());setEnforceDestroyMethod(false);}

這里應該很清晰了,給bean設置一些默認值,BeanDefinitionDefaults是一個Bean屬性默認值的封裝類,從該類獲取各個屬性的默認值,賦值給bean。
接著我們看AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)方法。
跟蹤進去:

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);//如果Bean定義中有@Lazy注解,則將該Bean預實例化屬性設置為@lazy注解的值if (lazy != null) {abd.setLazyInit(lazy.getBoolean("value"));}else if (abd.getMetadata() != metadata) {lazy = attributesFor(abd.getMetadata(), Lazy.class);if (lazy != null) {abd.setLazyInit(lazy.getBoolean("value"));}}//如果Bean定義中有@Primary注解,則為該Bean設置為autowiring自動依賴注入裝配的首選對象if (metadata.isAnnotated(Primary.class.getName())) {abd.setPrimary(true);}//如果Bean定義中有@DependsOn注解,則為該Bean設置所依賴的Bean名稱,//容器將確保在實例化該Bean之前首先實例化所依賴的BeanAnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);if (dependsOn != null) {abd.setDependsOn(dependsOn.getStringArray("value"));}if (abd instanceof AbstractBeanDefinition) {AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;AnnotationAttributes role = attributesFor(metadata, Role.class);if (role != null) {absBd.setRole(role.getNumber("value").intValue());}AnnotationAttributes description = attributesFor(metadata, Description.class);if (description != null) {absBd.setDescription(description.getString("value"));}}}

這里主要是處理bean上一些常用的注解,如@Lazy、@Primary、@DependsOn。注釋很清晰,這里就不贅言了。

五、檢查Bean是否已在IOC容器中注冊

跟蹤doScan()中的第30行if (checkCandidate(beanName, candidate))方法:

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {//是否包含beanName了if (!this.registry.containsBeanDefinition(beanName)) {return true;}//如果容器中已經存在同名bean//獲取容器中已存在的beanBeanDefinition existingDef = this.registry.getBeanDefinition(beanName);BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();if (originatingDef != null) {existingDef = originatingDef;}//新bean舊bean進行比較if (isCompatible(beanDefinition, existingDef)) {return false;}throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");}

可以看到,其實是通過調用IOC容器的containsBeanDefinition(beanName)方法,來判斷該beanName是否已存在,而IOC容器實際上是一個map,這里底層其實就是通過調用map.containsKey(key)來實現的。

六、為Bean應用相應的代理模式

跟蹤doScan()中的definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);方法

static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {//獲取注解Bean定義類中@Scope注解的proxyMode屬性值ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();//如果配置的@Scope注解的proxyMode屬性值為NO,則不應用代理模式if (scopedProxyMode.equals(ScopedProxyMode.NO)) {return definition;}//獲取配置的@Scope注解的proxyMode屬性值,如果為TARGET_CLASS//則返回true,如果為INTERFACES,則返回falseboolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);//為注冊的Bean創建相應模式的代理對象return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);}

這里就用到第二步中獲取到的@Scope注解的proxyMode屬性,然后為bean設置代理模式。

七、注冊Bean到IOC容器中

跟蹤doScan()中的第37行registerBeanDefinition(definitionHolder, this.registry);方法

//將解析的BeanDefinitionHold注冊到容器中public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.//獲取解析的BeanDefinition的名稱String beanName = definitionHolder.getBeanName();//向IOC容器注冊BeanDefinition9行 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.//如果解析的BeanDefinition有別名,向容器為其注冊別名String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}}

直接看第9行的代碼,繼續跟蹤進去:

//向IOC容器注冊解析的BeanDefiniton@Overridepublic void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {// 校驗 beanName 與 beanDefinition 非空Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");//校驗解析的BeanDefinitonif (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition oldBeanDefinition;// 從容器中獲取指定 beanName 的 BeanDefinitionoldBeanDefinition = this.beanDefinitionMap.get(beanName);// 如果已經存在if (oldBeanDefinition != null) {// 如果存在但是不允許覆蓋,拋出異常if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +"': There is already [" + oldBeanDefinition + "] bound.");}// 覆蓋 beanDefinition 大于 被覆蓋的 beanDefinition 的 ROLE ,打印 info 日志else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(oldBeanDefinition)) {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + oldBeanDefinition +"] with [" + beanDefinition + "]");}}else {if (this.logger.isDebugEnabled()) {this.logger.debug("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + oldBeanDefinition +"] with [" + beanDefinition + "]");}}// 允許覆蓋,直接覆蓋原有的 BeanDefinition 到 beanDefinitionMap 中。this.beanDefinitionMap.put(beanName, beanDefinition);}else {// 檢測創建 Bean 階段是否已經開啟,如果開啟了則需要對 beanDefinitionMap 進行并發控制if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)//注冊的過程中需要線程同步,以保證數據的一致性(因為有put、add、remove操作) 64 synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;// 從 manualSingletonNames 移除 beanNameif (this.manualSingletonNames.contains(beanName)) {Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);updatedSingletons.remove(beanName);this.manualSingletonNames = updatedSingletons;}}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);this.manualSingletonNames.remove(beanName);}this.frozenBeanDefinitionNames = null;}//檢查是否有同名的BeanDefinition已經在IOC容器中注冊 88 if (oldBeanDefinition != null || containsSingleton(beanName)) {//更新beanDefinitionNames 和 manualSingletonNamesresetBeanDefinition(beanName);}}

這里就是向IOC容器中注冊bean的核心代碼,這段代碼很長,分開來看,主要分為幾個步驟:

1.beanName和beanDefinition的合法性校驗
2.根據beanName從IOC容器中判斷是否已經注冊過
3.根據isAllowBeanDefinitionOverriding變量來判斷是否覆蓋
4.如果存在根據覆蓋規則,執行覆蓋或者拋出異常
5.如果不存在,則put到IOC容器beanDefinitionMap中

private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

到這里,注冊bean到IOC容器的過程就基本結束了,實際上IOC注冊不是什么神秘的東西,說白了就是把beanName和bean存入map集合中

此時我們再返回看第七步的代碼BeanDefinitionReaderUtils類的registerBeanDefinition()方法,可以看到 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
已經分析完了,剩下的就是把bean的別名也注冊進去就大功告成了。

//將解析的BeanDefinitionHold注冊到容器中public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.//獲取解析的BeanDefinition的名稱String beanName = definitionHolder.getBeanName();//向IOC容器注冊BeanDefinitionregistry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.//如果解析的BeanDefinition有別名,向容器為其注冊別名String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}}

八、總結

IoC容器其實就是DefaultListableBeanFactory,它里面有一個map類型的beanDefinitionMap變量,來存儲注冊的bean
IoC容器初始化過程:

1、資源定位
掃描包路徑下.class文件,將資源轉為Resource
2、資源加載
通過ASM框架獲取class元數據,封裝到BeanDefinition
3、資源解析
獲取bean上注解的屬性值。如@Scope
4、生成Bean
生成beanName,設置Bean默認值(懶加載、初始化方法等)、代理模式

5、注冊Bean
把BeanDefinition放入IoC容器DefaultListableBeanFactory

總結

以上是生活随笔為你收集整理的Bean的解析与注册的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。