(4)java的和spring的资源管理器的应用

1.java访问资源的方式

比如现在有一个资源文件application.properties,现在要得到该配置文件的流,该文件放在resouces目录下,文件内容如下

spring.application.name = spring-resources

通过java方式的加载资源,打印输出流

/**
 * @Project: spring
 * @description:  java 加载资源的方式
 * @author: sunkang
 * @create: 2018-09-23 22:16
 * @ModificationHistory who      when       What
 **/
public class FileLoadDemo {

    public static void main(String[] args) throws IOException {
        //1.用类加载器来实现,不过这个是直接加载编译好的classpath路劲的
        InputStream inputStream= FileLoadDemo.class.getClassLoader().getResourceAsStream("application.properties");
        System.out.println(inputStream);

        //2.通过绝对路劲的方式来加载
        File file = new File("");
        //file.getAbsolutePath() 得到的是user.dir,工作路径,跟下面表示方法一样
        System.out.println(System.getProperty("user.dir"));
        //spring-resources为一个模块,所以这里需要加上模块的路径
        File resouceFile = new File("spring-resources/src/main/resources/application.properties");
        InputStream ins=  new BufferedInputStream(new FileInputStream(resouceFile));
        System.out.println(ins);

        //3通过nio加载
        InputStream nos= Files.newInputStream(Paths.get("spring-resources/src/main/resources/application.properties"));
        System.out.println(nos);

        //4 通过URL的方式
        URL fileURL = file.toURI().toURL();
        URLConnection urlConnection = fileURL.openConnection();
        InputStream inputStreamFromURL = urlConnection.getInputStream();
        System.out.println(inputStreamFromURL);
    }
}

这里面着重讲解第四种方式
通过URL来定义资源的位置,该资源可以是文件,jar包,或者网络资源。通过不同前缀名来代表每一种资源的协议,具体请求而的时候由不同的处理协议的handler进行处理
举个例子:


/**
 * java 访问资源可以通过URL这个对象来访问各种资源,实际上URL为了得到inpustream需要以下的过程
*        URL -> URLConnection -> URLStreamHandler -> InputStream
*       这里用了委派模式,获取inputStream会先要获取URLConnection,而URLConnection是由URLStreamHandler创建而来
*       下面秒速了java支持的集中协议,可以rt.jar下在sun.net.www.protocol找到支持的协议模式
*      URL url = new URL("https://www.baidu.com"); // https 协议
*       URL ftpURL = new URL("ftp://ftp.baidu.com"); // ftp 协议
*       URL jar = new URL("jar://jar.baidu.com"); // jar 协议
*        file URLStreamHandler   = sun.net.www.protocol.file.Handler
*        http URLStreamHandler  =  sun.net.www.protocol.http.Handler
*        https URLStreamHandler  = sun.net.www.protocol.https.Handler
*        jar URLStreamHandler  = sun.net.www.protocol.jar.Handler
*        ftp URLStreamHandler  = sun.net.www.protocol.ftp.Handler
*        模式 URLStreamHandler =  sun.net.www.protocol.${protocol}.Handler
 *
 */
public class FileDemo {
    public static void main(String[] args) throws Exception {
        File file = new File("spring-resources/src/main/resources/application.properties");
        URL fileURL = file.toURI().toURL();
        URLConnection urlConnection = fileURL.openConnection();
        InputStream inputStreamFromURL = urlConnection.getInputStream();
        //spring-core  核心包的工具类,把流的内容转成字符串
        String content = StreamUtils.copyToString(inputStreamFromURL, Charset.forName("UTF-8"));
        System.out.println(inputStreamFromURL);
    }
}

从下面的这个图可以看出URL和其他接口的关系

image.png

在URL源码部分中的getURLStreamHandler的方法中,存在如下的代码段

if (handler == null) {
                String packagePrefixList = null;

                packagePrefixList
                    = java.security.AccessController.doPrivileged(
                    new sun.security.action.GetPropertyAction(
                        protocolPathProp,""));
                if (packagePrefixList != "") {
                    packagePrefixList += "|";
                }

                // REMIND: decide whether to allow the "null" class prefix
                // or not.
                packagePrefixList += "sun.net.www.protocol";

                StringTokenizer packagePrefixIter =
                    new StringTokenizer(packagePrefixList, "|");

                while (handler == null &&
                       packagePrefixIter.hasMoreTokens()) {

                    String packagePrefix =
                      packagePrefixIter.nextToken().trim();
                    try {
                        String clsName = packagePrefix + "." + protocol +
                          ".Handler";
                        Class cls = null;
                        try {
                            cls = Class.forName(clsName);
                        } catch (ClassNotFoundException e) {
                            ClassLoader cl = ClassLoader.getSystemClassLoader();
                            if (cl != null) {
                                cls = cl.loadClass(clsName);
                            }
                        }
                        if (cls != null) {
                            handler  =
                              (URLStreamHandler)cls.newInstance();
                        }
                    } catch (Exception e) {
                        // any number of exceptions can get thrown here
                    }
                }
            }

基本上可以了解到是根据 "sun.net.www.protocol" + 协议名称+ ".handler" 得到一个类的全限定名,然后通过Class.forName(clsName)来得到类,如果出错,则用引导类加载器来加载类,判断cls不为空,cls.newInstance()用反射创建一个类

在java的sun.misc.Launcher有用到URLStreamHandlerFactory的工厂,ExtClassLoader和AppClassLoader的有用到这个URLStreamHandlerFactory工厂,有兴趣的源码可以研究下

2.拓展java的classpath路劲加载协议

要模仿创建一个类名为 sun.net.www.protocol.classpath.hanlder的类,该类如下:

/**
 * Classpath 协议 Handler
 *
 */
public class Handler extends URLStreamHandler {
    private final String PROTOCOL_PREFIX = "classpath:/";
    @Override
    protected URLConnection openConnection(URL url) throws IOException {
        // 比如 url = "classpath:/META-INF/license.txt"
        // classpath  = META-INF/license.txt
        // 移除前缀 classpath:/
        // classpath:/META-INF/license.txt
        String urlString = url.toString();
        // META-INF/license.txt
        String classpath = urlString.substring(PROTOCOL_PREFIX.length());
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL classpathURL = classLoader.getResource(classpath);
        // 委派给 ClassLoader 实现
        return classpathURL.openConnection();
    }
}

测试类:

public class ClassPathUrlTest {
    public static void main(String[] args) throws IOException {
        URL url = new URL("classpath:/application.properties");
        URLConnection urlConnection = url.openConnection();

        InputStream inputStreamFromURL = urlConnection.getInputStream();

        String content = StreamUtils.copyToString(inputStreamFromURL,     Charset.forName("UTF-8"));

        System.out.println(content);
    }
}

测试结果如下:

spring.application.name = spring-resources

3.spring的加载方式 (DefaultResourceLoader)

/**
 * @Project: spring
 * @description:  spring 默认的加载资源的方式
 * @author: sunkang
 * @create: 2018-09-23 16:51
 * @ModificationHistory who      when       What
 **/
public class SprignResouceLoadTest {

    public static void main(String[] args) throws IOException {
//        ApplicationContext context = new ClassPathXmlApplicationContext();
//        context.getResource("application.properties")
        //ClassPathXmlApplicationContext继承了DefaultResourceLoader,所以实际上DefaultResourceLoader在处理
        //用classPath
        //默认的加载器
        ResourceLoader recourceLoder = new DefaultResourceLoader();
        Resource resource =  recourceLoder.getResource("application.properties");
        InputStream ins= resource.getInputStream();
        System.out.println(ins);

        //加载 classpath路径下的文件
        Resource classpathResource =  recourceLoder.getResource("classpath:application.properties");
        System.out.println(classpathResource.getInputStream());

        //通过file文件协议加载资源文件
        Resource fileResource =  recourceLoder.getResource("file:D:/Eclipse2018Data/personProject/spring/spring-resources/src/main/resources/application.properties");
        System.out.println(fileResource.getInputStream());

        //通过https协议加载资源文件
        Resource httpResource =  recourceLoder.getResource("https://start.spring.io/");
        System.out.println(fileResource.getInputStream());

    }
}

4.基于spring的拓展协议

/**
 * spring  拓展协议举例
 *
 */
public class ResourceDemo {
    public static void main(String[] args) throws IOException {
        // Resource
        // FileSystemResource
        // ClasspathResource
        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
        // 添加一个protocol = "cp" 处理
        resourceLoader.addProtocolResolver(new ProtocolResolver() {
            private static final String PROTOCOL_PREFIX = "cp:/";
            @Override
            public Resource resolve(String location, ResourceLoader resourceLoader) {
                if (location.startsWith(PROTOCOL_PREFIX)) {
                    // application.properties
                    String classpath = ResourceLoader.CLASSPATH_URL_PREFIX +
                            location.substring(PROTOCOL_PREFIX.length());
                    // cp:/application.properties -> classpath:application.properties
                    return resourceLoader.getResource(classpath);
                }
                return null;
            }
        });
        Resource resource =
                resourceLoader.getResource("cp:/application.properties");

        InputStream inputStream = resource.getInputStream();
        String content = StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
        System.out.println(content);
    }
}

看DefaultResourceLoader的源码可以了解到,先由添加的resourceLoader.addProtocolResolver()的协议一个个遍历先解析,解析到了就返回,cp拓展的协议的实现是委派给了ClassPathResource去解析了

你可能感兴趣的:((4)java的和spring的资源管理器的应用)