spring读取xml配置文件(二)

一、当spring解析完配置文件名的占位符后,就开始refresh容器

 1 @Override
 2     public void refresh() throws BeansException, IllegalStateException {
 3         synchronized (this.startupShutdownMonitor) {
 4             // Prepare this context for refreshing.
 5               //设置了启动时间,激活状态设为true,初始化一些propertySource
 6               //初始化的时候啥都没做,是个空方法。设置状态为开启
 7             prepareRefresh();
 8 
 9             // Tell the subclass to refresh the internal bean factory.
10               //这个方法内部刷新了BeanFactory,如果BeanFactory存在,那么先销毁,然后
11               //重新创建新的BeanFactory
12             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
13 
14             // Prepare the bean factory for use in this context.
15             prepareBeanFactory(beanFactory);
16 
17             try {
18                 // Allows post-processing of the bean factory in context subclasses.
19                 postProcessBeanFactory(beanFactory);
20 
21                 // Invoke factory processors registered as beans in the context.
22                 invokeBeanFactoryPostProcessors(beanFactory);
23 
24                 // Register bean processors that intercept bean creation.
25                 registerBeanPostProcessors(beanFactory);
26 
27                 // Initialize message source for this context.
28                 initMessageSource();
29 
30                 // Initialize event multicaster for this context.
31                 initApplicationEventMulticaster();
32 
33                 // Initialize other special beans in specific context subclasses.
34                 onRefresh();
35 
36                 // Check for listener beans and register them.
37                 registerListeners();
38 
39                 // Instantiate all remaining (non-lazy-init) singletons.
40                 finishBeanFactoryInitialization(beanFactory);
41 
42                 // Last step: publish corresponding event.
43                 finishRefresh();
44             }
45 
46             catch (BeansException ex) {
47                 if (logger.isWarnEnabled()) {
48                     logger.warn("Exception encountered during context initialization - " +
49                             "cancelling refresh attempt: " + ex);
50                 }
51 
52                 // Destroy already created singletons to avoid dangling resources.
53                 destroyBeans();
54 
55                 // Reset 'active' flag.
56                 cancelRefresh(ex);
57 
58                 // Propagate exception to caller.
59                 throw ex;
60             }
61 
62             finally {
63                 // Reset common introspection caches in Spring's core, since we
64                 // might not ever need metadata for singleton beans anymore...
65                 resetCommonCaches();
66             }
67         }
68     }

第7行设置了容器启动的时间,容器的状态被修改为false,表示已经启动,并且初始化PropertySource,不过初始化PropertySource内部的代码是空的,什么都没做。

第12行代码是对BeanFactory进行刷新,它调用了refreBeanFactory方法它的代码如下

 1 @Override
 2     protected final void refreshBeanFactory() throws BeansException {
 3         if (hasBeanFactory()) {
 4             destroyBeans();
 5             closeBeanFactory();
 6         }
 7         try {
 8             DefaultListableBeanFactory beanFactory = createBeanFactory();
 9             beanFactory.setSerializationId(getId());
10             customizeBeanFactory(beanFactory);
11             loadBeanDefinitions(beanFactory);
12             synchronized (this.beanFactoryMonitor) {
13                 this.beanFactory = beanFactory;
14             }
15         }
16         catch (IOException ex) {
17             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
18         }
19     }

首先判断这个Bean工厂是否已经存在,如果存在了就直接销毁,重新创建一个DefaultListableBeanFactory工厂,

这个工厂在创建的时候初始化了许多的容器字段,如是否可以在覆盖同名不同定义的bean定义,beanName->beanDefinition容器and so on.

    /** Optional id for this factory, for serialization purposes */
    private String serializationId;

    /** Whether to allow re-registration of a different definition with the same name */
    private boolean allowBeanDefinitionOverriding = true;

    /** Whether to allow eager class loading even for lazy-init beans */
    private boolean allowEagerClassLoading = true;

    /** Optional OrderComparator for dependency Lists and arrays */
    private Comparator<Object> dependencyComparator;

    /** Resolver to use for checking if a bean definition is an autowire candidate */
    private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();

    /** Map from dependency type to corresponding autowired value */
    private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<Class<?>, Object>(16);

    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

    /** Map of singleton and non-singleton bean names, keyed by dependency type */
    private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);

    /** Map of singleton-only bean names, keyed by dependency type */
    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);

    /** List of bean definition names, in registration order */
    private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);

    /** List of names of manually registered singletons, in registration order */
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16);


并且给这个Bean工厂设置序列化的id,这个序列化ID是工厂类的全限定名+@+这个工厂实例的hashcode

第10行customizeBeanFactory方法是在AbstractRefreshableApplicationContext类中定义的它的代码如下

if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}

如果这个应用上下文没有定义能否bean定义覆盖和循环依赖这两个属性就使用默认值,默认值都是为true

第11行代码loadBeanDefinitions(beanFactory)创建了的XmlBeanDefinitionReader实例,它的继承结构,继承自抽象的bean定义阅读器

这个方法内部的代码如下

 1 @Override
 2     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 3         // Create a new XmlBeanDefinitionReader for the given BeanFactory.
 4         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 5 
 6         // Configure the bean definition reader with this context's
 7         // resource loading environment.
 8         beanDefinitionReader.setEnvironment(this.getEnvironment());
 9         beanDefinitionReader.setResourceLoader(this);
10         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
11 
12         // Allow a subclass to provide custom initialization of the reader,
13         // then proceed with actually loading the bean definitions.
14         initBeanDefinitionReader(beanDefinitionReader);
15         loadBeanDefinitions(beanDefinitionReader);
16     }

第8行将ClasspathXmlApplicationContext中的环境变量设置到新创建的bean定义阅读器,把ClasspathXmlApplicationContext(它继承了ResourceLoader接口)资源加载器设置进去,设置实体解析器,这个解析器,实现的接口时EntityResolver,它的实现类有好多,这里设置的是一个SAX解析器,用于读取xml,这里绕来绕去的,看下ClasspathXmlApplicationContext的继承结构.

 第14行初始化bean定义阅读器,它主要是对这个xml阅读器进行了验证模式的设置,是自动验证还是不验证

第15行传入xml阅读器,准备读取xml,这个方法的代码如下

 1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
 2         Resource[] configResources = getConfigResources();
 3         if (configResources != null) {
 4             reader.loadBeanDefinitions(configResources);
 5         }
 6         String[] configLocations = getConfigLocations();
 7         if (configLocations != null) {
 8             reader.loadBeanDefinitions(configLocations);
 9         }
10     }

 第8行将配置文件传入阅读器,loadBeanDefinitions方法的代码如下

 1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
 2         ResourceLoader resourceLoader = getResourceLoader();
 3         if (resourceLoader == null) {
 4             throw new BeanDefinitionStoreException(
 5                     "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
 6         }
 7 
 8         if (resourceLoader instanceof ResourcePatternResolver) {
 9             // Resource pattern matching available.
10             try {
11                 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
12                 int loadCount = loadBeanDefinitions(resources);
13                 if (actualResources != null) {
14                     for (Resource resource : resources) {
15                         actualResources.add(resource);
16                     }
17                 }
18                 if (logger.isDebugEnabled()) {
19                     logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
20                 }
21                 return loadCount;
22             }
23             catch (IOException ex) {
24                 throw new BeanDefinitionStoreException(
25                         "Could not resolve bean definition resource pattern [" + location + "]", ex);
26             }
27         }
28         else {
29             // Can only load single resources by absolute URL.
30             Resource resource = resourceLoader.getResource(location);
31             int loadCount = loadBeanDefinitions(resource);
32             if (actualResources != null) {
33                 actualResources.add(resource);
34             }
35             if (logger.isDebugEnabled()) {
36                 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
37             }
38             return loadCount;
39         }
40     }

第2行这个resourceLoader 是之前创建XmlBeanDefinitionReader这个xml阅读器传进来的ClasspathXmlApplictionContext,这个类,在上面的图已经告诉了我们它是ResourceLoader的子类

第11行开始进入资源的解析,也就是对配置文件的路径找到所有的xml,并与之匹配,详细情况继续往下

1 @Override
2     public Resource[] getResources(String locationPattern) throws IOException {
3         return this.resourcePatternResolver.getResources(locationPattern);
4     }

第3行的resourcePatternResolver是一个PathMatchingResourcePatternResolver的实例,这个实例在创建ClasspathXmlApplicationContext的时候调用父类AbstractApplicationContext时被创建。这些路径匹配器的继承结构为

继续进入PathMatchingResourcePatternResolver的getResources方法

 1 @Override
 2     public Resource[] getResources(String locationPattern) throws IOException {
 3         Assert.notNull(locationPattern, "Location pattern must not be null");
 4         if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
 5             // a class path resource (multiple resources for same name possible)
 6             if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
 7                 // a class path resource pattern
 8                 return findPathMatchingResources(locationPattern);
 9             }
10             else {
11                 // all class path resources with the given name
12                 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
13             }
14         }
15         else {
16             // Only look for a pattern after a prefix here
17             // (to not get fooled by a pattern symbol in a strange prefix).
18             int prefixEnd = locationPattern.indexOf(":") + 1;
19             if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
20                 // a file pattern
21                 return findPathMatchingResources(locationPattern);
22             }
23             else {
24                 // a single resource with the given name
25                 return new Resource[] {getResourceLoader().getResource(locationPattern)};
26             }
27         }
28     }

第4行中的CLASSPATH_ALL_URL_PREFIX的值为classpath*:,这里是判断这个配置文件是不是以classpath*:开头的,如果不是那么就跳到第18行,取得:后面的字符串,如:classpath:spring/spring-*.xml或者spring/spring-*.xml最终都会得到spring/spring-*.xml这个字符串。如果这个字符串不含通配符,那么直接包装成resource数组返回。

第6行getPathMatcher()获得一个AntPathMatcher实例调用isPattern判断这个配置文件是不是使用了*或者?通配符的,代码如下。

@Override
    public boolean isPattern(String path) {
        return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
     }

如果有通配符就执行第8行,如果没有就执行第12行,

第12行的findAllClassPathResources方法,获取各加载器的类路径下的所有名字匹配的配置文件,假如配置文件叫做classpath*:spring/spring-context.xml,那么它先从父加载器中找。

它调用的classLoader的getResoures方法,代码如下:

 1 protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
 2         Set<Resource> result = new LinkedHashSet<Resource>(16);
 3         ClassLoader cl = getClassLoader();
 4         Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
 5         while (resourceUrls.hasMoreElements()) {
 6             URL url = resourceUrls.nextElement();
 7             result.add(convertClassLoaderURL(url));
 8         }
 9         if ("".equals(path)) {
10             // The above result is likely to be incomplete, i.e. only containing file system references.
11             // We need to have pointers to each of the jar files on the classpath as well...
12             addAllClassLoaderJarRoots(cl, result);
13         }
14         return result;
15     }

第12行addAllClassLoaderJarRoots方法是在只写了classpath*:前缀,却没有指定配置文件的情况下调用,它还会去寻找jar文件,然后包装成resource返回

第4行就调用了getResources方法,看看getResources方法的代码,parent值得是父加载器。

 1 public Enumeration<URL> getResources(String name) throws IOException {
 2         @SuppressWarnings("unchecked")
 3         Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
 4         if (parent != null) {
 5             tmp[0] = parent.getResources(name);
 6         } else {
 7             tmp[0] = getBootstrapResources(name);
 8         }
 9         tmp[1] = findResources(name);
10 
11         return new CompoundEnumeration<>(tmp);
12     }

如果配置文件的路径里有通配符,那么会进入findPathMatchingResources方法

 1 protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
 2         String rootDirPath = determineRootDir(locationPattern);
 3         String subPattern = locationPattern.substring(rootDirPath.length());
 4         Resource[] rootDirResources = getResources(rootDirPath);
 5         Set<Resource> result = new LinkedHashSet<Resource>(16);
 6         for (Resource rootDirResource : rootDirResources) {
 7             rootDirResource = resolveRootDirResource(rootDirResource);
 8             if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
 9                 result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
10             }
11             else if (isJarResource(rootDirResource)) {
12                 result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
13             }
14             else {
15                 result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
16             }
17         }
18         if (logger.isDebugEnabled()) {
19             logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
20         }
21         return result.toArray(new Resource[result.size()]);
22     }

第二行的determineRootDir方法的代码如下,仍然要判断是否是通配符的配置文件,如果是那么寻找最后一个/,如果找到就截取这个/前面的一段字符串,如:classpath*:spring/spring-*context.xml,就会被获取到classpath*:spring/这一段,如果没有/就取到classpath*:

protected String determineRootDir(String location) {
        int prefixEnd = location.indexOf(":") + 1;
        int rootDirEnd = location.length();
        while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
            rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
        }
        if (rootDirEnd == 0) {
            rootDirEnd = prefixEnd;
        }
        return location.substring(0, rootDirEnd);
    }

第3行表示截取通配符那一段,如classpath*:spring/spring-*.xml,会获取到spring-*.xml

第4行是从rootDir下找,如classpath*:spring/下找所有的配置文件,这段代码上面分析过,不过可以长话短说的再分析一遍,假设当前的locationPattern就是classpath*:spring/

 @Override
     public Resource[] getResources(String locationPattern) throws IOException {
         Assert.notNull(locationPattern, "Location pattern must not be null");
         if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
             // a class path resource (multiple resources for same name possible)
             if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                 // a class path resource pattern
                 return findPathMatchingResources(locationPattern);
             }
             else {
                 // all class path resources with the given name
                 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
             }
         }
         else {
             // Only look for a pattern after a prefix here
             // (to not get fooled by a pattern symbol in a strange prefix).
             int prefixEnd = locationPattern.indexOf(":") + 1;
             if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                 // a file pattern
                 return findPathMatchingResources(locationPattern);
             }
             else {
                 // a single resource with the given name
                 return new Resource[] {getResourceLoader().getResource(locationPattern)};
             }
         }
     }

先判断是否是以classpath*:,如果是,那再判断带不带通配符,如果不带,直接调用findAllClassPathResources方法,调用所有的加载器,先从父类开始,在所有的加载器的类路径包装成resource。

第6行循环根路径开始寻找配置文件了

第7行对路径进行解析,判断是否为OSGI类型的路径

第8行,判断是不是vfs协议的文件资源路径

第11行判断是不是jar类型的资源路径

第15行开始查找了,只要看到do开头的方法,基本就是开始真正开始办事了

 1 protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
 2             throws IOException {
 3 
 4         File rootDir;
 5         try {
 6             rootDir = rootDirResource.getFile().getAbsoluteFile();
 7         }
 8         catch (IOException ex) {
 9             if (logger.isWarnEnabled()) {
10                 logger.warn("Cannot search for matching files underneath " + rootDirResource +
11                         " because it does not correspond to a directory in the file system", ex);
12             }
13             return Collections.emptySet();
14         }
15         return doFindMatchingFileSystemResources(rootDir, subPattern);
16     }

第6行拿到这个根路径的绝对地址,最后再第15行调用doFindMatchingFileSystemResources,传入了俩个参数,一个是根路径,另一个就含通配符的那个字符串

 1 protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
 2         if (logger.isDebugEnabled()) {
 3             logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
 4         }
 5         Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
 6         Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
 7         for (File file : matchingFiles) {
 8             result.add(new FileSystemResource(file));
 9         }
10         return result;
11     }

直接看第5行的retrieveMatchingFiles方法代码

 1 protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
 2         if (!rootDir.exists()) {
 3             // Silently skip non-existing directories.
 4             if (logger.isDebugEnabled()) {
 5                 logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");
 6             }
 7             return Collections.emptySet();
 8         }
 9         if (!rootDir.isDirectory()) {
10             // Complain louder if it exists but is no directory.
11             if (logger.isWarnEnabled()) {
12                 logger.warn("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");
13             }
14             return Collections.emptySet();
15         }
16         if (!rootDir.canRead()) {
17             if (logger.isWarnEnabled()) {
18                 logger.warn("Cannot search for matching files underneath directory [" + rootDir.getAbsolutePath() +
19                         "] because the application is not allowed to read the directory");
20             }
21             return Collections.emptySet();
22         }
23         String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
24         if (!pattern.startsWith("/")) {
25             fullPattern += "/";
26         }
27         fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
28         Set<File> result = new LinkedHashSet<File>(8);
29         doRetrieveMatchingFiles(fullPattern, rootDir, result);
30         return result;
31     }

如果根路径不存在或者是个目录或者是不可读的,那么直接返回一个空的集合

第23行,将\替换成/

第28行,将绝对根路径和通配符字符串连接起来

第29行,我们又看到do开头的方法了,好高兴,进去看看,fullPattern 是全路径,dir是根路径,result是用来装资源的

 1 protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
 2         if (logger.isDebugEnabled()) {
 3             logger.debug("Searching directory [" + dir.getAbsolutePath() +
 4                     "] for files matching pattern [" + fullPattern + "]");
 5         }
 6         File[] dirContents = dir.listFiles();
 7         if (dirContents == null) {
 8             if (logger.isWarnEnabled()) {
 9                 logger.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
10             }
11             return;
12         }
13         for (File content : dirContents) {
14             String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
15             if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
16                 if (!content.canRead()) {
17                     if (logger.isDebugEnabled()) {
18                         logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
19                                 "] because the application is not allowed to read the directory");
20                     }
21                 }
22                 else {
23                     doRetrieveMatchingFiles(fullPattern, content, result);
24                 }
25             }
26             if (getPathMatcher().match(fullPattern, currPath)) {
27                 result.add(content);
28             }
29         }
30     }

第6行得到了根路径下的所有文件,循环这些file,如果是目录,那么继续匹配,调用matchStart方法,这个方法里面又调用了doMatch方法

  1 protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
  2         if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
  3             return false;
  4         }
  5 
  6         String[] pattDirs = tokenizePattern(pattern);
  7         String[] pathDirs = tokenizePath(path);
  8 
  9         int pattIdxStart = 0;
 10         int pattIdxEnd = pattDirs.length - 1;
 11         int pathIdxStart = 0;
 12         int pathIdxEnd = pathDirs.length - 1;
 13 
 14         // Match all elements up to the first **
 15         while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
 16             String pattDir = pattDirs[pattIdxStart];
 17             if ("**".equals(pattDir)) {
 18                 break;
 19             }
 20             if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
 21                 return false;
 22             }
 23             pattIdxStart++;
 24             pathIdxStart++;
 25         }
 26 
 27         if (pathIdxStart > pathIdxEnd) {
 28             // Path is exhausted, only match if rest of pattern is * or **'s
 29             if (pattIdxStart > pattIdxEnd) {
 30                 return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) :
 31                         !path.endsWith(this.pathSeparator));
 32             }
 33             if (!fullMatch) {
 34                 return true;
 35             }
 36             if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
 37                 return true;
 38             }
 39             for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
 40                 if (!pattDirs[i].equals("**")) {
 41                     return false;
 42                 }
 43             }
 44             return true;
 45         }
 46         else if (pattIdxStart > pattIdxEnd) {
 47             // String not exhausted, but pattern is. Failure.
 48             return false;
 49         }
 50         else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
 51             // Path start definitely matches due to "**" part in pattern.
 52             return true;
 53         }
 54 
 55         // up to last '**'
 56         while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
 57             String pattDir = pattDirs[pattIdxEnd];
 58             if (pattDir.equals("**")) {
 59                 break;
 60             }
 61             if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
 62                 return false;
 63             }
 64             pattIdxEnd--;
 65             pathIdxEnd--;
 66         }
 67         if (pathIdxStart > pathIdxEnd) {
 68             // String is exhausted
 69             for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
 70                 if (!pattDirs[i].equals("**")) {
 71                     return false;
 72                 }
 73             }
 74             return true;
 75         }
 76 
 77         while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
 78             int patIdxTmp = -1;
 79             for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
 80                 if (pattDirs[i].equals("**")) {
 81                     patIdxTmp = i;
 82                     break;
 83                 }
 84             }
 85             if (patIdxTmp == pattIdxStart + 1) {
 86                 // '**/**' situation, so skip one
 87                 pattIdxStart++;
 88                 continue;
 89             }
 90             // Find the pattern between padIdxStart & padIdxTmp in str between
 91             // strIdxStart & strIdxEnd
 92             int patLength = (patIdxTmp - pattIdxStart - 1);
 93             int strLength = (pathIdxEnd - pathIdxStart + 1);
 94             int foundIdx = -1;
 95 
 96             strLoop:
 97             for (int i = 0; i <= strLength - patLength; i++) {
 98                 for (int j = 0; j < patLength; j++) {
 99                     String subPat = pattDirs[pattIdxStart + j + 1];
100                     String subStr = pathDirs[pathIdxStart + i + j];
101                     if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
102                         continue strLoop;
103                     }
104                 }
105                 foundIdx = pathIdxStart + i;
106                 break;
107             }
108 
109             if (foundIdx == -1) {
110                 return false;
111             }
112 
113             pattIdxStart = patIdxTmp;
114             pathIdxStart = foundIdx + patLength;
115         }
116 
117         for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
118             if (!pattDirs[i].equals("**")) {
119                 return false;
120             }
121         }
122 
123         return true;
124     }

第6,7行将路径按照/进行分割成字符串数组

第17行,如果发现通配符出现**,那么退出循环,跳到第50行判断是不是全匹配,如果不是全匹配那么就返回true

如果fullMatcher为true,那么就跳到56行,从后往前匹配,如果中间出现任何不匹配的就返回false,如果都匹配,那么会匹配到**,那么又退出循环,跳到第117行,此时可以确定返回true了

如果没有通配符的情况下,并且都验证通过,那么会进入第21行进行验证,得看看匹配路径是否是目录,如果是目录那么就匹配失败。

第61行,这里会把通配符*和?替换成正则表达式,替换的逻辑代码如下:

 1 public AntPathStringMatcher(String pattern, boolean caseSensitive) {
 2             StringBuilder patternBuilder = new StringBuilder();
 3             Matcher matcher = GLOB_PATTERN.matcher(pattern);
 4             int end = 0;
 5             while (matcher.find()) {
 6                 patternBuilder.append(quote(pattern, end, matcher.start()));
 7                 String match = matcher.group();
 8                 if ("?".equals(match)) {
 9                     patternBuilder.append('.');
10                 }
11                 else if ("*".equals(match)) {
12                     patternBuilder.append(".*");
13                 }
14                 else if (match.startsWith("{") && match.endsWith("}")) {
15                     int colonIdx = match.indexOf(':');
16                     if (colonIdx == -1) {
17                         patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
18                         this.variableNames.add(matcher.group(1));
19                     }
20                     else {
21                         String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
22                         patternBuilder.append('(');
23                         patternBuilder.append(variablePattern);
24                         patternBuilder.append(')');
25                         String variableName = match.substring(1, colonIdx);
26                         this.variableNames.add(variableName);
27                     }
28                 }
29                 end = matcher.end();
30             }
31             patternBuilder.append(quote(pattern, end, pattern.length()));
32             this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
33                     Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
34         }

 第6行将截取从0开始到找到第一个匹配的子字符串的位置的字符串,并加上单引号,如:spring-*.xml 会截取到'spring-'这样的字符串

第七行获得匹配的字符串,这里是*

下面进行了判断,如果是*,那么就替换成.*追加到字符串后面

找到匹配的配置文件后,就添加到一个set集合中

最后我们再回到doFindMatchingFileSystemResources方法

 1 protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
 2         if (logger.isDebugEnabled()) {
 3             logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
 4         }
 5         Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
 6         Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
 7         for (File file : matchingFiles) {
 8             result.add(new FileSystemResource(file));
 9         }
10         return result;
11     }

第8表示这些文件都被打包成resource加到set集合里,最后一只返回到AbstractBeanDefinition类的loadBeanDefinitions方法

 1 public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
 2         ResourceLoader resourceLoader = getResourceLoader();
 3         if (resourceLoader == null) {
 4             throw new BeanDefinitionStoreException(
 5                     "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
 6         }
 7 
 8         if (resourceLoader instanceof ResourcePatternResolver) {
 9             // Resource pattern matching available.
10             try {
11                 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
12                 int loadCount = loadBeanDefinitions(resources);
13                 if (actualResources != null) {
14                     for (Resource resource : resources) {
15                         actualResources.add(resource);
16                     }
17                 }
18                 if (logger.isDebugEnabled()) {
19                     logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
20                 }
21                 return loadCount;
22             }
23             catch (IOException ex) {
24                 throw new BeanDefinitionStoreException(
25                         "Could not resolve bean definition resource pattern [" + location + "]", ex);
26             }
27         }
28         else {
29             // Can only load single resources by absolute URL.
30             Resource resource = resourceLoader.getResource(location);
31             int loadCount = loadBeanDefinitions(resource);
32             if (actualResources != null) {
33                 actualResources.add(resource);
34             }
35             if (logger.isDebugEnabled()) {
36                 logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
37             }
38             return loadCount;
39         }
40     }

第12行,调用了重载方法loadBeanDefinitions(Resource[]),方法调用一直进入到了XmlBeanDefinitionReader类中的loadBeanDefinitions方法

 1     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 2         Assert.notNull(encodedResource, "EncodedResource must not be null");
 3         if (logger.isInfoEnabled()) {
 4             logger.info("Loading XML bean definitions from " + encodedResource.getResource());
 5         }
 6 
 7         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
 8         if (currentResources == null) {
 9             currentResources = new HashSet<EncodedResource>(4);
10             this.resourcesCurrentlyBeingLoaded.set(currentResources);
11         }
12         if (!currentResources.add(encodedResource)) {
13             throw new BeanDefinitionStoreException(
14                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
15         }
16         try {
17             InputStream inputStream = encodedResource.getResource().getInputStream();
18             try {
19                 InputSource inputSource = new InputSource(inputStream);
20                 if (encodedResource.getEncoding() != null) {
21                     inputSource.setEncoding(encodedResource.getEncoding());
22                 }
23                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
24             }
25             finally {
26                 inputStream.close();
27             }
28         }
29         catch (IOException ex) {
30             throw new BeanDefinitionStoreException(
31                     "IOException parsing XML document from " + encodedResource.getResource(), ex);
32         }
33         finally {
34             currentResources.remove(encodedResource);
35             if (currentResources.isEmpty()) {
36                 this.resourcesCurrentlyBeingLoaded.remove();
37             }
38         }
39     }

第17行,拿到这个配置文件的输入流,并包装成InputSource,这个时候我们又看到了do开头的方法doLoadBeanDefinitions,它的代码为

1 try {
2             Document doc = doLoadDocument(inputSource, resource);
3             return registerBeanDefinitions(doc, resource);
4         }

看到第二行,这个配置文件被加载成了Document对象,在进入doLoadDocument方法

1 protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
2         return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
3                 getValidationModeForResource(resource), isNamespaceAware());
4     }

看到第3行,获得验证模式,它是根据这个xml的是否以DOCTYPE开头来确定时dtd验证还是xsd验证

 1 public int detectValidationMode(InputStream inputStream) throws IOException {
 2         // Peek into the file to look for DOCTYPE.
 3         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
 4         try {
 5             boolean isDtdValidated = false;
 6             String content;
 7             while ((content = reader.readLine()) != null) {
 8                 content = consumeCommentTokens(content);
 9                 if (this.inComment || !StringUtils.hasText(content)) {
10                     continue;
11                 }
12                 if (hasDoctype(content)) {
13                     isDtdValidated = true;
14                     break;
15                 }
16                 if (hasOpeningTag(content)) {
17                     // End of meaningful data...
18                     break;
19                 }
20             }
21             return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
22         }
23         catch (CharConversionException ex) {
24             // Choked on some character encoding...
25             // Leave the decision up to the caller.
26             return VALIDATION_AUTO;
27         }
28         finally {
29             reader.close();
30         }
31     }

第12行的hasDoctype方法

private boolean hasDoctype(String content) {
        return content.contains(DOCTYPE);
    }

DOCTYPE的值就是DOCTYPE字符串

看看document是怎么创建的

 1 @Override
 2     public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
 3             ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
 4 
 5         DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
 6         if (logger.isDebugEnabled()) {
 7             logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
 8         }
 9         DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
10         return builder.parse(inputSource);
11     }

首先在第5行创建了一个文档构建工厂,然后通过工厂创建构建者,最后用构建者解析输入资源,返回一个文档对象,这些类是JDK中javax.xml.parses包下的类,我们学习解析xml的时候已经使用过了,这里就不再往里面跑了。这样配置文件的读取工作就搞定了,接下来的就是解析配置文件了。

方法调用的序列图

原文地址:https://www.cnblogs.com/honger/p/6815941.html