父類:java
AttributeAccessor:git
能夠在xml的bean定義裏面加上DTD文件裏面沒有的屬性,如github
<bean id="cbean" class="com.study.spring.samples.CBean" name="leeSamll" age="18"> <constructor-arg type="String" value="cbean01"></constructor-arg> </bean>
BeanMetadataElement :spring
定義bean定義來源於哪裏,在BeanDefinition 裏面的getResourceDescrption裏面獲取併發
子類:app
類圖:maven
請思考:爲何要增長AnnotatedBeanDefinition?用GenericBeanDefinition不能夠嗎?是否是註解方式的Bean定義信息的存放及使用方式與通用的Bean定義方式不同了?ide
GenericBeanDefinition要定義的東西太多了,xml方式的處理和註解方式的處理可能不太同樣了,因此作了擴展加入AnnotatedBeanDefinition,是爲了只定義本身須要的東西,後面直接從AnnotatedBeanDefinition獲取就好了post
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
findCandidateComponents方法說明:this
組件索引方式獲取bean定義:在pom.xml裏面加入spring-context-indexer這個依賴,在編譯的時候就會生成META-INF/spring.components文件(加了註解的類),而後bean定義就能夠從spring.components裏面獲取,而不用在啓動的時候去包下面掃描獲取bean定義,變得很快
scanCandidateComponents方法說明:
MetadataReader說明:
說明:
獲取類的信息,不僅只有反射,ASM也能夠獲取,ASM經過獲取類的字節碼,參觀Class(ClassVisistor)能夠獲取到類上的註解和類對應的信息(類的屬性、類的方法等),用到的ASM組件有ClassReader和ClassVisistor
咱們本身實現是如何作的:
1)Class.forname("className")加載類得到Class對象
2)反射獲取註解
3)判斷是否存在組件,存在爲其建立BeanDefinition
4)看指定了名字沒,若是沒有,應用名字生成策略生成一個名字
5)註冊BeanDefinition
spring解析註解利用的是ASM,ASM是一個低層次的字節碼操做庫
由於加載類都要放到內存裏面,用不到的話內存就會浪費了,用ASM加載類不會進內存裏面。在spring的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String)的方法裏面的這段代碼讀取類的信息、註解信息的
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
* Copyright 2002-2009 the original author or authors. package org.springframework.core.type.classreading; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; /** * Simple facade for accessing class metadata, * as read by an ASM {@link org.springframework.asm.ClassReader}. * * @author Juergen Hoeller * @since 2.5 */ public interface MetadataReader { /** * Return the resource reference for the class file. */ Resource getResource(); /** * Read basic class metadata for the underlying class. */ ClassMetadata getClassMetadata(); /** * Read full annotation metadata for the underlying class, * including metadata for annotated methods. */ AnnotationMetadata getAnnotationMetadata(); }
在spring的org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String)的方法裏面的這段代碼判斷和建立BeanDefinition的
if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + resource); } } }
判斷是不是候選組件的方法:
/** * Determine whether the given class does not match any exclude filter * and does match at least one include filter. * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
能夠,示例以下:
package com.study.leesamll.spring.ext; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.stereotype.Component; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface MyComponetAnno { String value() default ""; }
使用:
package com.study.leesamll.spring.service; import java.util.Locale; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import com.study.leesmall.spring.ext.MyComponetAnno; //@Componet //@Service @MyComponetAnno public class Abean { @Autowired private ApplicationContext applicationContext; public Abean() { System.out.println("-----------------Abean 被實例化了。。。。。。。。。"); } public void doSomething() { System.out.println(this + " do something .....mike.love=" + this.applicationContext.getEnvironment().getProperty("mike.love")); System.out .println("-----------mike.name=" + this.applicationContext.getMessage("mike.name", null, Locale.CHINA)); } }
示例:
package com.study.leesmall.spring.ext; import java.io.IOException; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import com.study.leesmall.spring.service.Abean; public class MyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // 使用metadataReader中的類信息、註解信息來進行你的過濾判斷邏輯 return metadataReader.getClassMetadata().getClassName().equals(Abean.class.getName()); } }
TypeFilter的子類:
請思考:像@Controller 註解,它和@Service、@Component 註解有不一樣的意圖,這種不一樣的意圖將會在哪裏實現?若是咱們本身也有相似的須要自定義組件註解,是不就能夠模仿@Controller。猜測 spring是如何作到靈活擴展這個的?
在前面咱們已經拿到BeanDefinition了,下面就是註冊BeanDefinition了
1.1 搞清楚BeanFactory中BeanDefinition是如何存儲的?
1.2 搞清楚註冊的過程是怎樣的。
2.1 思考:咱們原來是如何來存儲beanName,BeanDefinition的?
用併發的Map來存,beanName做爲鍵,BeanDefinition做爲值
2.2 思考:咱們註冊BeanDefinition的處理邏輯是怎樣的?
首先判斷beanName是否是合法的,若是是合法的再放到Map裏面
入口:DefaultListableBeanFactory.registerBeanDefinition(String beanName,BeanDefinitionbeanDefinition)
仍是先拿到調用棧來分析:
處理BeanDefinition的過程:
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
說明:Spring裏面默認的bean的名字的生成策略是拿類的名稱來當bean的名稱
看一下AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);方法裏面是怎麼處理註解BeanDefinition的:
下面就來看具體的註冊bean定義邏輯:
PS:
1.Maven怎麼加依賴時怎麼同時下載源碼?
Eclipse-window-preference-maven-勾選download artifact sources
下載慢的話把maven的setting.xml配置文件的鏡像地址換成阿里的更快
完整代碼獲取地址:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-source-study