【附录】Spring 资源访问 基础及应用

此文是 【Spring 容器详解】-> 【ApplicationContext 做了哪些企业化的增强?】的支节点。

在Spring框架中,ApplicationContext是BeanFactory的重要扩展,提供了更丰富的功能。其中,资源访问(Resource Access)是ApplicationContext相对于BeanFactory的一个重要扩展功能。

一、 ApplicationContext的资源访问扩展

1. 核心接口:ResourceLoader

ApplicationContext实现了ResourceLoader接口,提供了统一的资源访问能力:

java 复制代码
public interface ResourceLoader {
    Resource getResource(String location);
    ClassLoader getClassLoader();
}

2. ApplicationContext的资源访问能力

ApplicationContext在资源访问方面提供了以下扩展功能:

3. 统一的资源访问接口

java 复制代码
@Configuration
public class ResourceAccessExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void demonstrateResourceAccess() {
        // 1. 访问classpath资源
        Resource classpathResource = applicationContext.getResource("classpath:application.properties");
        
        // 2. 访问文件系统资源
        Resource fileResource = applicationContext.getResource("file:/path/to/file.txt");
        
        // 3. 访问URL资源
        Resource urlResource = applicationContext.getResource("https://example.com/config.xml");
        
        // 4. 访问相对路径资源
        Resource relativeResource = applicationContext.getResource("config/database.properties");
    }
}

4. 资源模式解析

ApplicationContext支持Ant风格的路径模式匹配:

java 复制代码
@Component
public class ResourcePatternResolverExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void resolveResources() throws IOException {
        // 获取ResourcePatternResolver
        ResourcePatternResolver resolver = (ResourcePatternResolver) applicationContext;
        
        // 匹配所有properties文件
        Resource[] resources = resolver.getResources("classpath*:*.properties");
        
        // 匹配特定目录下的所有文件
        Resource[] configFiles = resolver.getResources("classpath:config/**/*.xml");
        
        // 匹配多个位置的文件
        Resource[] allConfigs = resolver.getResources("classpath*:config/*.properties");
        
        for (Resource resource : resources) {
            System.out.println("找到资源: " + resource.getFilename());
        }
    }
}

二、 资源访问的具体实现

1. 内置的Resource实现类

Spring提供了多种Resource实现类来支持不同类型的资源:

1.1 ClassPathResource

java 复制代码
@Component
public class ClassPathResourceExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void accessClassPathResource() throws IOException {
        // 访问classpath下的资源
        Resource resource = applicationContext.getResource("classpath:application.properties");
        
        if (resource.exists()) {
            // 读取资源内容
            try (InputStream inputStream = resource.getInputStream()) {
                byte[] content = inputStream.readAllBytes();
                String contentStr = new String(content, StandardCharsets.UTF_8);
                System.out.println("资源内容: " + contentStr);
            }
        }
    }
}

1.2 FileSystemResource

java 复制代码
@Component
public class FileSystemResourceExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void accessFileSystemResource() throws IOException {
        // 访问文件系统资源
        Resource resource = applicationContext.getResource("file:/data/config.properties");
        
        if (resource.exists()) {
            // 获取文件信息
            File file = resource.getFile();
            System.out.println("文件大小: " + file.length());
            System.out.println("最后修改时间: " + new Date(file.lastModified()));
        }
    }
}

1.3 UrlResource

java 复制代码
@Component
public class UrlResourceExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void accessUrlResource() throws IOException {
        // 访问URL资源
        Resource resource = applicationContext.getResource("https://api.example.com/config.json");
        
        if (resource.exists()) {
            // 读取URL内容
            try (InputStream inputStream = resource.getInputStream()) {
                byte[] content = inputStream.readAllBytes();
                String contentStr = new String(content, StandardCharsets.UTF_8);
                System.out.println("URL内容: " + contentStr);
            }
        }
    }
}

2. 资源加载策略

ApplicationContext使用不同的资源加载策略:

java 复制代码
@Component
public class ResourceLoadingStrategyExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public void demonstrateLoadingStrategy() {
        // 1. 默认策略 - 根据URL前缀判断
        Resource resource1 = applicationContext.getResource("classpath:config.properties");
        Resource resource2 = applicationContext.getResource("file:/config.properties");
        Resource resource3 = applicationContext.getResource("https://config.properties");
        
        // 2. 相对路径策略
        Resource resource4 = applicationContext.getResource("config.properties");
        // 默认从classpath加载
        
        // 3. 通配符策略
        ResourcePatternResolver resolver = (ResourcePatternResolver) applicationContext;
        try {
            Resource[] resources = resolver.getResources("classpath*:config/*.properties");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三、 实际应用场景

1. 配置文件加载

java 复制代码
@Component
public class ConfigurationLoader {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public Properties loadConfiguration(String configPath) throws IOException {
        Resource resource = applicationContext.getResource(configPath);
        Properties properties = new Properties();
        
        if (resource.exists()) {
            try (InputStream inputStream = resource.getInputStream()) {
                properties.load(inputStream);
            }
        }
        
        return properties;
    }
    
    public void loadMultipleConfigs() throws IOException {
        ResourcePatternResolver resolver = (ResourcePatternResolver) applicationContext;
        Resource[] resources = resolver.getResources("classpath*:config/*.properties");
        
        for (Resource resource : resources) {
            System.out.println("加载配置文件: " + resource.getFilename());
            Properties props = new Properties();
            try (InputStream inputStream = resource.getInputStream()) {
                props.load(inputStream);
                // 处理配置
            }
        }
    }
}

2. 模板文件加载

java 复制代码
@Component
public class TemplateLoader {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public String loadTemplate(String templatePath) throws IOException {
        Resource resource = applicationContext.getResource(templatePath);
        
        if (resource.exists()) {
            try (InputStream inputStream = resource.getInputStream();
                 BufferedReader reader = new BufferedReader(
                     new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
                
                StringBuilder content = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
                return content.toString();
            }
        }
        
        return null;
    }
    
    public void loadAllTemplates() throws IOException {
        ResourcePatternResolver resolver = (ResourcePatternResolver) applicationContext;
        Resource[] templates = resolver.getResources("classpath:templates/*.html");
        
        for (Resource template : templates) {
            String content = loadTemplate("classpath:templates/" + template.getFilename());
            System.out.println("模板: " + template.getFilename() + " 内容长度: " + content.length());
        }
    }
}

3. 国际化资源加载

java 复制代码
@Component
public class I18nResourceLoader {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    public Properties loadMessages(Locale locale) throws IOException {
        String resourcePath = String.format("classpath:messages_%s_%s.properties", 
            locale.getLanguage(), locale.getCountry());
        
        Resource resource = applicationContext.getResource(resourcePath);
        Properties messages = new Properties();
        
        if (resource.exists()) {
            try (InputStream inputStream = resource.getInputStream()) {
                messages.load(inputStream);
            }
        }
        
        return messages;
    }
    
    public void loadAllMessageFiles() throws IOException {
        ResourcePatternResolver resolver = (ResourcePatternResolver) applicationContext;
        Resource[] messageFiles = resolver.getResources("classpath:messages*.properties");
        
        for (Resource messageFile : messageFiles) {
            System.out.println("国际化文件: " + messageFile.getFilename());
            Properties messages = new Properties();
            try (InputStream inputStream = messageFile.getInputStream()) {
                messages.load(inputStream);
                System.out.println("消息数量: " + messages.size());
            }
        }
    }
}

四、 高级特性

1. 自定义ResourceLoader

java 复制代码
@Configuration
public class CustomResourceLoaderConfig {
    
    @Bean
    public ResourceLoader customResourceLoader() {
        return new DefaultResourceLoader() {
            @Override
            public Resource getResource(String location) {
                // 自定义资源加载逻辑
                if (location.startsWith("custom:")) {
                    return new CustomResource(location.substring(7));
                }
                return super.getResource(location);
            }
        };
    }
}

class CustomResource implements Resource {
    private final String location;
    
    public CustomResource(String location) {
        this.location = location;
    }
    
    @Override
    public boolean exists() {
        // 自定义存在性检查逻辑
        return true;
    }
    
    @Override
    public InputStream getInputStream() throws IOException {
        // 自定义输入流获取逻辑
        return new ByteArrayInputStream("Custom resource content".getBytes());
    }
    
    // 实现其他Resource接口方法...
    @Override
    public String getFilename() { return location; }
    @Override
    public String getDescription() { return "Custom resource: " + location; }
    @Override
    public boolean isReadable() { return true; }
    @Override
    public boolean isOpen() { return false; }
    @Override
    public URL getURL() throws IOException { return null; }
    @Override
    public URI getURI() throws IOException { return null; }
    @Override
    public File getFile() throws IOException { return null; }
    @Override
    public long contentLength() throws IOException { return 0; }
    @Override
    public long lastModified() throws IOException { return 0; }
    @Override
    public Resource createRelative(String relativePath) throws IOException { return null; }
}

2. 资源缓存机制

java 复制代码
@Component
public class ResourceCacheExample {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    private final Map<String, Resource> resourceCache = new ConcurrentHashMap<>();
    
    public Resource getCachedResource(String location) {
        return resourceCache.computeIfAbsent(location, key -> {
            return applicationContext.getResource(key);
        });
    }
    
    public void clearCache() {
        resourceCache.clear();
    }
    
    public void preloadResources(String... locations) {
        for (String location : locations) {
            getCachedResource(location);
        }
    }
}

五、 性能优化建议

1. 资源访问优化

java 复制代码
@Component
public class ResourceAccessOptimization {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    // 1. 使用ResourcePatternResolver进行批量操作
    public Resource[] getResourcesEfficiently(String pattern) throws IOException {
        ResourcePatternResolver resolver = (ResourcePatternResolver) applicationContext;
        return resolver.getResources(pattern);
    }
    
    // 2. 缓存频繁访问的资源
    private final Map<String, byte[]> resourceCache = new ConcurrentHashMap<>();
    
    public byte[] getResourceContent(String location) throws IOException {
        return resourceCache.computeIfAbsent(location, key -> {
            try {
                Resource resource = applicationContext.getResource(key);
                if (resource.exists()) {
                    try (InputStream inputStream = resource.getInputStream()) {
                        return inputStream.readAllBytes();
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return new byte[0];
        });
    }
    
    // 3. 异步加载资源
    @Async
    public CompletableFuture<Resource> loadResourceAsync(String location) {
        return CompletableFuture.supplyAsync(() -> {
            return applicationContext.getResource(location);
        });
    }
}

总结

ApplicationContext在资源访问方面相比BeanFactory提供了以下重要扩展:

  1. 统一的资源访问接口:实现了ResourceLoader接口,提供统一的资源访问能力
  2. 多种资源类型支持:支持classpath、文件系统、URL等多种资源类型
  3. 模式匹配能力:支持Ant风格的路径模式匹配
  4. 自动资源解析:根据URL前缀自动选择合适的Resource实现
  5. 批量资源操作:通过ResourcePatternResolver支持批量资源操作
相关推荐
笑衬人心。20 分钟前
缓存的三大问题分析与解决
java·spring·缓存
XiangCoder40 分钟前
🔥Java核心难点:对象引用为什么让90%的初学者栽跟头?
后端
二闹1 小时前
LambdaQueryWrapper VS QueryWrapper:安全之选与灵活之刃
后端
得物技术1 小时前
Rust 性能提升“最后一公里”:详解 Profiling 瓶颈定位与优化|得物技术
后端·rust
XiangCoder1 小时前
Java编程案例:从数字翻转到成绩统计的实用技巧
后端
duration~1 小时前
SpringAI实现Reread(Advisor)
java·人工智能·spring boot·spring
aiopencode1 小时前
iOS 文件管理全流程实战,从开发调试到数据迁移
后端
YuforiaCode1 小时前
24SpringCloud黑马商城微服务整合Seata重启服务报错的解决办法
java·spring·微服务
Lemon程序馆1 小时前
Kafka | 集群部署和项目接入
后端·kafka
集成显卡1 小时前
Rust 实战五 | 配置 Tauri 应用图标及解决 exe 被识别为威胁的问题
后端·rust