亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Spring的初始化和XML解析的實現

2024-07-14 08:43:41
字體:
來源:轉載
供稿:網友

前言

Spring是什么?它是一個應用程序框架,為應用程序的開發提供強大的支持,例如對事務處理和持久化的支持等;它也是一個bean容器,管理bean對象的整個生命周期,維護bean的各種存在狀態,例如bean對象的實例化、銷毀、bean的單實例和多實例狀態等。

Spring作為Java發展史上不可忽視的存在,說他重新定義了Java也不為過。它功能強大,著實為日常開發提供了大大的便利。表面越簡單的東西,背后越復雜。
從本章節開始,我們一起分析Spring的源碼,看它到底是怎么樣來實現我們常說常用的諸如IOC、Annotation、AOP、事務等功能的。

1、Spring的入口

在我們的項目中,web.xml必不可少,其中就定義了Spring的監聽器。

<listener>   <listener-class>      org.springframework.web.context.ContextLoaderListener   </listener-class></listener>

我們來看ContextLoaderListener類,可以看到它實現了ServletContextListener接口,
contextInitialized就是Spring初始化的入口方法。

Spring還有一個入口,叫做org.springframework.web.servlet.DispatcherServlet,它們之間是父子容器的關系,最終都會調用到同一個方法org.springframework.context.support.AbstractApplicationContext.refresh()。

2、初始化

Spring的初始化第一步就是要加載配置文件,然后解析里面的配置項。

ok,我們來到XmlWebApplicationContext類的loadBeanDefinitions方法。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {  String[] configLocations = getConfigLocations();  if (configLocations != null) {    for (String configLocation : configLocations) {      reader.loadBeanDefinitions(configLocation);    }  }}

可以看到,configLocations是一個數組,它獲取的就是配置文件。在筆者的項目中,只有一個配置文件,名字是applicationContext.xml。下一步就是通過loadBeanDefinitions這個方法解析這個配置文件。

3、解析XML配置

首先把一個配置文件封裝成一個Resource對象,然后獲取Resource對象的輸入流,轉換成InputSource對象,最后解析成Document對象。下面代碼只保留了主要部分。

public int loadBeanDefinitions(String location, Set<Resource> actualResources)              throws BeanDefinitionStoreException {    ResourceLoader resourceLoader = getResourceLoader();    if (resourceLoader instanceof ResourcePatternResolver) {      // Resource pattern matching available.      try {        //這里的location就是配置文件-applicationContext.xml,轉成Resource對象        Resource[] resources=resourceLoader).getResources(location);        //獲取resources對象的輸入流 再轉成JDK的InputSource對象,最后解析成Document        InputStream inputStream = resources.getInputStream();        InputSource inputSource = new InputSource(inputStream);        Document doc = doLoadDocument(inputSource, resource);      }      catch (IOException ex) {        throw new BeanDefinitionStoreException(            "Could not resolve bean definition resource pattern [" + location + "]", ex);      }    }  }

applicationContext.xml配置文件解析成Document對象,它的Root節點信息如下:

[    [#text:],   [context:component-scan: null],   [#text:],   [bean: null],   [#text:],   [bean: null],   [#text:],   [bean: null],  [#text:],   [bean: null],   [#text:],   [#comment: 指定了表現層資源的前綴和后綴    viewClass:JstlView表示JSP模板頁面需要使用JSTL標簽庫    prefix 和suffix:查找視圖頁面的前綴和后綴,比如傳進來的邏輯視圖名為hello,則該該            jsp視圖頁面應該存放在“WEB-INF/jsp/hello.jsp”],   [#text:],   [bean: null],   [#text: ]]

4、加載Bean信息

上一步我們看到Spring已經把applicationContext.xml這個配置文件解析成了Document對象,接下來就是關鍵的一步。先看源碼

  //這里拿到的是Document對象的根節點,根節點信息參考上圖  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {    if (delegate.isDefaultNamespace(root)) {      NodeList nl = root.getChildNodes();      for (int i = 0; i < nl.getLength(); i++) {        Node node = nl.item(i);        if (node instanceof Element) {          Element ele = (Element) node;          //這里有兩個分支。          //一個是處理默認的節點(import、alias、bean、beans)          //一個是處理自定義的節點(context:component-scan)          if (delegate.isDefaultNamespace(ele)) {            parseDefaultElement(ele, delegate);          }          else {            delegate.parseCustomElement(ele);          }        }      }    }    else {      delegate.parseCustomElement(root);    }  }

4.1 component-scan的解析

首先定位到自定義解析方法delegate.parseCustomElement(ele);

最終調用了org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(Element element, ParserContext parserContext),不過它是怎么調用到這個類的呢?說起來就比較有意思了。

我們先來看Spring里面的一個配置文件,/META-INF/spring.handlers

http/://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http/://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http/://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http/://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http/://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

這里面配置了不同標簽的處理類,比如context標簽處理類就是ContextNamespaceHandler,然后通過反射實例化這個處理類,調用它的init()方法。init()方法里面它又注冊了一堆處理類,其中就有我們很感興趣的component-scan。

  public NamespaceHandler resolve(String namespaceUri) {    //handlerMappings里有個方法loadAllProperties(),獲取Spring所有的配置項    Map<String, Object> handlerMappings = getHandlerMappings();    Object handlerOrClassName = handlerMappings.get(namespaceUri);    if (handlerOrClassName == null) {      return null;    }    else if (handlerOrClassName instanceof NamespaceHandler) {      return (NamespaceHandler) handlerOrClassName;    }    else {      String className = (String) handlerOrClassName;      try {        //以context:component-scan舉例        //這里拿到的className就是org.springframework.context.config.ContextNamespaceHandler        //通過反射,實例化這個ContextNamespaceHandler,然后調用init方法        Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);        NamespaceHandler namespaceHandler = BeanUtils.instantiateClass(handlerClass);        namespaceHandler.init();        handlerMappings.put(namespaceUri, namespaceHandler);        return namespaceHandler;      }    }  }  public void init() {    registerBeanDefinitionParser("annotation-config",       new AnnotationConfigBeanDefinitionParser());    registerBeanDefinitionParser("component-scan",       new ComponentScanBeanDefinitionParser());    //...未完  }

最終Spring就可以通過component-scan這個標簽,拿到ComponentScanBeanDefinitionParser類,調用它的parse()方法。

 public BeanDefinition parse(Element element, ParserContext parserContext) {    //獲取包掃描路徑,對應配置文件中的base-package="com.viewscenes.netsupervisor"    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);    basePackage = parserContext.getReaderContext().getEnvironment().       resolvePlaceholders(basePackage);    //這里可能有多個包路徑,分割成數組    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);    /**     * configureScanner 配置掃描器。     * scanner.doScan 掃描執行     * registerComponents 這里重點是對registerComponents的支持     *      * @return     */    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);    return null;  }

4.1.1 configureScanner 配置掃描器

這里面重點就是注冊了默認的過濾器。use-default-filters,默認值是true,如果配置文件配置了此屬性的值為false,有些注解就加不進來,到下一步掃描的時候就注冊不了Bean。

protected void registerDefaultFilters() {  //這個就是配置的use-default-filters,如果配置了false。那么下面的  // Component、ManagedBean、Named注解都不會被掃描到  if (useDefaultFilters) {     this.includeFilters.add(new AnnotationTypeFilter(Component.class));    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();    try {      this.includeFilters.add(new AnnotationTypeFilter(      ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)),                                      false));      logger.debug("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.    }       //...未完  }}

4.1.2 doScan掃描

doScan分為三個步驟。

  • findCandidateComponents 掃描包路徑下的所有class文件,過濾有Component注解的類,轉換成BeanDefinition對象,加入一個LinkedHashSet中。
  • 循環上一步返回的LinkedHashSet,設置基本屬性,比如setLazyInit、setScope。
  • 注冊BeanDefinition對象,向Map容器中緩存beanName和BeanDefinition,向List中加入beanName。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();    for (String basePackage : basePackages) {      //findCandidateComponents方法掃描class文件,判斷Component注解,轉成BeanDefinition對象返回。      //值得注意的是,Component不止是@Component,還有      //@Controller、@Service、@Repository,因為在這三個注解上面還有個@Component。     //這就相當于它們都是Component的子注解。      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);      for (BeanDefinition candidate : candidates) {        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.                              resolveScopeMetadata(candidate);        //設置屬性,沒有配置的都是默認值        candidate.setScope(scopeMetadata.getScopeName());        candidate.setxxx(scopeMetadata.getxxxName());        String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);        //registerBeanDefinition方法 注冊BeanDefinition,等同于下面兩句        //this.beanDefinitionMap.put(beanName, beanDefinition);        //this.beanDefinitionNames.add(beanName);        registerBeanDefinition(definitionHolder, this.registry);      }    }    return beanDefinitions;  }

最后整個方法返回的就是beanDefinition對象的Set集合,以兩個Controller為例。

[    Generic bean: class [com.viewscenes.netsupervisor.controller.IndexController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:/apache-tomcat-7.0.78/webapps/springmvc_dubbo_producer/WEB-INF/classes/com/viewscenes/netsupervisor/controller/IndexController.class],     Generic bean: class [com.viewscenes.netsupervisor.controller.UserController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:/apache-tomcat-7.0.78/webapps/springmvc_dubbo_producer/WEB-INF/classes/com/viewscenes/netsupervisor/controller/UserController.class]]

4.1.3 對annotation-config的支持

我們知道,在Spring配置文件有個配置是context:annotation-config 但如果配置了context:component-scan 就不必再配置config,這是因為在解析component-scan的時候已經默認添加了annotation-config的支持,除非你手動設置了annotation-config="false",不過這可不太妙,因為在IOC的時候就沒辦法支持@Autowired等注解了。

protected void registerComponents(XmlReaderContext readerContext,            Set<BeanDefinitionHolder> beanDefinitions, Element element) {  boolean annotationConfig = true;  if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {    annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));  }  if (annotationConfig) { //判斷annotation-config屬性的值    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {      RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);      beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));    }    if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {      RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);      beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));    }    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {      RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);      beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));    }      ......未完  }}

4.2 bean標簽的解析

bean標簽的解析,就是默認的處理方法。

獲取bean標簽的id,并且把beanName賦值為id,設置別名。新建AbstractBeanDefinition對象,通過反射設置beanClass,解析property屬性名稱和值。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {    //獲取bean_id     String id = ele.getAttribute(ID_ATTRIBUTE);    String beanName = id;    AbstractBeanDefinition beanDefinition =                parseBeanDefinitionElement(ele, beanName, containingBean);    String[] aliasesArray = StringUtils.toStringArray(aliases);    //最后返回已經包含了beanName、class對象和一系列方法的BeanDefinition對象    return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}public AbstractBeanDefinition parseBeanDefinitionElement(Element ele,                   String beanName, BeanDefinition containingBean) {    String className = ele.getAttribute(CLASS_ATTRIBUTE).trim();    try {      //根據className反射設置setBeanClass和setBeanClassName      AbstractBeanDefinition bd = createBeanDefinition(className, parent);      //設置默認方法 setScope、setLazyInit、setAutowireMode...      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);      //設置property屬性 <bean><property name="id" value="1001"></property></bean>      parsePropertyElements(ele, bd);      return bd;    }    return null;}

注冊BeanDefinition對象,和component-scan掃描的bean注冊一樣。向容器中填充對象。

不管是XML配置的Bean,還是通過component-scan掃描注冊的Bean它們最后都是殊途同歸的,會轉換成一個BeanDefinition對象。記錄著這個Bean對象的屬性和方法,最后都注冊到容器中,等待在實例化和IOC的時候遍歷它們。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品视频男人的天堂| 在线不卡国产精品| 久久久在线免费观看| 国产精品流白浆视频| 成人精品一区二区三区| 91精品国产综合久久香蕉| 亚洲免费伊人电影在线观看av| 亚洲男人的天堂网站| 久久久久久噜噜噜久久久精品| 久久九九免费视频| 性欧美激情精品| 精品国产福利视频| 这里只有精品在线播放| 亚洲欧美日韩国产中文专区| 久久久成人av| 日韩精品高清在线| 在线观看日韩欧美| 欧美成人性色生活仑片| 精品国内亚洲在观看18黄| 欧美一乱一性一交一视频| 久久久精品网站| 插插插亚洲综合网| 日韩精品一区二区三区第95| 久久精品久久精品亚洲人| 日韩av中文字幕在线免费观看| 久久精品小视频| 中文字幕久久亚洲| 有码中文亚洲精品| 日韩中文字幕欧美| 日韩欧美国产骚| 亚洲第一福利视频| 国产成人涩涩涩视频在线观看| 亚洲缚视频在线观看| 色综合色综合网色综合| 亚洲乱码av中文一区二区| 美女av一区二区三区| 亚洲无限乱码一二三四麻| 久久影视电视剧免费网站清宫辞电视| 亚洲自拍偷拍一区| 日韩中文字幕视频在线观看| 欧美天堂在线观看| 久久久精品视频在线观看| 日韩毛片在线观看| 亚洲r级在线观看| 97国产精品免费视频| 亚洲欧美国产精品va在线观看| 中文字幕av一区中文字幕天堂| 亚洲深夜福利视频| 久久精品99无色码中文字幕| 人人爽久久涩噜噜噜网站| 欧美亚洲成人xxx| 97久久精品人搡人人玩| 国产精品大陆在线观看| 91精品国产91久久久| 97视频在线观看亚洲| 日韩在线精品一区| 一本一本久久a久久精品综合小说| 国产ts人妖一区二区三区| 日韩精品在线免费观看| 亚洲一区二区三区在线免费观看| 国产精品18久久久久久首页狼| 国产精品永久在线| 国产一区深夜福利| 日韩欧美a级成人黄色| 精品国产一区二区三区久久久狼| 91国产视频在线播放| 在线a欧美视频| 自拍偷拍亚洲区| 91视频免费在线| 色综合久久久久久中文网| 97精品视频在线| 91av视频在线免费观看| 91高潮在线观看| 欧美一区二粉嫩精品国产一线天| 日本精品视频在线观看| 欧美一区二区大胆人体摄影专业网站| 日韩有码在线电影| 亚洲香蕉成视频在线观看| 69av视频在线播放| 91久久精品国产91久久| 亚洲在线观看视频网站| 欧美综合国产精品久久丁香| 日韩中文在线中文网在线观看| 久久福利视频网| 国产91免费看片| 成人美女免费网站视频| 日韩成人在线视频网站| 91精品在线影院| 欧美视频免费在线| 欧美wwwwww| 久久99精品久久久久久噜噜| 亚洲电影免费观看高清| 伊人久久免费视频| 色视频www在线播放国产成人| 欧美日韩国产在线播放| 欧美激情二区三区| 欧美丝袜美女中出在线| 日韩在线视频导航| 国产在线视频2019最新视频| 国产精品成人观看视频国产奇米| 成人h视频在线观看播放| 日韩一区二区在线视频| 久久视频国产精品免费视频在线| 欧美日韩亚洲系列| 一区二区三区高清国产| 日韩在线观看免费全| 亚洲乱码一区av黑人高潮| 久久伊人精品一区二区三区| 日韩激情av在线播放| 欧美成人免费一级人片100| 欧美极品欧美精品欧美视频| 国产精品第2页| 国产一区二区三区视频免费| 日韩性xxxx爱| 日韩中文字幕亚洲| 国产乱人伦真实精品视频| 亚洲福利视频网站| 日韩精品在线私人| 国产91色在线|免| 亚洲精品v天堂中文字幕| 亚洲一区美女视频在线观看免费| 亚洲一区二区免费| 成人黄色av播放免费| 国产精品吊钟奶在线| 久久综合伊人77777蜜臀| 亚洲影院高清在线| 日韩最新在线视频| 日韩中文字幕免费视频| 欧美肥老太性生活视频| 亚洲第一区第一页| 国产综合久久久久| 日韩av色综合| 国产亚洲美女精品久久久| 欧美性猛交99久久久久99按摩| 久久伊人精品一区二区三区| 最新中文字幕亚洲| 亚洲精品久久7777777| 日韩国产精品亚洲а∨天堂免| 欧美成人精品h版在线观看| 日韩精品亚洲视频| 中文字幕亚洲在线| 亚洲第一视频网站| 国产成人精品日本亚洲专区61| 亚洲专区在线视频| 搡老女人一区二区三区视频tv| 国产精品视频色| 2019av中文字幕| 亚洲国产黄色片| 一区二区三区四区精品| 韩日精品中文字幕| 欧美日本亚洲视频| 中文字幕免费精品一区高清| 精品久久久久人成| 国产欧美日韩精品丝袜高跟鞋| 欧美日韩免费在线观看| 九九热最新视频//这里只有精品| 热99久久精品| 中文字幕亚洲激情| 日韩欧美在线观看视频| 日韩女在线观看| 2019日本中文字幕| 欧美视频在线视频| 77777少妇光屁股久久一区| 国产精品av电影|