麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發 > 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网站免费看 | 欧美日韩国产成人在线 | 视频一区二区精品 | 香蕉视频99 | 在线观看91精品 | www.国产免费 | 中文字幕 日本 | 免费一及片| 久久精品色 | 成人激情视频网 | 精品亚洲二区 | 美女黄网站免费观看 | 国产一级中文字幕 | 日本视频网 | 成人激情视频网 | 毛片在哪里看 | 精品小视频 | 欧美精品一区二区性色 | 中文字幕一区2区 | 91精品国产777在线观看 | 国产精品视频一区二区三区四区国 | 欧美日韩大片在线观看 | 久久久一区二区三区视频 | 中文字幕一二三区芒果 | 娇喘在线 | 国产精品a一 | 夜夜看| 久久亚洲成人 | 国产成人精品无人区一区 | 北京一级毛片 | 日本a级一区| 亚洲精中文字幕二区三区 | 男男羞羞视频网站国产 | 色猫av| 久久久久99999| 日韩av片在线播放 | 日韩视频一区二区在线观看 | 国产精品久久国产精品 | 欧美一级做性受免费大片免费 | 91短视频在线 | 国内精品久久久久久2021浪潮 |