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