一、当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.

  1. /** Optional id for this factory, for serialization purposes */
  2. private String serializationId;
  3.  
  4. /** Whether to allow re-registration of a different definition with the same name */
  5. private boolean allowBeanDefinitionOverriding = true;
  6.  
  7. /** Whether to allow eager class loading even for lazy-init beans */
  8. private boolean allowEagerClassLoading = true;
  9.  
  10. /** Optional OrderComparator for dependency Lists and arrays */
  11. private Comparator<Object> dependencyComparator;
  12.  
  13. /** Resolver to use for checking if a bean definition is an autowire candidate */
  14. private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
  15.  
  16. /** Map from dependency type to corresponding autowired value */
  17. private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<Class<?>, Object>(16);
  18.  
  19. /** Map of bean definition objects, keyed by bean name */
  20. private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);
  21.  
  22. /** Map of singleton and non-singleton bean names, keyed by dependency type */
  23. private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
  24.  
  25. /** Map of singleton-only bean names, keyed by dependency type */
  26. private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
  27.  
  28. /** List of bean definition names, in registration order */
  29. private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);
  30.  
  31. /** List of names of manually registered singletons, in registration order */
  32. 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判断这个配置文件是不是使用了*或者?通配符的,代码如下。

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

如果有通配符就执行第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. 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*:

  1. protected String determineRootDir(String location) {
  2. int prefixEnd = location.indexOf(":") + 1;
  3. int rootDirEnd = location.length();
  4. while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
  5. rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
  6. }
  7. if (rootDirEnd == 0) {
  8. rootDirEnd = prefixEnd;
  9. }
  10. return location.substring(0, rootDirEnd);
  11. }

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

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

  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. }

先判断是否是以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方法

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

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的时候已经使用过了,这里就不再往里面跑了。这样配置文件的读取工作就搞定了,接下来的就是解析配置文件了。

方法调用的序列图

spring读取xml配置文件(二)的更多相关文章

  1. Spring读取xml配置文件的原理与实现

    本篇博文的目录: 一:前言 二:spring的配置文件 三:依赖的第三方库.使用技术.代码布局 四:Document实现 五:获取Element的实现 六:解析Element元素 七:Bean创造器 ...

  2. Spring 读取XML配置文件的两种方式

    import org.springframework.context.ApplicationContext; import org.springframework.context.support.Cl ...

  3. Spring如何读取xml配置文件的

    我们通过一个小案例来看xml解析过程. 1. 导包 <dependencies> <!-- xml解析工具 --> <dependency> <groupId ...

  4. Spring 通过XML配置文件以及通过注解形式来AOP 来实现前置,环绕,异常通知,返回后通知,后通知

    本节主要内容: 一.Spring 通过XML配置文件形式来AOP 来实现前置,环绕,异常通知     1. Spring AOP  前置通知 XML配置使用案例     2. Spring AOP   ...

  5. 【XML配置文件读取】使用jdom读取XML配置文件信息

    在项目中我们经常需要将配置信息写在配置文件中,而XML配置文件是常用的格式. 下面将介绍如何通过jdom来读取xml配置文件信息. 配置文件信息 <?xml version="1.0& ...

  6. spring读取prperties配置文件(2)

    接上篇,spring读取prperties配置文件(1),这一篇主要讲述spring如何用annotation的方式去读取自定义的配置文件. 这里我先定义好属性文件"user.propert ...

  7. JavaWeb_(Spring框架)xml配置文件

    系列博文 JavaWeb_(Spring框架)xml配置文件  传送门 JavaWeb_(Spring框架)注解配置 传送门 Xml配置 a)Bean元素:交由Spring管理的对象都要配置在bean ...

  8. 如何配置多个Spring的xml配置文件(多模块配置)

    如何使用多个Spring的xml配置文件(多模块配置) (2009-08-22 13:42:43)   如何使用多个Spring的xml配置文件(多模块配置) 在用Struts Spring Hibe ...

  9. spring读取prperties配置文件(1)

    博客地址http://www.cnblogs.com/shizhongtao/p/3438431.html 属性文件命名是*.properties,在java中,用类java.util.Propert ...

随机推荐

  1. Laravel --- 要点笔记

    一.路由: // 常规用法 Route::get('/',function(){ return 'get'; }) // 匹配多个 Route::match(['get','post'],'/',fu ...

  2. 关于CORS 应该注意的几点

    前言 对于跨域,随着w3c的CORS的出现,相比较于有些年头的jsonp,CORS以其简单安全,支持post的优势越来越收到大家的欢迎.具体如何CORS的原理和实现,直接推荐阮老师的文章,十分详细.本 ...

  3. 使用Python爬取微信公众号文章并保存为PDF文件(解决图片不显示的问题)

    前言 第一次写博客,主要内容是爬取微信公众号的文章,将文章以PDF格式保存在本地. 爬取微信公众号文章(使用wechatsogou) 1.安装 pip install wechatsogou --up ...

  4. CI框架使用(一)

    CI框架的使用是很简单,也 是mvc模式.其中有好多类直接调用.   在使用帮助函数的时候,都需要手动加载,或者是在配置文件中加一个自动加载 $this->load->helper('ur ...

  5. 长春理工大学第十四届程序设计竞赛(重现赛)L

    L.Homework Stream 题目链接:https://ac.nowcoder.com/acm/contest/912/L 题目 作为大珩班尖子生,小r每天有很多作业要完成,例如工图.工图和工图 ...

  6. lower_bound 和 upper_bound 功能和用法

    以前用这两个函数的时候,简单看了几句别人的博客,记住了大概,用的时候每用一次就弄混一次,相当难受,今天对照着这两个函数的源码和自己的尝试发现:其实这两个函数只能用于 "升序" 序列 ...

  7. 关于火狐浏览器设置cookie的一个问题

    最近发现我一个项目的网页,里面的cookie无法添加了,急的我瞪着我的PHP代码沉思了好久,我默认用的火狐浏览器,然而我默默的打开另一个叫360的浏览器,发现它的cookie是正常添加的. ... 难 ...

  8. POJ 2796:Feel Good(单调栈)

    http://poj.org/problem?id=2796 题意:给出n个数,问一个区间里面最小的元素*这个区间元素的和的最大值是多少. 思路:只想到了O(n^2)的做法. 参考了http://ww ...

  9. HTML标签--入门

    最近开始学习前端的知识,分享自己学的一点东西 <!DOCTYPE html> <!--HTML标识,,,用于告诉浏览器,这是一个HTML文档--> <html> & ...

  10. python之pip install

    安装方式1 wget  http://python-distribute.org/distribute_setup.py sudo python distribute_setup.py wget  h ...