1.從Spring2.0之後的版本中,Spring也引入了基於註解(Annotation)方式的配置,註解(Annotation)是JDK1.5中引入的一個新特性,用於簡化Bean的配置,某些場合能夠取代XML配置文件。開發人員對註解(Annotation)的態度也是蘿蔔青菜各有所愛,我的認爲註解能夠大大簡化配置,提升開發速度,同時也不能徹底取代XML配置方式,XML 方式更加靈活,而且發展的相對成熟,這種配置方式爲大多數 Spring 開發者熟悉;註解方式使用起來很是簡潔,可是尚處於發展階段,XML配置文件和註解(Annotation)能夠相互配合使用。java
應某些人員的要求,本文章就分析Spring對註解(Annotation)的解析過程,若是你對註解還不熟悉,請參考:http://blog.csdn.net/chjttony/archive/2010/11/22/6026079.aspx中8之後的對於註解的簡單介紹和前一篇博客中轉載的對Spring註解基本知識介紹:http://blog.csdn.net/chjttony/archive/2011/03/29/6286144.aspx.web
Spring IoC容器對於類級別的註解和類內部的註解分如下兩種處理策略:編程
(1).類級別的註解:如@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named註解,都是添加在類上面的類級別註解,Spring容器根據註解的過濾規則掃描讀取註解Bean定義類,並將其註冊到Spring IoC容器中。數據結構
(2).類內部的註解:如@Autowire、@Value、@Resource以及EJB和WebService相關的註解等,都是添加在類內部的字段或者方法上的類內部註解,Spring IoC容器經過Bean後置註解處理器解析Bean內部的註解。app
下面將根據這兩種處理策略,分別分析Spring處理註解相關的源碼。ide
2.AnnotationConfigApplicationContext對註解Bean初始化:函數
Spring中,管理註解Bean定義的容器有兩個:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContex。這兩個類是專門處理Spring註解方式配置的容器,直接依賴於註解做爲容器配置信息來源的IoC容器。 AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,二者的用法以及對註解的處理方式幾乎沒有什麼差異,所以本文將以AnnotationConfigApplicationContext爲例進行講解。源碼分析
AnnotationConfigApplicationContext的源碼以下:post
public class AnnotationConfigApplicationContext extends GenericApplicationContext { //建立一個讀取註解的Bean定義讀取器,並將其設置到容器中 private final AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(this); //建立一個掃描指定類路徑中註解Bean定義的掃描器,並將其設置到容器中 private final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this); //默認構造函數,初始化一個空容器,容器不包含任何 Bean 信息,須要在稍後經過調用其register() //方法註冊配置類,並調用refresh()方法刷新容器,觸發容器對註解Bean的載入、解析和註冊過程 public AnnotationConfigApplicationContext() { } //最經常使用的構造函數,經過將涉及到的配置類傳遞給該構造函數,以實現將相應配置類中的Bean //自動註冊到容器中 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { register(annotatedClasses); refresh(); } //該構造函數會自動掃描以給定的包及其子包下的全部類,並自動識別全部的Spring Bean,將其 //註冊到容器中 public AnnotationConfigApplicationContext(String... basePackages) { scan(basePackages); refresh(); } //爲容器的註解Bean讀取器和註解Bean掃描器設置Bean名稱產生器 public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { this.reader.setBeanNameGenerator(beanNameGenerator); this.scanner.setBeanNameGenerator(beanNameGenerator); } //爲容器的註解Bean讀取器和註解Bean掃描器設置做用範圍元信息解析器 public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) { this.reader.setScopeMetadataResolver(scopeMetadataResolver); this.scanner.setScopeMetadataResolver(scopeMetadataResolver); } //爲容器註冊一個要被處理的註解Bean,新註冊的Bean,必須手動調用容器的 //refresh()方法刷新容器,觸發容器對新註冊的Bean的處理 public void register(Class<?>... annotatedClasses) { this.reader.register(annotatedClasses); } //掃描指定包路徑及其子包下的註解類,爲了使新添加的類被處理,必須手動調用 //refresh()方法刷新容器 public void scan(String... basePackages) { this.scanner.scan(basePackages); } }
經過對AnnotationConfigApplicationContext的源碼分析,咱們瞭解到Spring對註解的處理分爲兩種方式:this
(1).直接將註解Bean註冊到容器中:
能夠在初始化容器時註冊;也能夠在容器建立以後手動調用註冊方法向容器註冊,而後經過手動刷新容器,使得容器對註冊的註解Bean進行處理。
(2).經過掃描指定的包及其子包下的全部類:
在初始化註解容器時指定要自動掃描的路徑,若是容器建立之後向給定路徑動態添加了註解Bean,則須要手動調用容器掃描的方法,而後手動刷新容器,使得容器對所註冊的Bean進行處理。
接下來,將會對兩種處理方式詳細分析其實現過程。
3.AnnotationConfigApplicationContext註冊註解Bean:
當建立註解處理容器時,若是傳入的初始參數是具體的註解Bean定義類時,註解容器讀取並註冊。
(1).AnnotationConfigApplicationContext經過調用註解Bean定義讀取器AnnotatedBeanDefinitionReader的register方法向容器註冊指定的註解Bean,註解Bean定義讀取器向容器註冊註解Bean的源碼以下:
從上面的源碼咱們能夠看出,註冊註解Bean定義類的基本步驟:
a,須要使用註解元數據解析器解析註解Bean中關於做用域的配置。
b,使用AnnotationConfigUtils的processCommonDefinitionAnnotations方法處理註解Bean定義類中通用的註解。
c,使用AnnotationConfigUtils的applyScopedProxyMode方法建立對於做用域的代理對象。
d,經過BeanDefinitionReaderUtils向容器註冊Bean。
下面咱們繼續分析這3步的具體實現過程
(2).AnnotationScopeMetadataResolver解析做用域元數據:
AnnotationScopeMetadataResolver經過processCommonDefinitionAnnotations方法解析註解Bean定義類的做用域元信息,即判斷註冊的Bean是原生類型(prototype)仍是單態(singleton)類型,其源碼以下:
上述代碼中的annDef.getMetadata().getAnnotationAttributes方法就是獲取對象中指定類型的註解的值。
(3).AnnotationConfigUtils處理註解Bean定義類中的通用註解:
AnnotationConfigUtils類的processCommonDefinitionAnnotations在向容器註冊Bean以前,首先對註解Bean定義類中的通用Spring註解進行處理,源碼以下:
//處理Bean定義中通用註解 static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { //若是Bean定義中有@Primary註解,則爲該Bean設置爲autowiring自動依賴注入//裝配的首選對象 if (abd.getMetadata().isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } //若是Bean定義中有@Lazy註解,則將該Bean預實例化屬性設置爲@lazy註解的值 if (abd.getMetadata().isAnnotated(Lazy.class.getName())) { Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value"); abd.setLazyInit(value); } //若是Bean定義中有@ DependsOn註解,則爲該Bean設置所依賴的Bean名稱, //容器將確保在實例化該Bean以前首先實例化所依賴的Bean if (abd.getMetadata().isAnnotated(DependsOn.class.getName())) { String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName()).get("value"); abd.setDependsOn(value); } }
//處理Bean定義中通用註解 static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { //若是Bean定義中有@Primary註解,則爲該Bean設置爲autowiring自動依賴注入//裝配的首選對象 if (abd.getMetadata().isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } //若是Bean定義中有@Lazy註解,則將該Bean預實例化屬性設置爲@lazy註解的值 if (abd.getMetadata().isAnnotated(Lazy.class.getName())) { Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value"); abd.setLazyInit(value); } //若是Bean定義中有@ DependsOn註解,則爲該Bean設置所依賴的Bean名稱, //容器將確保在實例化該Bean以前首先實例化所依賴的Bean if (abd.getMetadata().isAnnotated(DependsOn.class.getName())) { String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName()).get("value"); abd.setDependsOn(value); } }
(4).AnnotationConfigUtils根據註解Bean定義類中配置的做用域爲其應用相應的代理策略:
AnnotationConfigUtils類的applyScopedProxyMode方法根據註解Bean定義類中配置的做用域@Scope註解的值,爲Bean定義應用相應的代理模式,主要是在Spring面向切面編程(AOP)中使用。源碼以下
//根據做用域爲Bean應用引用的代碼模式 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,則返回false boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); //爲註冊的Bean建立相應模式的代理對象 return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); }
//根據做用域爲Bean應用引用的代碼模式 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,則返回false boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS); //爲註冊的Bean建立相應模式的代理對象 return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass); }
這段爲Bean引用建立相應模式的代理,若是在Spring面向切面編程(AOP)中涉及到再詳細分析,這裏不作深刻的分析。
(5).BeanDefinitionReaderUtils向容器註冊Bean:
BeanDefinitionReaderUtils向容器註冊載入的Bean咱們在第4篇博客中已經分析過,主要是校驗Bean定義,而後將Bean添加到容器中一個管理Bean定義的HashMap中,這裏就不作分析。
4.AnnotationConfigApplicationContext掃描指定包及其子包下的註解Bean:
當建立註解處理容器時,若是傳入的初始參數是註解Bean定義類所在的包時,註解容器將掃描給定的包及其子包,將掃描到的註解Bean定義載入並註冊。
(1).Spring中經常使用的註解:
a.Component註解:
b.Service註解:
c.Controller註解:
d.Repository註解:
經過分析Spring這4個經常使用的註解源碼,咱們看到:@Service、@Controller和@Repository註解都添加了一個@Component註解,所以他們都屬於@Component
註解。
(2).ClassPathBeanDefinitionScanner掃描給定的包及其子包:
AnnotationConfigApplicationContext經過調用類路徑Bean定義掃描器ClassPathBeanDefinitionScanner掃描給定包及其子包下的全部類,主要源碼以下:
類路徑Bean定義掃描器ClassPathBeanDefinitionScanner主要經過findCandidateComponents方法調用其父類ClassPathScanningCandidateComponentProvider類來掃描獲取給定包及其子包下的類。
(3).ClassPathScanningCandidateComponentProvider掃描給定包及其子包的類:
ClassPathScanningCandidateComponentProvider類的findCandidateComponents方法具體實現掃描給定類路徑包的功能,主要源碼以下
public class ClassPathScanningCandidateComponentProvider implements ResourceLoaderAware { //保存過濾規則要包含的註解,即Spring默認的@Component、@Repository、@Service、//@Controller註解的Bean,以及JavaEE6的@ManagedBean和JSR-330的@Named註解 private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>(); //保存過濾規則要排除的註解 private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>(); //構造方法,該方法在子類ClassPathBeanDefinitionScanner的構造方法中被調用 public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) { //若是使用Spring默認的過濾規則,則向容器註冊過濾規則 if (useDefaultFilters) { registerDefaultFilters(); } } //向容器註冊過濾規則 protected void registerDefaultFilters() { //向要包含的過濾規則中添加@Component註解類,注意Spring中@Repository //@Service和@Controller都是Component,由於這些註解都添加了@Component註解 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); //獲取當前類的類加載器 ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { //向要包含的過濾規則添加JavaEE6的@ManagedBean註解 this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false)); logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { //向要包含的過濾規則添加@Named註解 this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false)); logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } //掃描給定類路徑的包 public Set<BeanDefinition> findCandidateComponents(String basePackage) { //建立存儲掃描到的類的集合 Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { //解析給定的包路徑,this.resourcePattern=」 **/*.class」, //ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX=「classpath:」 //resolveBasePackage方法將包名中的」.」轉換爲文件系統的」/」 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + this.resourcePattern; //將給定的包路徑解析爲Spring資源對象 Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); //遍歷掃描到的資源 for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { //爲指定資源獲取元數據讀取器,元信息讀取器經過彙編(ASM)讀//取資源元信息 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); //若是掃描到的類符合容器配置的過濾規則 if (isCandidateComponent(metadataReader)) { //經過彙編(ASM)讀取資源字節碼中的Bean定義元信息 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); //設置Bean定義來源於resource sbd.setResource(resource); //爲元數據元素設置配置資源對象 sbd.setSource(resource); //檢查Bean是不是一個可實例化的對象 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); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } //判斷元信息讀取器讀取的類是否符合容器定義的註解過濾規則 protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //若是讀取的類的註解在排除註解過濾規則中,返回false for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } //若是讀取的類的註解在包含的註解的過濾規則中,則返回ture for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return true; } } //若是讀取的類的註解既不在排除規則,也不在包含規則中,則返回false return false; } …… }
public class ClassPathScanningCandidateComponentProvider implements ResourceLoaderAware { //保存過濾規則要包含的註解,即Spring默認的@Component、@Repository、@Service、//@Controller註解的Bean,以及JavaEE6的@ManagedBean和JSR-330的@Named註解 private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>(); //保存過濾規則要排除的註解 private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>(); //構造方法,該方法在子類ClassPathBeanDefinitionScanner的構造方法中被調用 public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) { //若是使用Spring默認的過濾規則,則向容器註冊過濾規則 if (useDefaultFilters) { registerDefaultFilters(); } } //向容器註冊過濾規則 protected void registerDefaultFilters() { //向要包含的過濾規則中添加@Component註解類,注意Spring中@Repository //@Service和@Controller都是Component,由於這些註解都添加了@Component註解 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); //獲取當前類的類加載器 ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { //向要包含的過濾規則添加JavaEE6的@ManagedBean註解 this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false)); logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { //向要包含的過濾規則添加@Named註解 this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false)); logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } //掃描給定類路徑的包 public Set<BeanDefinition> findCandidateComponents(String basePackage) { //建立存儲掃描到的類的集合 Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { //解析給定的包路徑,this.resourcePattern=」 **/*.class」, //ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX=「classpath:」 //resolveBasePackage方法將包名中的」.」轉換爲文件系統的」/」 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + this.resourcePattern; //將給定的包路徑解析爲Spring資源對象 Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); //遍歷掃描到的資源 for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { //爲指定資源獲取元數據讀取器,元信息讀取器經過彙編(ASM)讀//取資源元信息 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); //若是掃描到的類符合容器配置的過濾規則 if (isCandidateComponent(metadataReader)) { //經過彙編(ASM)讀取資源字節碼中的Bean定義元信息 ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); //設置Bean定義來源於resource sbd.setResource(resource); //爲元數據元素設置配置資源對象 sbd.setSource(resource); //檢查Bean是不是一個可實例化的對象 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); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } //判斷元信息讀取器讀取的類是否符合容器定義的註解過濾規則 protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { //若是讀取的類的註解在排除註解過濾規則中,返回false for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return false; } } //若是讀取的類的註解在包含的註解的過濾規則中,則返回ture for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, this.metadataReaderFactory)) { return true; } } //若是讀取的類的註解既不在排除規則,也不在包含規則中,則返回false return false; } …… }
5.AnnotationConfigWebApplicationContext載入註解Bean定義:
AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的Web版,它們對於註解Bean的註冊和掃描是基本相同的,可是AnnotationConfigWebApplicationContext對註解Bean定義的載入稍有不一樣,AnnotationConfigWebApplicationContext注入註解Bean定義源碼以下: