轉http://blog.csdn.net/congcong68/article/details/40829037
我們在SpringMVC開發項目中,有的用注解和XML配置Bean,這兩種都各有自己的優勢,數據源配置比較經常用XML配置,控制層依賴的service比較經常用注解等(在部署時比較不會改變的),我們經常比較常用的注解有@Component是通用標注,@Controller標注web控制器,@Service標注Servicec層的服務,@Respository標注DAO層的數據訪問。SpringMVC啟動時怎么被自動掃描然后解析並注冊到Bean工廠中去(放到DefaultListableBeanFactory中的Map<String, BeanDefinition> beanDefinitionMap中 以BeanName為key)?我們今天帶着這些問題來了解分析這實現的過程,我們在分析之前先了解一下這些注解。
@Controller標注web控制器,@Service標注Service層的服務,@Respository標注DAO層的數據訪問。@Component是通用標注,只是定義為一個類為Bean,SpringMVC會把所有添加@Component注解的類作為使用自動掃描注入配置路徑下的備選對象。@Controller、@Service\@Respository只是更加的細化,都是被@Component標注,所以我們比較不推薦使用@Component。源代碼如下:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { String value() default ""; } @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Repository { String value() default ""; }
都是有標示@Component
我們在配置文件中,標示配置需要掃描哪些包下,也可以配置對某個包下不掃描,代碼如下:
<context:component-scan base-package="cn.test"> <context:exclude-filter type="regex" expression="cn.test.*.*.controller"/> <context:exclude-filter type="regex" expression="cn.test.*.*.controller2"/> </context:component-scan>
說明:
<context:exclude-filter>指定的不掃描包,<context:exclude-filter>指定的掃描包
SpringMVC先讀取配置文件,然后根據context:component-scan中屬性base-package去掃描指定包下的class和jar文件,把標示@Controller標注web控制器,@Service標注Servicec層的服務,@Respository標注DAO層的數據訪問等注解的都獲取,並注冊為Bean類放到Bean工廠,我們接下來要分析的這個過程。我們平時項目開發都是這樣的注解,實現MVC模式,代碼如下:
//控制層 @Controller @RequestMapping(value="/test") public class TestController2 { @Autowired private TestService testService; @RequestMapping(value="/index") public String getIndex(Model model){ return ""; } } //服務層 @Service("testService") public class TestServiceImpl implements TestService{ }
我們今天的入口點就在這,因為解析注解的到注冊,也是先讀取配置文件並解析,在解析時掃描對應包下的Java類,里面有DefaultBeanDefinitionDocumentReader這個類,doRegisterBeanDefinitions這個方法實現解析配置文件的Bean,這邊已經讀取進來形成Document 形式存儲,然后開始解析Bean,是由BeanDefinitionParserDelegate類實現的,BeanDefinitionParserDelegate完成具體Bean的解析(例如:bean標簽、import標簽等)這個在上一篇SpringMVC 源代碼深度解析 IOC容器(Bean 解析、注冊)里有解析,今天注解屬於擴展的標簽,是由NamespaceHandler和BeanDefinitionParser來解析。源代碼如下:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
NamespaceHandler這邊這邊起到了什么作用,根據不同的Namespace獲取不同的NamespaceHandler,因為我們在Beans標簽配置了命名空間,然后就可以配置對應的標簽,解析標簽時,比較有自己的所實現的NamespaceHandler來解析,如圖所示:
