spring IOC源码学习(二):BeanDefinition资源加载

spring IOC资源加载,资源的查找定位,类的继承结构如下:


资源的加载是通过类XmlBeanDefinitionReader类来实现的。经过之前的一系列调用 ,到达loadBeanDefinitions方法。如下,非关键代码已经去掉。

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		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();
			}
		}
	}

执行 InputStream inputStream =encodedResource.getResource().getInputStream() 获得资源的I/O。由上图中类的继承关系,接口InputStreamStream的方法getInputStream()。接口方法的实现类为ClassPathResource。方法的实现如下:

	public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
			is = this.clazz.getResourceAsStream(this.path);
		}
		else if (this.classLoader != null) {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		else {
			is = ClassLoader.getSystemResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}


spring IOC中使用的类加载器是“应用类加载器”,在初始化时已经确定,因此classLoader不为null。进入第二个判断入口。调用抽象类ClassLoader的getResourceAsStream()方法,方法的实现类是java.net.URLClassLoader,返回资源的I/O,如下:

    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            if (url == null) {
                return null;
            }
            URLConnection urlc = url.openConnection();
            InputStream is = urlc.getInputStream();
            if (urlc instanceof JarURLConnection) {
                JarURLConnection juc = (JarURLConnection)urlc;
                JarFile jar = juc.getJarFile();
                synchronized (closeables) {
                    if (!closeables.containsKey(jar)) {
                        closeables.put(jar, null);
                    }
                }
            } else if (urlc instanceof sun.net.www.protocol.file.FileURLConnection) {
                synchronized (closeables) {
                    closeables.put(is, null);
                }
            }
            return is;
        } catch (IOException e) {
            return null;
        }
    }

ClassLoader类的getResource、getResources等方法可以加载classpath中的资源,ClassLoader获取资源传入的参数是相当于classpath的相对路径,如果某个资源想要被ClassLoader加载。该资源需要放到当前的classpath中,或者把资源的目录、jar包文件作为classpath。ClassLoader在加载一个资源时默认使用双亲委派模型原则。如果可以通过父加载器找到资源,则自己不必继续查找,交由父加载器去查找,并返回父加载器查找到的资源。


调用java.lang.ClassLoader的getResource返回资源的i/o,可以看到双亲委派模型在资源查找中的运用。

    public URL getResource(String name) {
        URL url;
        if (parent != null) {
			//扩展类加载器加载资源
            url = parent.getResource(name);
        } else {
			//启动类加载器加载资源
            url = getBootstrapResource(name);
        }
        if (url == null) {
			//应用类加载器加载资源
            url = findResource(name);
        }
        return url;
    }
因资源文件放在classpath路径下,默认由应用类加载器加载。即调用url= findResource(name)返回资源信息。


接下来,了解getBootstrapResource(name)和findResource(name)。先看getBootstrapResource(name)的执行代码。

    /**
     * Find resources from the VM's built-in classloader.(通过虚拟机内置的类加载器查找资源。)
     */
    private static URL getBootstrapResource(String name) {
		//取得启动类加载器的路径实例
        URLClassPath ucp = getBootstrapClassPath();
		//通过启动类加载器获取资源
        Resource res = ucp.getResource(name);
		//返回资源的URL实例对象。
        return res != null ? res.getURL() : null;
    }


getBootstrapClassPath()返回查找系统资源的URLClassPath类对象(启动类加载器),URLClassPath是jvm的底层代码,感兴趣的可以进去看下。

    // Returns the URLClassPath that is used for finding system resources.
    static URLClassPath getBootstrapClassPath() {
        return sun.misc.Launcher.getBootstrapClassPath();
    }
调试代码,getBootstrapResource返回值为null,父类加载器查找的路径中没有指定资源,通知子类去加载器类。


执行url= findResource(name);

    /**
     * Finds the resource with the specified name on the URL search path.
     *
     * @param name the name of the resource
     * @return a {@code URL} for the resource, or {@code null}
     * if the resource could not be found, or if the loader is closed.
     */
    public URL findResource(final String name) {
        /*
         * The same restriction to finding classes applies to resources
         */
        URL url = AccessController.doPrivileged(
            new PrivilegedAction<URL>() {
                public URL run() {
                    return ucp.findResource(name, true);
                }
            }, acc);

        return url != null ? ucp.checkURL(url) : null;
    }
其中,AccessController是后续要研究的内容,这里暂不提及。调试,返回的url不为null。通过对上述调用过程及结果分析,资源的查找过程符合双亲委派模型原则。

补充一

虚拟机类加载过程的“加载”阶段。在加载阶段,虚拟机需要完成以下三件事情:

1、 通过一个类的全限定名获取定义此类的二进制字节流。

2、 将这个字节流代表的静态存储结构转化为方法区运行时数据结构。

3、 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。

虚拟机规范的这3点要求,其实并不算具体。如第一点,通过一个类的全限定名获取定义此类的二进制字节流。它没有指明二进制字节流要从一个class文件获取,也没有说怎样获取,灵活度相当大。例如:

1、 从zip包中获取。

2、 从网络获取。

3、 运行时计算生成,使用最多的场景就是动态代理技术。

4、 由其它文件生成。

5、 从数据库中读取。


补充二

URL类的用法。java URL处理,URL(UniformResource Loacator)统一资源定位符,表示为互联网上的资源。java网络类可以通过网络或者远程连接来实现应用。而且,可以对国际互联网以及URL资源进行访问。java的URL类可以让访问网络资源就像是访问本地文件夹一样方便快捷。可以通过使用java的URL类经由URL完成读取和修改数据操作。看个例子:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

public class UrlTest {
	public static void main(String[] args) throws IOException {
		
		//创建url连接实例
		URL url = new URL("https://www.baidu.com");
		System.out.println(url);
		//在访问这个连接的资源和内容之前,需要打开这些资源和内容上的连接,使用openConnection来完成这一操作
		URLConnection urlcon = (URLConnection)url.openConnection();
		InputStream is = urlcon.getInputStream();
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		StringBuffer sb = new StringBuffer();
		String s = null;
		while((s=br.readLine())!=null){
			sb.append(s).append("\n");
		}
		System.out.println(sb.toString());
	}
}
通过上述例子,对URL的使用有了直观的了解。spring中获取到资源的URL,执行 URLConnection urlc = url.openConnection(),打开资源的连接,为接下来资源的访问作好准备。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值