spring读取xml配置文件(二)
一、当spring解析完配置文件名的占位符后,就开始refresh容器
- @Override
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- //设置了启动时间,激活状态设为true,初始化一些propertySource
- //初始化的时候啥都没做,是个空方法。设置状态为开启
- prepareRefresh();
- // Tell the subclass to refresh the internal bean factory.
- //这个方法内部刷新了BeanFactory,如果BeanFactory存在,那么先销毁,然后
- //重新创建新的BeanFactory
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // Prepare the bean factory for use in this context.
- prepareBeanFactory(beanFactory);
- try {
- // Allows post-processing of the bean factory in context subclasses.
- postProcessBeanFactory(beanFactory);
- // Invoke factory processors registered as beans in the context.
- invokeBeanFactoryPostProcessors(beanFactory);
- // Register bean processors that intercept bean creation.
- registerBeanPostProcessors(beanFactory);
- // Initialize message source for this context.
- initMessageSource();
- // Initialize event multicaster for this context.
- initApplicationEventMulticaster();
- // Initialize other special beans in specific context subclasses.
- onRefresh();
- // Check for listener beans and register them.
- registerListeners();
- // Instantiate all remaining (non-lazy-init) singletons.
- finishBeanFactoryInitialization(beanFactory);
- // Last step: publish corresponding event.
- finishRefresh();
- }
- catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Exception encountered during context initialization - " +
- "cancelling refresh attempt: " + ex);
- }
- // Destroy already created singletons to avoid dangling resources.
- destroyBeans();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- }
- finally {
- // Reset common introspection caches in Spring's core, since we
- // might not ever need metadata for singleton beans anymore...
- resetCommonCaches();
- }
- }
- }
第7行设置了容器启动的时间,容器的状态被修改为false,表示已经启动,并且初始化PropertySource,不过初始化PropertySource内部的代码是空的,什么都没做。
第12行代码是对BeanFactory进行刷新,它调用了refreBeanFactory方法它的代码如下
- @Override
- protected final void refreshBeanFactory() throws BeansException {
- if (hasBeanFactory()) {
- destroyBeans();
- closeBeanFactory();
- }
- try {
- DefaultListableBeanFactory beanFactory = createBeanFactory();
- beanFactory.setSerializationId(getId());
- customizeBeanFactory(beanFactory);
- loadBeanDefinitions(beanFactory);
- synchronized (this.beanFactoryMonitor) {
- this.beanFactory = beanFactory;
- }
- }
- catch (IOException ex) {
- throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
- }
- }
首先判断这个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定义阅读器
这个方法内部的代码如下
- @Override
- protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
- // Create a new XmlBeanDefinitionReader for the given BeanFactory.
- XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
- // Configure the bean definition reader with this context's
- // resource loading environment.
- beanDefinitionReader.setEnvironment(this.getEnvironment());
- beanDefinitionReader.setResourceLoader(this);
- beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
- // Allow a subclass to provide custom initialization of the reader,
- // then proceed with actually loading the bean definitions.
- initBeanDefinitionReader(beanDefinitionReader);
- loadBeanDefinitions(beanDefinitionReader);
- }
第8行将ClasspathXmlApplicationContext中的环境变量设置到新创建的bean定义阅读器,把ClasspathXmlApplicationContext(它继承了ResourceLoader接口)资源加载器设置进去,设置实体解析器,这个解析器,实现的接口时EntityResolver,它的实现类有好多,这里设置的是一个SAX解析器,用于读取xml,这里绕来绕去的,看下ClasspathXmlApplicationContext的继承结构.
第14行初始化bean定义阅读器,它主要是对这个xml阅读器进行了验证模式的设置,是自动验证还是不验证
第15行传入xml阅读器,准备读取xml,这个方法的代码如下
- protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
- Resource[] configResources = getConfigResources();
- if (configResources != null) {
- reader.loadBeanDefinitions(configResources);
- }
- String[] configLocations = getConfigLocations();
- if (configLocations != null) {
- reader.loadBeanDefinitions(configLocations);
- }
- }
第8行将配置文件传入阅读器,loadBeanDefinitions方法的代码如下
- public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
- ResourceLoader resourceLoader = getResourceLoader();
- if (resourceLoader == null) {
- throw new BeanDefinitionStoreException(
- "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
- }
- if (resourceLoader instanceof ResourcePatternResolver) {
- // Resource pattern matching available.
- try {
- Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
- int loadCount = loadBeanDefinitions(resources);
- if (actualResources != null) {
- for (Resource resource : resources) {
- actualResources.add(resource);
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
- }
- return loadCount;
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException(
- "Could not resolve bean definition resource pattern [" + location + "]", ex);
- }
- }
- else {
- // Can only load single resources by absolute URL.
- Resource resource = resourceLoader.getResource(location);
- int loadCount = loadBeanDefinitions(resource);
- if (actualResources != null) {
- actualResources.add(resource);
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
- }
- return loadCount;
- }
- }
第2行这个resourceLoader 是之前创建XmlBeanDefinitionReader这个xml阅读器传进来的ClasspathXmlApplictionContext,这个类,在上面的图已经告诉了我们它是ResourceLoader的子类
第11行开始进入资源的解析,也就是对配置文件的路径找到所有的xml,并与之匹配,详细情况继续往下
- @Override
- public Resource[] getResources(String locationPattern) throws IOException {
- return this.resourcePatternResolver.getResources(locationPattern);
- }
第3行的resourcePatternResolver是一个PathMatchingResourcePatternResolver的实例,这个实例在创建ClasspathXmlApplicationContext的时候调用父类AbstractApplicationContext时被创建。这些路径匹配器的继承结构为
继续进入PathMatchingResourcePatternResolver的getResources方法
- @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)};
- }
- }
- }
第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方法,代码如下:
- protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
- Set<Resource> result = new LinkedHashSet<Resource>(16);
- ClassLoader cl = getClassLoader();
- Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
- while (resourceUrls.hasMoreElements()) {
- URL url = resourceUrls.nextElement();
- result.add(convertClassLoaderURL(url));
- }
- if ("".equals(path)) {
- // The above result is likely to be incomplete, i.e. only containing file system references.
- // We need to have pointers to each of the jar files on the classpath as well...
- addAllClassLoaderJarRoots(cl, result);
- }
- return result;
- }
第12行addAllClassLoaderJarRoots方法是在只写了classpath*:前缀,却没有指定配置文件的情况下调用,它还会去寻找jar文件,然后包装成resource返回
第4行就调用了getResources方法,看看getResources方法的代码,parent值得是父加载器。
- public Enumeration<URL> getResources(String name) throws IOException {
- @SuppressWarnings("unchecked")
- Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
- if (parent != null) {
- tmp[0] = parent.getResources(name);
- } else {
- tmp[0] = getBootstrapResources(name);
- }
- tmp[1] = findResources(name);
- return new CompoundEnumeration<>(tmp);
- }
如果配置文件的路径里有通配符,那么会进入findPathMatchingResources方法
- protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
- String rootDirPath = determineRootDir(locationPattern);
- String subPattern = locationPattern.substring(rootDirPath.length());
- Resource[] rootDirResources = getResources(rootDirPath);
- Set<Resource> result = new LinkedHashSet<Resource>(16);
- for (Resource rootDirResource : rootDirResources) {
- rootDirResource = resolveRootDirResource(rootDirResource);
- 8 if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
- result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
- }
- else if (isJarResource(rootDirResource)) {
- result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
- }
- else {
- result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
- }
- return result.toArray(new Resource[result.size()]);
- }
第二行的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开头的方法,基本就是开始真正开始办事了
- protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
- throws IOException {
- File rootDir;
- try {
- rootDir = rootDirResource.getFile().getAbsoluteFile();
- }
- catch (IOException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Cannot search for matching files underneath " + rootDirResource +
- " because it does not correspond to a directory in the file system", ex);
- }
- return Collections.emptySet();
- }
- return doFindMatchingFileSystemResources(rootDir, subPattern);
- }
第6行拿到这个根路径的绝对地址,最后再第15行调用doFindMatchingFileSystemResources,传入了俩个参数,一个是根路径,另一个就含通配符的那个字符串
- protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
- if (logger.isDebugEnabled()) {
- logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
- }
- Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
- Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
- for (File file : matchingFiles) {
- result.add(new FileSystemResource(file));
- }
- return result;
- }
直接看第5行的retrieveMatchingFiles方法代码
- protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
- if (!rootDir.exists()) {
- // Silently skip non-existing directories.
- if (logger.isDebugEnabled()) {
- logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");
- }
- return Collections.emptySet();
- }
- if (!rootDir.isDirectory()) {
- // Complain louder if it exists but is no directory.
- if (logger.isWarnEnabled()) {
- logger.warn("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");
- }
- return Collections.emptySet();
- }
- if (!rootDir.canRead()) {
- if (logger.isWarnEnabled()) {
- logger.warn("Cannot search for matching files underneath directory [" + rootDir.getAbsolutePath() +
- "] because the application is not allowed to read the directory");
- }
- return Collections.emptySet();
- }
- String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
- if (!pattern.startsWith("/")) {
- fullPattern += "/";
- }
- fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
- Set<File> result = new LinkedHashSet<File>(8);
- doRetrieveMatchingFiles(fullPattern, rootDir, result);
- return result;
- }
如果根路径不存在或者是个目录或者是不可读的,那么直接返回一个空的集合
第23行,将\\替换成/
第28行,将绝对根路径和通配符字符串连接起来
第29行,我们又看到do开头的方法了,好高兴,进去看看,fullPattern 是全路径,dir是根路径,result是用来装资源的
- protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
- if (logger.isDebugEnabled()) {
- logger.debug("Searching directory [" + dir.getAbsolutePath() +
- "] for files matching pattern [" + fullPattern + "]");
- }
- File[] dirContents = dir.listFiles();
- if (dirContents == null) {
- if (logger.isWarnEnabled()) {
- logger.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
- }
- return;
- }
- for (File content : dirContents) {
- String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
- if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
- if (!content.canRead()) {
- if (logger.isDebugEnabled()) {
- logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
- "] because the application is not allowed to read the directory");
- }
- }
- else {
- doRetrieveMatchingFiles(fullPattern, content, result);
- }
- }
- if (getPathMatcher().match(fullPattern, currPath)) {
- result.add(content);
- }
- }
- }
第6行得到了根路径下的所有文件,循环这些file,如果是目录,那么继续匹配,调用matchStart方法,这个方法里面又调用了doMatch方法
- protected boolean doMatch(String pattern, String path, boolean fullMatch, Map<String, String> uriTemplateVariables) {
- if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
- return false;
- }
- String[] pattDirs = tokenizePattern(pattern);
- String[] pathDirs = tokenizePath(path);
- int pattIdxStart = 0;
- int pattIdxEnd = pattDirs.length - 1;
- int pathIdxStart = 0;
- int pathIdxEnd = pathDirs.length - 1;
- // Match all elements up to the first **
- while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
- String pattDir = pattDirs[pattIdxStart];
- if ("**".equals(pattDir)) {
- break;
- }
- if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) {
- return false;
- }
- pattIdxStart++;
- pathIdxStart++;
- }
- if (pathIdxStart > pathIdxEnd) {
- // Path is exhausted, only match if rest of pattern is * or **'s
- if (pattIdxStart > pattIdxEnd) {
- return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) :
- !path.endsWith(this.pathSeparator));
- }
- if (!fullMatch) {
- return true;
- }
- if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) {
- return true;
- }
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
- if (!pattDirs[i].equals("**")) {
- return false;
- }
- }
- return true;
- }
- else if (pattIdxStart > pattIdxEnd) {
- // String not exhausted, but pattern is. Failure.
- return false;
- }
- else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
- // Path start definitely matches due to "**" part in pattern.
- return true;
- }
- // up to last '**'
- while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
- String pattDir = pattDirs[pattIdxEnd];
- if (pattDir.equals("**")) {
- break;
- }
- if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) {
- return false;
- }
- pattIdxEnd--;
- pathIdxEnd--;
- }
- if (pathIdxStart > pathIdxEnd) {
- // String is exhausted
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
- if (!pattDirs[i].equals("**")) {
- return false;
- }
- }
- return true;
- }
- while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
- int patIdxTmp = -1;
- for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
- if (pattDirs[i].equals("**")) {
- patIdxTmp = i;
- break;
- }
- }
- if (patIdxTmp == pattIdxStart + 1) {
- // '**/**' situation, so skip one
- pattIdxStart++;
- continue;
- }
- // Find the pattern between padIdxStart & padIdxTmp in str between
- // strIdxStart & strIdxEnd
- int patLength = (patIdxTmp - pattIdxStart - 1);
- int strLength = (pathIdxEnd - pathIdxStart + 1);
- int foundIdx = -1;
- strLoop:
- for (int i = 0; i <= strLength - patLength; i++) {
- for (int j = 0; j < patLength; j++) {
- String subPat = pattDirs[pattIdxStart + j + 1];
- String subStr = pathDirs[pathIdxStart + i + j];
- if (!matchStrings(subPat, subStr, uriTemplateVariables)) {
- continue strLoop;
- }
- }
- foundIdx = pathIdxStart + i;
- break;
- }
- if (foundIdx == -1) {
- return false;
- }
- pattIdxStart = patIdxTmp;
- pathIdxStart = foundIdx + patLength;
- }
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
- if (!pattDirs[i].equals("**")) {
- return false;
- }
- }
- return true;
- }
第6,7行将路径按照/进行分割成字符串数组
第17行,如果发现通配符出现**,那么退出循环,跳到第50行判断是不是全匹配,如果不是全匹配那么就返回true
如果fullMatcher为true,那么就跳到56行,从后往前匹配,如果中间出现任何不匹配的就返回false,如果都匹配,那么会匹配到**,那么又退出循环,跳到第117行,此时可以确定返回true了
如果没有通配符的情况下,并且都验证通过,那么会进入第21行进行验证,得看看匹配路径是否是目录,如果是目录那么就匹配失败。
第61行,这里会把通配符*和?替换成正则表达式,替换的逻辑代码如下:
- public AntPathStringMatcher(String pattern, boolean caseSensitive) {
- StringBuilder patternBuilder = new StringBuilder();
- Matcher matcher = GLOB_PATTERN.matcher(pattern);
- int end = 0;
- while (matcher.find()) {
- patternBuilder.append(quote(pattern, end, matcher.start()));
- String match = matcher.group();
- if ("?".equals(match)) {
- patternBuilder.append('.');
- }
- else if ("*".equals(match)) {
- patternBuilder.append(".*");
- }
- else if (match.startsWith("{") && match.endsWith("}")) {
- int colonIdx = match.indexOf(':');
- if (colonIdx == -1) {
- patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
- this.variableNames.add(matcher.group(1));
- }
- else {
- String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
- patternBuilder.append('(');
- patternBuilder.append(variablePattern);
- patternBuilder.append(')');
- String variableName = match.substring(1, colonIdx);
- this.variableNames.add(variableName);
- }
- }
- end = matcher.end();
- }
- patternBuilder.append(quote(pattern, end, pattern.length()));
- this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
- Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
- }
第6行将截取从0开始到找到第一个匹配的子字符串的位置的字符串,并加上单引号,如:spring-*.xml 会截取到'spring-'这样的字符串
第七行获得匹配的字符串,这里是*
下面进行了判断,如果是*,那么就替换成.*追加到字符串后面
找到匹配的配置文件后,就添加到一个set集合中
最后我们再回到doFindMatchingFileSystemResources方法
- protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
- if (logger.isDebugEnabled()) {
- logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
- }
- Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
- Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
- for (File file : matchingFiles) {
- result.add(new FileSystemResource(file));
- }
- return result;
- }
第8表示这些文件都被打包成resource加到set集合里,最后一只返回到AbstractBeanDefinition类的loadBeanDefinitions方法
- public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
- ResourceLoader resourceLoader = getResourceLoader();
- if (resourceLoader == null) {
- throw new BeanDefinitionStoreException(
- "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
- }
- if (resourceLoader instanceof ResourcePatternResolver) {
- // Resource pattern matching available.
- try {
- Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
- int loadCount = loadBeanDefinitions(resources);
- if (actualResources != null) {
- for (Resource resource : resources) {
- actualResources.add(resource);
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
- }
- return loadCount;
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException(
- "Could not resolve bean definition resource pattern [" + location + "]", ex);
- }
- }
- else {
- // Can only load single resources by absolute URL.
- Resource resource = resourceLoader.getResource(location);
- int loadCount = loadBeanDefinitions(resource);
- if (actualResources != null) {
- actualResources.add(resource);
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
- }
- return loadCount;
- }
- }
第12行,调用了重载方法loadBeanDefinitions(Resource[]),方法调用一直进入到了XmlBeanDefinitionReader类中的loadBeanDefinitions方法
- public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
- Assert.notNull(encodedResource, "EncodedResource must not be null");
- if (logger.isInfoEnabled()) {
- logger.info("Loading XML bean definitions from " + encodedResource.getResource());
- }
- Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
- if (currentResources == null) {
- currentResources = new HashSet<EncodedResource>(4);
- this.resourcesCurrentlyBeingLoaded.set(currentResources);
- }
- if (!currentResources.add(encodedResource)) {
- throw new BeanDefinitionStoreException(
- "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
- }
- try {
- InputStream inputStream = encodedResource.getResource().getInputStream();
- try {
- InputSource inputSource = new InputSource(inputStream);
- if (encodedResource.getEncoding() != null) {
- inputSource.setEncoding(encodedResource.getEncoding());
- }
- return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
- }
- finally {
- inputStream.close();
- }
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException(
- "IOException parsing XML document from " + encodedResource.getResource(), ex);
- }
- finally {
- currentResources.remove(encodedResource);
- if (currentResources.isEmpty()) {
- this.resourcesCurrentlyBeingLoaded.remove();
- }
- }
- }
第17行,拿到这个配置文件的输入流,并包装成InputSource,这个时候我们又看到了do开头的方法doLoadBeanDefinitions,它的代码为
- try {
- Document doc = doLoadDocument(inputSource, resource);
- return registerBeanDefinitions(doc, resource);
- }
看到第二行,这个配置文件被加载成了Document对象,在进入doLoadDocument方法
- protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
- return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
- getValidationModeForResource(resource), isNamespaceAware());
- }
看到第3行,获得验证模式,它是根据这个xml的是否以DOCTYPE开头来确定时dtd验证还是xsd验证
- public int detectValidationMode(InputStream inputStream) throws IOException {
- // Peek into the file to look for DOCTYPE.
- BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
- try {
- boolean isDtdValidated = false;
- String content;
- while ((content = reader.readLine()) != null) {
- content = consumeCommentTokens(content);
- if (this.inComment || !StringUtils.hasText(content)) {
- continue;
- }
- if (hasDoctype(content)) {
- isDtdValidated = true;
- break;
- }
- if (hasOpeningTag(content)) {
- // End of meaningful data...
- break;
- }
- }
- return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
- }
- catch (CharConversionException ex) {
- // Choked on some character encoding...
- // Leave the decision up to the caller.
- return VALIDATION_AUTO;
- }
- finally {
- reader.close();
- }
- }
第12行的hasDoctype方法
- private boolean hasDoctype(String content) {
- return content.contains(DOCTYPE);
- }
DOCTYPE的值就是DOCTYPE字符串
看看document是怎么创建的
- @Override
- public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
- ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
- DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
- if (logger.isDebugEnabled()) {
- logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
- }
- DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
- return builder.parse(inputSource);
- }
首先在第5行创建了一个文档构建工厂,然后通过工厂创建构建者,最后用构建者解析输入资源,返回一个文档对象,这些类是JDK中javax.xml.parses包下的类,我们学习解析xml的时候已经使用过了,这里就不再往里面跑了。这样配置文件的读取工作就搞定了,接下来的就是解析配置文件了。
方法调用的序列图
spring读取xml配置文件(二)的更多相关文章
- Spring读取xml配置文件的原理与实现
本篇博文的目录: 一:前言 二:spring的配置文件 三:依赖的第三方库.使用技术.代码布局 四:Document实现 五:获取Element的实现 六:解析Element元素 七:Bean创造器 ...
- Spring 读取XML配置文件的两种方式
import org.springframework.context.ApplicationContext; import org.springframework.context.support.Cl ...
- Spring如何读取xml配置文件的
我们通过一个小案例来看xml解析过程. 1. 导包 <dependencies> <!-- xml解析工具 --> <dependency> <groupId ...
- Spring 通过XML配置文件以及通过注解形式来AOP 来实现前置,环绕,异常通知,返回后通知,后通知
本节主要内容: 一.Spring 通过XML配置文件形式来AOP 来实现前置,环绕,异常通知 1. Spring AOP 前置通知 XML配置使用案例 2. Spring AOP ...
- 【XML配置文件读取】使用jdom读取XML配置文件信息
在项目中我们经常需要将配置信息写在配置文件中,而XML配置文件是常用的格式. 下面将介绍如何通过jdom来读取xml配置文件信息. 配置文件信息 <?xml version="1.0& ...
- spring读取prperties配置文件(2)
接上篇,spring读取prperties配置文件(1),这一篇主要讲述spring如何用annotation的方式去读取自定义的配置文件. 这里我先定义好属性文件"user.propert ...
- JavaWeb_(Spring框架)xml配置文件
系列博文 JavaWeb_(Spring框架)xml配置文件 传送门 JavaWeb_(Spring框架)注解配置 传送门 Xml配置 a)Bean元素:交由Spring管理的对象都要配置在bean ...
- 如何配置多个Spring的xml配置文件(多模块配置)
如何使用多个Spring的xml配置文件(多模块配置) (2009-08-22 13:42:43) 如何使用多个Spring的xml配置文件(多模块配置) 在用Struts Spring Hibe ...
- spring读取prperties配置文件(1)
博客地址http://www.cnblogs.com/shizhongtao/p/3438431.html 属性文件命名是*.properties,在java中,用类java.util.Propert ...
随机推荐
- Laravel --- 要点笔记
一.路由: // 常规用法 Route::get('/',function(){ return 'get'; }) // 匹配多个 Route::match(['get','post'],'/',fu ...
- 关于CORS 应该注意的几点
前言 对于跨域,随着w3c的CORS的出现,相比较于有些年头的jsonp,CORS以其简单安全,支持post的优势越来越收到大家的欢迎.具体如何CORS的原理和实现,直接推荐阮老师的文章,十分详细.本 ...
- 使用Python爬取微信公众号文章并保存为PDF文件(解决图片不显示的问题)
前言 第一次写博客,主要内容是爬取微信公众号的文章,将文章以PDF格式保存在本地. 爬取微信公众号文章(使用wechatsogou) 1.安装 pip install wechatsogou --up ...
- CI框架使用(一)
CI框架的使用是很简单,也 是mvc模式.其中有好多类直接调用. 在使用帮助函数的时候,都需要手动加载,或者是在配置文件中加一个自动加载 $this->load->helper('ur ...
- 长春理工大学第十四届程序设计竞赛(重现赛)L
L.Homework Stream 题目链接:https://ac.nowcoder.com/acm/contest/912/L 题目 作为大珩班尖子生,小r每天有很多作业要完成,例如工图.工图和工图 ...
- lower_bound 和 upper_bound 功能和用法
以前用这两个函数的时候,简单看了几句别人的博客,记住了大概,用的时候每用一次就弄混一次,相当难受,今天对照着这两个函数的源码和自己的尝试发现:其实这两个函数只能用于 "升序" 序列 ...
- 关于火狐浏览器设置cookie的一个问题
最近发现我一个项目的网页,里面的cookie无法添加了,急的我瞪着我的PHP代码沉思了好久,我默认用的火狐浏览器,然而我默默的打开另一个叫360的浏览器,发现它的cookie是正常添加的. ... 难 ...
- POJ 2796:Feel Good(单调栈)
http://poj.org/problem?id=2796 题意:给出n个数,问一个区间里面最小的元素*这个区间元素的和的最大值是多少. 思路:只想到了O(n^2)的做法. 参考了http://ww ...
- HTML标签--入门
最近开始学习前端的知识,分享自己学的一点东西 <!DOCTYPE html> <!--HTML标识,,,用于告诉浏览器,这是一个HTML文档--> <html> & ...
- python之pip install
安装方式1 wget http://python-distribute.org/distribute_setup.py sudo python distribute_setup.py wget h ...