深度解析kfyty725/loveqq-framework的资源定位:ResourcePatternResolver实现

深度解析kfyty725/loveqq-framework的资源定位:ResourcePatternResolver实现

【免费下载链接】loveqq-framework 全新轻量级 ioc/aop/javafx 框架,更小,更强大。 该框架基本实现自我配置,具有更强大的复杂的条件bean注册推断,全框架复合注解支持;统一命令式/响应式编程风格,包含过滤器、拦截器等;提供 javafx mvvm 框架,可实现模型-数据的双向绑定,父子窗口生命周期绑定及监听;提供动态数据源配置支持;提供注解式缓存支持;默认提供 jar 包瘦身方式打包,支持 jar-index 启动。 【免费下载链接】loveqq-framework 项目地址: https://gitcode.com/kfyty725/loveqq-framework

引言:资源定位的核心挑战

你是否还在为框架中的资源定位逻辑感到困惑?是否曾因路径匹配不准确导致配置文件加载失败?本文将深入剖析loveqq-framework中ResourcePatternResolver接口的实现原理,带你彻底掌握资源定位的核心机制。读完本文后,你将能够:

  • 理解PathMatchingResourcePatternResolver的工作原理
  • 掌握Ant风格路径匹配的实现细节
  • 学会在实际项目中正确使用资源定位功能
  • 解决复杂场景下的资源查找问题

1. ResourcePatternResolver概述

1.1 什么是ResourcePatternResolver

ResourcePatternResolver(资源模式解析器)是loveqq-framework中负责资源定位的核心组件,它能够根据指定的模式匹配规则,在类路径下查找符合条件的资源。该组件广泛应用于框架的各个模块,如MyBatis集成、动态SQL解析、配置文件加载等场景。

1.2 核心实现类:PathMatchingResourcePatternResolver

在loveqq-framework中,PathMatchingResourcePatternResolverResourcePatternResolver接口的主要实现类,它提供了基于Ant风格路径模式的资源查找功能。

@Component
@RequiredArgsConstructor
@SuppressWarnings("UrlHashCode")
public class PathMatchingResourcePatternResolver {
    private volatile boolean loaded;
    private final Set<URL> urls;
    private final PatternMatcher patternMatcher;

    // 构造函数
    public PathMatchingResourcePatternResolver() {
        this(new HashSet<>());
    }

    public PathMatchingResourcePatternResolver(Set<URL> urls) {
        this(urls, new AntPathMatcher());
    }
    
    // 核心方法
    public Set<URL> findResources(String pattern) {
        // 实现细节后续分析
    }
}

1.3 类关系图

mermaid

2. PathMatchingResourcePatternResolver实现原理

2.1 初始化过程

PathMatchingResourcePatternResolver的初始化过程主要涉及类路径的解析和资源的预加载:

protected Set<URL> obtainURL() {
    if (!this.loaded) {
        synchronized (this) {
            if (!this.loaded) {
                this.urls.addAll(ClassLoaderUtil.resolveClassPath(classLoader(this.getClass())));
                this.loaded = true;
            }
        }
    }
    return this.urls;
}

上述代码采用了双重检查锁定(Double-Checked Locking)机制,确保类路径的解析只执行一次,提高了资源查找的效率。

2.2 资源查找流程

findResources方法是资源查找的入口,它根据资源的存储位置(JAR包或文件系统)调用不同的查找逻辑:

public Set<URL> findResources(String pattern) {
    try {
        Set<URL> urls = this.obtainURL();
        Set<URL> resources = new HashSet<>();
        for (URL url : urls) {
            if (url.getFile().endsWith(".jar")) {
                resources.addAll(this.findResourcesByJar(new JarFile(url.getFile().replace("%20", " ")), pattern));
            } else {
                resources.addAll(this.findResourcesByFile(url, pattern));
            }
        }
        return resources;
    } catch (IOException e) {
        throw ExceptionUtil.wrap(e);
    }
}

2.3 JAR包内资源查找

findResourcesByJar方法负责查找JAR包内的资源:

public Set<URL> findResourcesByJar(JarFile jarFile, String pattern) {
    Set<URL> resources = new HashSet<>();
    Enumeration<JarEntry> entries = jarFile.entries();
    while (entries.hasMoreElements()) {
        JarEntry jarEntry = entries.nextElement();
        if (this.patternMatcher.matches(pattern, jarEntry.getName())) {
            resources.add(IOUtil.newNestedJarURL(jarFile, jarEntry.getName()));
        }
    }
    return resources;
}

2.4 文件系统资源查找

findResourcesByFile方法负责查找文件系统中的资源:

public Set<URL> findResourcesByFile(URL url, String pattern) {
    try {
        Set<URL> resources = new HashSet<>();
        File[] files = new File(url.getPath()).listFiles();
        if (files == null || files.length < 1) {
            return resources;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                resources.addAll(this.findResourcesByFile(file.toURI().toURL(), pattern));
                continue;
            }
            String filePath = file.getPath();
            if (filePath.contains("classes")) {
                filePath = filePath.substring(filePath.indexOf("classes" + File.separator) + 8).replace('\\', '/');
            }
            if (this.patternMatcher.matches(pattern, filePath)) {
                resources.add(file.toURI().toURL());
            }
        }
        return resources;
    } catch (MalformedURLException e) {
        throw ExceptionUtil.wrap(e);
    }
}

3. Ant风格路径匹配详解

3.1 PatternMatcher接口

PatternMatcher(模式匹配器)是路径匹配的核心接口,定义了模式匹配的基本方法:

public interface PatternMatcher {
    /**
     * 路径匹配
     *
     * @param pattern 规则,eg: /aa/**
     * @param source  要匹配的字符串,eg: /aa/bb
     * @return true if matched
     */
    boolean matches(String pattern, String source);
}

3.2 AntPathMatcher实现

AntPathMatcherPatternMatcher接口的主要实现类,它支持Ant风格的路径匹配,提供了强大的模式匹配能力。

3.2.1 Ant风格路径规则

Ant风格路径匹配支持以下通配符:

  • ?:匹配一个字符
  • *:匹配零个或多个字符
  • **:匹配零个或多个目录

一些常见的例子:

模式说明
com/t?st.jsp匹配com/test.jspcom/tast.jsp
com/*.jsp匹配com目录下所有.jsp文件
com/**/test.jsp匹配com路径下所有test.jsp文件
org/**/servlet/bla.jsp匹配org/servlet/bla.jsporg/test/servlet/bla.jsp
3.2.2 核心匹配算法

AntPathMatcher的核心是doMatch方法,它实现了复杂的路径匹配逻辑:

protected boolean doMatch(String pattern, String path, boolean fullMatch) {
    // 路径分隔符一致性检查
    if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
        return false;
    }

    // 分割路径为目录数组
    String[] pattDirs = tokenizeToStringArray(pattern, this.pathSeparator, true, true);
    String[] pathDirs = tokenizeToStringArray(path, this.pathSeparator, true, true);

    int pattIdxStart = 0;
    int pattIdxEnd = pattDirs.length - 1;
    int pathIdxStart = 0;
    int pathIdxEnd = pathDirs.length - 1;

    // 匹配第一个**之前的部分
    while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
        String patDir = pattDirs[pattIdxStart];
        if ("**".equals(patDir)) {
            break;
        }
        if (!matchStrings(patDir, pathDirs[pathIdxStart])) {
            return false;
        }
        pattIdxStart++;
        pathIdxStart++;
    }

    // 处理剩余部分(包含**的情况)
    // ... 省略复杂的匹配逻辑 ...

    return true;
}
3.2.3 字符串匹配

matchStrings方法实现了字符串级别的通配符匹配:

private boolean matchStrings(String pattern, String str) {
    char[] patArr = pattern.toCharArray();
    char[] strArr = str.toCharArray();
    int patIdxStart = 0;
    int patIdxEnd = patArr.length - 1;
    int strIdxStart = 0;
    int strIdxEnd = strArr.length - 1;
    char ch;

    boolean containsStar = false;
    for (char aPatArr : patArr) {
        if (aPatArr == '*') {
            containsStar = true;
            break;
        }
    }

    // 没有*号的情况,直接比较
    if (!containsStar) {
        if (patIdxEnd != strIdxEnd) {
            return false; // 长度不同
        }
        for (int i = 0; i <= patIdxEnd; i++) {
            ch = patArr[i];
            if (ch != '?') {
                if (ch != strArr[i]) {
                    return false; // 字符不匹配
                }
            }
        }
        return true;
    }

    // 处理包含*号的情况
    // ... 省略复杂的匹配逻辑 ...

    return true;
}

4. 实际应用场景

4.1 MyBatis集成中的应用

loveqq-boot-starter-mybatis模块中,PathMatchingResourcePatternResolver被用于扫描Mapper接口和SQL映射文件:

public class MapperScanner {
    private PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver;
    
    public void afterPropertiesSet() {
        // 扫描Mapper接口
        List<Class<?>> collect = Arrays.stream(mapperScan.value())
            .flatMap(e -> PackageUtil.scanClass(e, this.pathMatchingResourcePatternResolver).stream())
            .collect(Collectors.toList());
        // ...
    }
}

4.2 动态SQL解析

loveqq-data-korm模块中,PathMatchingResourcePatternResolver用于加载动态SQL文件:

public class AbstractDynamicProvider {
    public String provideSql(Method method) {
        // 获取SQL资源路径
        String path = this.getSqlPath(method);
        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = 
            this.configuration.getPathMatchingResourcePatternResolver();
        try {
            for (URL file : pathMatchingResourcePatternResolver.findResources(path)) {
                // 解析SQL文件内容
                return IOUtil.readString(file.openStream());
            }
        } catch (IOException e) {
            throw new ORMException("Load dynamic sql failed: " + path, e);
        }
        throw new ORMException("Dynamic sql not found: " + path);
    }
}

4.3 资源查找流程

mermaid

5. 性能优化与最佳实践

5.1 避免过度使用**通配符

**通配符会导致递归扫描整个目录结构,可能影响性能。在实际使用中,应尽量指定具体的目录层级。

5.2 资源缓存

PathMatchingResourcePatternResolver已经实现了类路径URL的缓存机制,避免重复解析:

protected Set<URL> obtainURL() {
    if (!this.loaded) {
        synchronized (this) {
            if (!this.loaded) {
                this.urls.addAll(ClassLoaderUtil.resolveClassPath(classLoader(this.getClass())));
                this.loaded = true;
            }
        }
    }
    return this.urls;
}

5.3 最佳实践

1.** 明确资源位置 :尽量指定明确的资源路径,减少通配符的使用 2. 利用缓存 :对于频繁使用的资源模式,考虑缓存查找结果 3. 避免重复扫描 :在应用启动时预加载所需资源,避免运行时重复扫描 4. 合理组织资源 **:按照功能模块组织资源文件,便于查找和维护

6. 总结与展望

6.1 主要知识点回顾

  • PathMatchingResourcePatternResolver是loveqq-framework中资源定位的核心组件
  • 基于Ant风格的路径匹配支持灵活的资源查找
  • AntPathMatcher实现了复杂的路径匹配算法,支持?***等通配符
  • 资源查找支持JAR包内资源和文件系统资源

6.2 未来优化方向

1.** 并行扫描 :引入并行处理机制,提高多资源同时查找的效率 2. 更丰富的匹配模式 :支持正则表达式等更多匹配模式 3. 资源变更监听 :增加资源变更监听功能,支持热加载 4. 缓存策略优化 **:实现更智能的缓存淘汰策略,平衡内存占用和查找效率

通过本文的深入分析,相信你已经对loveqq-framework中的资源定位机制有了全面的了解。在实际开发中,合理利用PathMatchingResourcePatternResolver可以极大提高资源管理的效率和灵活性。如果你在使用过程中遇到任何问题,欢迎在项目仓库提交issue,我们将及时响应和解答。

附录:常用API参考

PathMatchingResourcePatternResolver

方法说明
Set<URL> findResources(String pattern)根据模式查找资源
Set<URL> findResourcesByJar(JarFile jarFile, String pattern)查找JAR包内资源
Set<URL> findResourcesByFile(URL url, String pattern)查找文件系统资源

AntPathMatcher

方法说明
boolean matches(String pattern, String source)判断路径是否匹配模式
boolean matchStart(String pattern, String path)判断路径是否匹配模式前缀
String extractPathWithinPattern(String pattern, String path)提取模式匹配的路径部分

【免费下载链接】loveqq-framework 全新轻量级 ioc/aop/javafx 框架,更小,更强大。 该框架基本实现自我配置,具有更强大的复杂的条件bean注册推断,全框架复合注解支持;统一命令式/响应式编程风格,包含过滤器、拦截器等;提供 javafx mvvm 框架,可实现模型-数据的双向绑定,父子窗口生命周期绑定及监听;提供动态数据源配置支持;提供注解式缓存支持;默认提供 jar 包瘦身方式打包,支持 jar-index 启动。 【免费下载链接】loveqq-framework 项目地址: https://gitcode.com/kfyty725/loveqq-framework

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值