Spring 资源管理

引入动机

• 为什么 Spring 不使用 Java 标准资源管理,而选择重新发明轮子?
• Java 标准资源管理强大,然而扩展复杂,资源存储方式并不统一
• Spring 要自立门户(重要的话,要讲三遍)
• Spring “抄”、“超” 和 “潮”

Java 标准资源管理

• Java 标准资源定位

职责说明
面向资源文件系统、artifact(jar、war、ear 文件)以及远程资源(HTTP、FTP 等)
API 整合java.lang.ClassLoader#getResource、java.io.File 或 java.net.URL
资源定位java.net.URL 或 java.net.URI
面向流式存储java.net.URLConnection
协议扩展java.net.URLStreamHandler 或 java.net.URLStreamHandlerFactory

• Java URL 协议扩展

  • 基于 java.net.URLStreamHandlerFactory
  • 基于 java.net.URLStreamHandler

• 基于 java.net.URLStreamHandlerFactory 扩展协议

• 基于 java.net.URLStreamHandler 扩展协议

  • JDK 1.8 內建协议实现
协议实现类
filesun.net.www.protocol.file.Handler
ftpsun.net.www.protocol.ftp.Handler
httpsun.net.www.protocol.http.Handler
httpssun.net.www.protocol.https.Handler
jarsun.net.www.protocol.jar.Handler
mailtosun.net.www.protocol.mailto.Handler
netdocsun.net.www.protocol.netdoc.Handler

• 基于 java.net.URLStreamHandler 扩展协议

  • 实现类名必须为 “Handler”
实现类命名规则说明
默认sun.net.www.protocol.${protocol}.Handler
自定义通过 Java Properties java.protocol.handler.pkgs 指定实现类包名,实现类名必须为“Handler”。如果存在多包名指定,通过分隔符 “|”

Spring 资源接口

• 资源接口

类型接口
输入流org.springframework.core.io.InputStreamSource
只读资源org.springframework.core.io.Resource
可写资源org.springframework.core.io.WritableResource
编码资源org.springframework.core.io.support.EncodedResource
上下文资源org.springframework.core.io.ContextResource

Spring 内建 Resource 实现

• 內建实现

资源来源资源协议实现类
Bean 定义org.springframework.beans.factory.support.BeanDefinitionResource
数组org.springframework.core.io.ByteArrayResource
类路径classpath:/org.springframework.core.io.ClassPathResource
文件系统file:/org.springframework.core.io.FileSystemResource
URLURL 支持的协议org.springframework.core.io.UrlResource
ServletContextorg.springframework.web.context.support.ServletContextResource

Spring Resource 接口扩展

• 可写资源接口

  • org.springframework.core.io.WritableResource
    • org.springframework.core.io.FileSystemResource
    • org.springframework.core.io.FileUrlResource(@since 5.0.2)
    • org.springframework.core.io.PathResource(@since 4.0 & @Deprecated)

• 编码资源接口

  • org.springframework.core.io.support.EncodedResource
/**
 * 带有字符编码的 {@link FileSystemResource} 示例
 */
public class EncodedFileSystemResourceDemo {

    public static void main(String[] args) throws IOException {
        String currentJavaFilePath = System.getProperty("user.dir") + "/thinking-in-spring/resource/src/main/java/org/geekbang/thinking/in/spring/resource/EncodedFileSystemResourceDemo.java";
        File currentJavaFile = new File(currentJavaFilePath);
        // FileSystemResource => WritableResource => Resource
        FileSystemResource fileSystemResource = new FileSystemResource(currentJavaFilePath);
        EncodedResource encodedResource = new EncodedResource(fileSystemResource, "UTF-8");
        // 字符输入流
        // 字符输入流
        try (Reader reader = encodedResource.getReader()) {
            System.out.println(IOUtils.toString(reader));
        }
    }
}

Spring 资源加载器

• Resource 加载器

  • org.springframework.core.io.ResourceLoader
    • org.springframework.core.io.DefaultResourceLoader
      • org.springframework.core.io.FileSystemResourceLoader
      • org.springframework.core.io.ClassRelativeResourceLoader
      • org.springframework.context.support.AbstractApplicationContext
/**
 * 带有字符编码的 {@link FileSystemResourceLoader} 示例
 */
public class EncodedFileSystemResourceLoaderDemo {

    public static void main(String[] args) throws IOException {
        String currentJavaFilePath = "/" + System.getProperty("user.dir") + "/thinking-in-spring/resource/src/main/java/org/geekbang/thinking/in/spring/resource/EncodedFileSystemResourceLoaderDemo.java";
        // 新建一个 FileSystemResourceLoader 对象
        FileSystemResourceLoader resourceLoader = new FileSystemResourceLoader();
        // FileSystemResource => WritableResource => Resource
        Resource resource = resourceLoader.getResource(currentJavaFilePath);
        EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");
        // 字符输入流
        try (Reader reader = encodedResource.getReader()) {
            System.out.println(IOUtils.toString(reader));
        }
    }
}

Spring 通配路径资源加载器

• 通配路径 ResourceLoader

  • org.springframework.core.io.support.ResourcePatternResolver
    • org.springframework.core.io.support.PathMatchingResourcePatternResolver

• 路径匹配器

  • org.springframework.util.PathMatcher
    • Ant 模式匹配实现 - org.springframework.util.AntPathMatcher

Spring 通配路径资源扩展

• 实现 org.springframework.util.PathMatcher

• 重置 PathMatcher

  • PathMatchingResourcePatternResolver#setPathMatcher
/**
 * 自定义 {@link ResourcePatternResolver} 示例
 */
public class CustomizedResourcePatternResolverDemo {

    public static void main(String[] args) throws IOException {
        // 读取当前 package 对应的所有的 .java 文件
        // *.java
        String currentPackagePath = "/" + System.getProperty("user.dir") + "/thinking-in-spring/resource/src/main/java/org/geekbang/thinking/in/spring/resource/";
        String locationPattern = currentPackagePath + "*.java";
        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(new FileSystemResourceLoader());

        resourcePatternResolver.setPathMatcher(new JavaFilePathMatcher());

        Resource[] resources = resourcePatternResolver.getResources(locationPattern);

        Stream.of(resources).map(ResourceUtils::getContent).forEach(System.out::println);
    }

    static class JavaFilePathMatcher implements PathMatcher {

        @Override
        public boolean isPattern(String path) {
            return path.endsWith(".java");
        }

        @Override
        public boolean match(String pattern, String path) {
            return path.endsWith(".java");
        }

        @Override
        public boolean matchStart(String pattern, String path) {
            return false;
        }

        @Override
        public String extractPathWithinPattern(String pattern, String path) {
            return null;
        }

        @Override
        public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
            return null;
        }

        @Override
        public Comparator<String> getPatternComparator(String path) {
            return null;
        }

        @Override
        public String combine(String pattern1, String pattern2) {
            return null;
        }
    }
}

依赖注入 Spring Resource

• 基于 @Value 实现

  • 如:
    @Value(“classpath:/…”)
    private Resource resource;
    @Value(“classpath*:/META-INF/*.properties”)
    private Resource[] propertiesResources;
/**
 * 注入 {@link Resource} 对象示例
 */
public class InjectingResourceDemo {

    @Value("classpath:/META-INF/default.properties")
    private Resource defaultPropertiesResource;

    @Value("classpath*:/META-INF/*.properties")
    private Resource[] propertiesResources;

    @Value("${user.dir}")
    private String currentProjectRootPath;

    @PostConstruct
    public void init() {
        System.out.println(ResourceUtils.getContent(defaultPropertiesResource));
        System.out.println("================");
        Stream.of(propertiesResources).map(ResourceUtils::getContent).forEach(System.out::println);
        System.out.println("================");
        System.out.println(currentProjectRootPath);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(InjectingResourceDemo.class);
        // 启动 Spring 应用上下文
        context.refresh();
        // 关闭 Spring 应用上下文
        context.close();
    }
}

依赖注入 ResourceLoader

• 方法一:实现 ResourceLoaderAware 回调
• 方法二:@Autowired 注入 ResourceLoader
• 方法三:注入 ApplicationContext 作为 ResourceLoader

/**
 * 注入 {@link ResourceLoader} 对象示例
 */
public class InjectingResourceLoaderDemo implements ResourceLoaderAware {

    private ResourceLoader resourceLoader; // 方法一

    @Autowired
    private ResourceLoader autowiredResourceLoader; // 方法二

    @Autowired
    private AbstractApplicationContext applicationContext; // 方法三

    @PostConstruct
    public void init() {
        System.out.println("resourceLoader == autowiredResourceLoader : " + (resourceLoader == autowiredResourceLoader));
        System.out.println("resourceLoader == applicationContext : " + (resourceLoader == applicationContext));
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(InjectingResourceLoaderDemo.class);
        // 启动 Spring 应用上下文
        context.refresh();
        // 关闭 Spring 应用上下文
        context.close();
    }
}

面试题

Spring 配置资源中有哪些常见类型?
答:
• XML 资源
• Properties 资源
• YAML 资源

请例举不同类型 Spring 配置资源?
答:
• XML 资源

  • 普通 Bean Definition XML 配置资源 - *.xml
  • Spring Schema 资源 - *.xsd

• Properties 资源

  • 普通 Properties 格式资源 - *.properties
  • Spring Handler 实现类映射文件 - META-INF/spring.handlers
  • Spring Schema 资源映射文件 - META-INF/spring.schemas

• YAML 资源

  • 普通 YAML 配置资源 - *.yaml 或 *.yml

Java 标准资源管理扩展的步骤?
答:
• 简易实现

  • 实现 URLStreamHandler 并放置在 sun.net.www.protocol.${protocol}.Handler 包下

• 自定义实现

  • 实现 URLStreamHandler
  • 添加 -Djava.protocol.handler.pkgs 启动参数,指向 URLStreamHandler 实现类的包下

• 高级实现

  • 实现 URLStreamHandlerFactory 并传递到 URL 之中
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值