您好,登錄后才能下訂單哦!
本篇文章為大家展示了Bean的解析與注冊(cè)是怎樣的,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
我們將一起探討spring對(duì)bean解析,并注冊(cè)到IOC容器的過程。
我們先接著看下面這段代碼:
ClassPathBeanDefinitionScanner類
//類路徑Bean定義掃描器掃描給定包及其子包 protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); //創(chuàng)建一個(gè)集合,存放掃描到的BeanDefinition封裝類 Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); //遍歷掃描所有給定的包路徑 for (String basePackage : basePackages) { //調(diào)用父類ClassPathScanningCandidateComponentProvider的方法 //掃描給定類路徑,獲取符合條件的Bean定義 10 Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //遍歷掃描到的Bean for (BeanDefinition candidate : candidates) { //獲取@Scope注解的值,即獲取Bean的作用域 14 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); //為Bean設(shè)置作用域 candidate.setScope(scopeMetadata.getScopeName()); //為Bean生成名稱 18 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); //如果掃描到的Bean不是Spring的注解Bean,則為Bean設(shè)置默認(rèn)值, //設(shè)置Bean的自動(dòng)依賴注入裝配屬性等 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //如果掃描到的Bean是Spring的注解Bean,則處理其通用的Spring注解 if (candidate instanceof AnnotatedBeanDefinition) { //處理注解Bean中通用的注解,在分析注解Bean定義類讀取器時(shí)已經(jīng)分析過 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //根據(jù)Bean名稱檢查指定的Bean是否需要在容器中注冊(cè),或者在容器中沖突 30 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); //根據(jù)注解中配置的作用域,為Bean應(yīng)用相應(yīng)的代理模式 33 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //向容器注冊(cè)掃描到的Bean 37 registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
上篇文章我們主要分析了第10行的 findCandidateComponents(basePackage)
方法, 該方法主要是從給定的包路徑中掃描符合過濾規(guī)則的Bean,并存入集合beanDefinitions
中。
接下來的步驟可以分為以下幾個(gè)方面:
遍歷bean集合
獲取@Scope注解的值,即獲取Bean的作用域
為Bean生成名稱
給Bean的一些屬性設(shè)置默認(rèn)值
檢查Bean是否已在IOC容器中注冊(cè)
根據(jù)Bean的作用域,生成相應(yīng)的代理模式
把Bean放入IOC容器中
首先來看下 獲取Bean作用域的過程,主要是上面第14行ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
這段代碼,我們繼續(xù)跟蹤進(jìn)去:
AnnotationScopeMetadataResolver類
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) { //默認(rèn)是singleton ScopeMetadata metadata = new ScopeMetadata(); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition; //獲取@Scope注解的值 AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor( annDef.getMetadata(), this.scopeAnnotationType); //將獲取到的@Scope注解的值設(shè)置到要返回的對(duì)象中 if (attributes != null) { metadata.setScopeName(attributes.getString("value")); //獲取@Scope注解中的proxyMode屬性值,在創(chuàng)建代理對(duì)象時(shí)會(huì)用到 ScopedProxyMode proxyMode = attributes.getEnum("proxyMode"); //如果@Scope的proxyMode屬性為DEFAULT或者NO if (proxyMode == ScopedProxyMode.DEFAULT) { //設(shè)置proxyMode為NO proxyMode = this.defaultProxyMode; } //為返回的元數(shù)據(jù)設(shè)置proxyMode metadata.setScopedProxyMode(proxyMode); } } //返回解析的作用域元信息對(duì)象 return metadata; }
Bean的作用域是通過@Scope
注解實(shí)現(xiàn)的,我們先來看下@Scope
注解的屬性:
可以看到@Scope
注解有三個(gè)屬性,
value
屬性就是我們常用的用來設(shè)置Bean的單例/多例
proxyMode
是用來設(shè)置代理方式的
關(guān)于@Scope
注解原理的詳細(xì)分析,請(qǐng)看這篇文章<>,這里就不詳細(xì)講了。
這里的AnnotationAttributes
是注解屬性key-value的封裝類,繼承了LinkedHashMap
,所以也是key-value形式的數(shù)據(jù)結(jié)構(gòu)。 通過debug看一下,這個(gè)變量拿到的值:
可以看到獲取到了3個(gè)屬性的值,其中value = prototype
就是該Bean的作用域,這里我設(shè)置的是多例,然后再把獲取到的注解屬性值賦值給ScopeMetadata
。
再回到上面ClassPathBeanDefinitionScanner類的doScan()方法中的第18行, 把我們獲取到的Bean的作用域賦值給Bean。
然后為Bean生成名字,代碼String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
跟蹤進(jìn)去,在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的名稱,如果注解中沒有設(shè)置,那么生成一個(gè)默認(rèn)的Bean的名稱,通過ClassUtils.getShortName(beanClassName)
生成一個(gè)類名的小寫開頭駝峰的名字,如studentController
。
主要是doScan()中的如下兩個(gè)方法:
//如果掃描到的Bean不是Spring的注解Bean,則為Bean設(shè)置默認(rèn)值, //設(shè)置Bean的自動(dòng)依賴注入裝配屬性等 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //如果掃描到的Bean是Spring的注解Bean,則處理其通用的Spring注解 if (candidate instanceof AnnotatedBeanDefinition) { //處理注解Bean中通用的注解,在分析注解Bean定義類讀取器時(shí)已經(jīng)分析過 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); }
首先看postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName)
:
跟蹤進(jìn)去會(huì)到如下方法:
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); }
這里應(yīng)該很清晰了,給bean設(shè)置一些默認(rèn)值,BeanDefinitionDefaults
是一個(gè)Bean屬性默認(rèn)值的封裝類,從該類獲取各個(gè)屬性的默認(rèn)值,賦值給bean。
接著我們看AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)
方法。
跟蹤進(jìn)去:
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); //如果Bean定義中有@Lazy注解,則將該Bean預(yù)實(shí)例化屬性設(shè)置為@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設(shè)置為autowiring自動(dòng)依賴注入裝配的首選對(duì)象 if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } //如果Bean定義中有@ DependsOn注解,則為該Bean設(shè)置所依賴的Bean名稱, //容器將確保在實(shí)例化該Bean之前首先實(shí)例化所依賴的Bean AnnotationAttributes 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
。
注釋很清晰,這里就不贅言了。
跟蹤doScan()中的第30行if (checkCandidate(beanName, candidate))
方法:
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { //是否包含beanName了 if (!this.registry.containsBeanDefinition(beanName)) { return true; } //如果容器中已經(jīng)存在同名bean //獲取容器中已存在的bean BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } //新bean舊bean進(jìn)行比較 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() + "]"); }
可以看到,其實(shí)是通過調(diào)用IOC容器的containsBeanDefinition(beanName)
方法,來判斷該beanName是否已存在,而IOC容器實(shí)際上是一個(gè)map
,這里底層其實(shí)就是通過調(diào)用map.containsKey(key)
來實(shí)現(xiàn)的。
跟蹤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,則不應(yīng)用代理模式 if (scopedProxyMode.equals(ScopedProxyMode.NO)) { return definition; } //獲取配置的@Scope注解的proxyMode屬性值,如果為TARGET_CLASS //則返回true,如果為INTERFACES,則返回false boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); //為注冊(cè)的Bean創(chuàng)建相應(yīng)模式的代理對(duì)象 return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); }
這里就用到第二步中我門獲取到的@Scope注解的proxyMode
屬性,然后為bean設(shè)置代理模式。
跟蹤doScan()中的第37行registerBeanDefinition(definitionHolder, this.registry);
方法
//將解析的BeanDefinitionHold注冊(cè)到容器中 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //獲取解析的BeanDefinition的名稱 String beanName = definitionHolder.getBeanName(); //向IOC容器注冊(cè)BeanDefinition 第9行 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //如果解析的BeanDefinition有別名,向容器為其注冊(cè)別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
直接看第9行的代碼,繼續(xù)跟蹤進(jìn)去:
//向IOC容器注冊(cè)解析的BeanDefiniton @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 校驗(yàn) beanName 與 beanDefinition 非空 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //校驗(yàn)解析的BeanDefiniton if (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 的 BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 如果已經(jīng)存在 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_INFRASTRUCTURE if (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 { // 檢測(cè)創(chuàng)建 Bean 階段是否已經(jīng)開啟,如果開啟了則需要對(duì) beanDefinitionMap 進(jìn)行并發(fā)控制 if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) //注冊(cè)的過程中需要線程同步,以保證數(shù)據(jù)的一致性(因?yàn)橛衟ut、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 移除 beanName if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } //檢查是否有同名的BeanDefinition已經(jīng)在IOC容器中注冊(cè) 88 if (oldBeanDefinition != null || containsSingleton(beanName)) { //更新beanDefinitionNames 和 manualSingletonNames resetBeanDefinition(beanName); } }
這里就是向IOC容器中注冊(cè)bean的核心代碼,這段代碼很長(zhǎng),我們分開來看,主要分為幾個(gè)步驟:
beanName
和beanDefinition
的合法性校驗(yàn)
根據(jù)beanName
從IOC容器中判斷是否已經(jīng)注冊(cè)過
根據(jù)isAllowBeanDefinitionOverriding
變量來判斷是否覆蓋
如果存在根據(jù)覆蓋規(guī)則,執(zhí)行覆蓋或者拋出異常
如果不存在,則put到IOC容器beanDefinitionMap
中
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
到這里,注冊(cè)bean到IOC容器的過程就基本結(jié)束了,實(shí)際上IOC注冊(cè)不是什么神秘的東西,說白了就是把beanName
和bean
存入map集合中
此時(shí)我們?cè)俜祷乜?strong>第七步的代碼BeanDefinitionReaderUtils
類的registerBeanDefinition()
方法,可以看到 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
我們已經(jīng)分析完了,剩下的就是把bean的別名也注冊(cè)進(jìn)去就大功告成了。
//將解析的BeanDefinitionHold注冊(cè)到容器中 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //獲取解析的BeanDefinition的名稱 String beanName = definitionHolder.getBeanName(); //向IOC容器注冊(cè)BeanDefinition registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //如果解析的BeanDefinition有別名,向容器為其注冊(cè)別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
IoC容器其實(shí)就是DefaultListableBeanFactory
,它里面有一個(gè)map
類型的beanDefinitionMap
變量,來存儲(chǔ)注冊(cè)的bean
IoC容器初始化過程:
1、資源定位
掃描包路徑下.class
文件,將資源轉(zhuǎn)為Resource
2、資源加載
通過ASM框架獲取class元數(shù)據(jù),封裝到BeanDefinition
3、資源解析
獲取bean上注解的屬性值。如@Scope
4、生成Bean
生成beanName
,設(shè)置Bean默認(rèn)值(懶加載、初始化方法等)、代理模式
5、注冊(cè)Bean
把BeanDefinition
放入IoC容器DefaultListableBeanFactory
最后思考幾個(gè)小疑問:
beanDefinitionMap
是ConcurrentHashMap
類型的,應(yīng)該是線程安全的,但是為什么在代碼64行的地方,還要加sync鎖呢?
之前已經(jīng)檢查過容器中是否有重名bean了,為什么在88行還要再檢查一次呢?
上述內(nèi)容就是Bean的解析與注冊(cè)是怎樣的,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。