国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院

首頁 > 開發 > 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教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院
96久久久久久| 国产无遮挡又黄又爽免费网站| 国产精品四虎| 国产精品xxx电影| 国产精品自产拍在线网站| 九九视频九九热| 国产精彩视频在线观看免费蜜芽| 91蜜桃在线视频| 亚洲欧洲成人| 国产在线观看18| 国产午夜电影| 青青草中文字幕| 国产激情小视频在线| 日本aⅴ写真网站免费| 国产午夜三区视频在线| 99热免费观看| 中文字幕在线观看播放| 四虎一区二区三区| 最近最好的中文字幕2019免费| 天天av天天爽| 91中文字幕| 国产视频资源| 爱福利在线视频| 国产高清av| 懂色av一区| 免费精品国产自产拍观看| 精品国产白色丝袜高跟鞋| 国产69久久| 狠狠干婷婷色| 亚洲精品乱码电影在线观看| 国产第一页在线| 中文字幕第一页在线| 黄色片大全在线观看| 99久久免费精品国产免费| 久久精品亚洲7777影院| 一区二区三区四区在线免费视频| 国产美女极品在线| 国产另类图片| 国产精品自产拍在线网站| 在线免费观看污| 91美女主播在线视频| 国产精品免费视频二三区| 国产美女福利在线| 国产美女高潮| 国产图片综合| 精品成人免费自拍视频| 国产不卡视频| 香蕉视频在线观看www| 亚洲成人在线播放| av在线第一页| 国产在线一区二区视频| 国产在线观看网站| 国产老肥熟xxxx在线观看| 日本中文字幕在线视频| 午夜视频99| 在线中文字幕第一页| 四虎影视成人永久免费观看视频| 亚洲欧美久久婷婷爱综合一区天堂 | 精品成人一区二区三区免费视频| 国产一区二区三区四区尤物| 最新av免费看| 国产经典av| www.国产精| 国产亚洲精品久久久网站好莱| 国产三级av在线| gogo高清在线播放免费| 天堂在线看视频| 亚洲国产日韩在线人成电影| 黄色一级片视频| 性网站在线播放| 国产高清免费av在线| 欧美激情福利视频在线观看免费| 国产一级激情| 日本视频在线观看一区二区三区| 丁香在线视频| av亚洲在线| 国产麻豆视频网站| 99免费视频| 国产羞羞视频在线观看| 久久精品视频免费看| ·天天天天操| 国产福利免费观看| 五月婷婷在线观看| 国产在线日本| 蜜桃av在线免费观看| 91资源在线观看| 国产黄色网页| 国产精品冒白浆免费视频| 国产尤物一区二区三区| 人人在线视频| 免费午夜一级| 日本在线天堂| gogo在线高清视频| 在线三级中文| 日韩a视频在线观看| 精品街拍一区二区| 69久久久久| 91在线最新| 四虎成人免费| 免费电影网站在线视频观看福利| 在线视频观看亚洲| 亚洲永久免费网站| 日本久久国产| 九九热视频精品在线观看| 久青青在线观看视频国产| 久草亚洲一区| 国产乱妇乱子在线播视频播放网站| 欧美日韩性视频一区二区三区| 亚洲成人在线播放| 欧美日韩**字幕一区| 久久香蕉av| 国产区卡一卡二卡三乱码免费| 国产三区四区在线观看| 国产精美视频| av免费在线免费| 免费a级毛片在线播放| 超碰在线网站| 国产精品久久久久久精| 精品麻豆视频| 久草网在线视频| 国产91在线视频蝌蚪| 99在线免费观看| 久久香蕉av| 国产www.大片在线| 免费午夜一级| 国产欧美一区二区三区小说| 一本大道久久精品| 久热精品视频在线播放| 日本中文字幕视频| 在线免费看黄av| 天天操天天是| 青草视频在线播放| 97高清视频| 国产偷激情在线| 免费高清视频日韩| 中文国产字幕在线观看| 激情网站在线| h视频在线网站| 精品国语对白精品自拍视| 国产专区在线播放| www.操操| 亚洲欧美日韩成人网| 国产午夜电影| 2018av男人天堂| 国产乱妇乱子| 激情网站在线| 开心丁香婷婷深爱五月| 亚洲精品视频区| 999国产在线视频| 国产一级免费| 中文字幕麻豆| 国产乱xxⅹxx国语对白| 美女av在线播放| 免费黄色网页在线观看| 日p在线观看| 四虎网站在线观看| 亚洲wwwwww| gogo高清在线播放免费| 香蕉视频网站在线观看| 夜夜嗨yeyeh| 在线āv视频| 精品欧美日韩一区二区| 精品视频一二区| 97视频在线| 亚洲网站一区| 欧美视频免费一区二区三区| av在线free| 久艹在线视频| 1区不卡电影| 国产黄在线播放| 99热99re6国产在线播放| 在线āv视频| 另类专区欧美| 丁香花视频在线观看| 成网站在线观看人免费| 国产九九九九| 51成人精品网站| www.av在线| 大香伊人中文字幕精品| 国产精品区一区二| 四虎成人免费观看在线网址| av在线第一页| 国产麻豆免费| 亚洲综合色视频在线观看| 牛牛精品视频在线| 国产一区精品| 亚洲社区在线| 尤物在线视频| 亚洲成人av在线影院| 五月综合网站| 免费av不卡在线观看| 国产成人午夜精品| 激情综合丁香| 中文乱码字幕高清在线观看| 国产中文字幕在线观看| 夜色资源网av在先锋网站观看| 免费看ww视频网站入口| 国产激情视频一区二区三区| 黄色片大全在线观看| av在线二区|