此文是 【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提供了以下重要扩展:
- 统一的资源访问接口:实现了ResourceLoader接口,提供统一的资源访问能力
- 多种资源类型支持:支持classpath、文件系统、URL等多种资源类型
- 模式匹配能力:支持Ant风格的路径模式匹配
- 自动资源解析:根据URL前缀自动选择合适的Resource实现
- 批量资源操作:通过ResourcePatternResolver支持批量资源操作