在spring mvc中掃描註解機制是咱們理解javabean是怎麼被加載,是如何被spring進行管理的第一步。那spring mvc 是如何掃描全部的編譯文件並對註解進行操做的呢,下面咱們來看下:
java
在spring-mvc中咱們都會配置一個web.xml文件,內容以下:
咱們知道只要servlet中的load-on-startup配置了大於1的數字,類就會在應用啓動的時候被加載,在加載時候是調用了DispatcherServlet 的init()方法。
但咱們進入dispatcherServlet並無找到init()方法,此時咱們繼續查找父類,在其父類的父類HttpServletBean這個類中找到了init()方法:
init方法中有一個initServletBean()方法,這個方法就是用來初始化javabean的。
在其子類,FrameworkServlet中咱們看到了initServletBean方法的實現方法,其方法中有這樣一行代碼:
其實,只要獲取到ApplicationContext就能獲取到javabean信息,此時咱們進去看下,其是如何初始化ApplicationContext的:
在FrameworkServlet中,標紅代碼建立了ApplicationContext,關於其中的parent參數,之後再說,繼續跟進,在createWebApplicationContext方法中:
其實,不管是ClassPathXmlApplicationContext仍是WEB裝載都會調用這段方法,區別只是在於,web容器中,用servlet裝載時,servlet包裝了一個XmlWebApplicationContext而已,不管是ClassPathXmlApplicationContext仍是XmlWebApplicationContext他們都繼承自抽象類:AbstractApplicationContext,他們共用了AbstractApplicationContext的refresh()方法:
雖然這些一層調用一層看起來很煩,但仍是但願你們有時間看下去,由於在spring-mvc裝載bean的時候不只要讀取解析xml文件還要對class文件進行bean讀取加載,而且在xml解析,類加載,實例化的過程當中有不少需求,而且spring-mvc採用了分離設計,下面咱們來看看obtainFreshBeanFactory()方法:
在obtainFreshBeanFactory()方法中,加載bean的是第一行refreshBeanFactory()方法,而不是getBeanFactory()方法。
關於refreshBeanFactory()的實現是在AbstractRefreshableApplicationContext類中實現了,它繼承自AbstractApplicationContext同時,
ClassPathXmlApplicationContext與XmlWebApplicationContext也繼承了AbstractRefreshableApplicationContext這個類
在第一行,他createBeanFactory,同時在loadBeanDefinitions(beanFactory)中加載bean並將bean放入了BeanFactory中,繼續跟蹤loadBeanDefinitions方法,它是由AbstractXmlApplicationContext類中的方法實現,web項目中會由XmlWebApplicationContext來實現,loadBeanDefinitions方法以下:
它經過XmlBeanDefinitionReader類去讀取springXML中的信息(也就是解析SpringContext.xml)並加載bean,接下來會調用多層,並在XMLBeanDefinitionReader類中去調用doLoadBeanDefinitions、regiseterBeanDefinitions操做,在regiseterBeanDefinitions的時候就開始解析XML了。它調用了DefaultBeanDefinitionDocumentReader類的registerBeanDefinitions方法,以下圖所示:
在這個方法中作了解析XML中的操做,看標紅代碼,這段代碼作了bean的解析工做。跟蹤進去會發現裏面解析了XML的信息,其中NamespaceHanderSupport的parse方法會根據節點的類型,找到合適的解析(BeanDefinitionParse)方式,他們預先已經被註冊號了,放在一個HashMap中,例如在Spring的annotation的註解掃描中,咱們一般會配置:<context:component-scan base-package="com.xxx">,此時根據名稱「component-scan」就會找到對應的解析器來解析,此時解析註解是用的ComponentScanBeanDefinitionParser的parse方法。
在parse獲取到後,有一個關鍵的步驟就是定義了ClassPathBeanDefinitionScanner來掃描類信息來對class文件進行掃描:
這個方法中最重要的就是doScan方法的做用,那doScan方法是如何掃描的呢,這裏能夠跟蹤進去一塊兒看看:
咱們先跟進去找到findCandidateComponents方法:
在此方法中經過標紅代碼獲取路徑,並取得了class文件,那麼是怎麼獲取到class文件的呢,有興趣的朋友能夠將classpath路徑寫爲:本身項目編譯文件路徑例如:classpath*:org/sinovate/**/*.class,並用以下操做:
在控制檯輸出resources信息即可以看到class文件路徑名稱信息等。在獲取到.class路徑以及文件名稱後,咱們就能夠經過classLoaderListenser加載.class文件了,此時在根據Annotation類的方法就能夠對@Controller,@Service,@Resource,等一系列註解進行控制與裝載了。web