001/** 002 * Copyright (c) 2022-2023, Mybatis-Flex ([email protected]). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.spring.boot; 017 018import org.apache.ibatis.io.VFS; 019import org.springframework.core.io.Resource; 020import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 021import org.springframework.core.io.support.ResourcePatternResolver; 022 023import java.io.IOException; 024import java.io.UncheckedIOException; 025import java.net.URL; 026import java.net.URLDecoder; 027import java.nio.charset.Charset; 028import java.text.Normalizer; 029import java.util.List; 030import java.util.stream.Collectors; 031import java.util.stream.Stream; 032 033/** 034 * Mybatis 的 VFS 支持 035 */ 036public class SpringBootVFS extends VFS { 037 038 private static Charset urlDecodingCharset; 039 private final ResourcePatternResolver resourceResolver; 040 041 static { 042 setUrlDecodingCharset(Charset.defaultCharset()); 043 } 044 045 public SpringBootVFS() { 046 this.resourceResolver = new PathMatchingResourcePatternResolver(getClass().getClassLoader()); 047 } 048 049 @Override 050 public boolean isValid() { 051 return true; 052 } 053 054 @Override 055 protected List<String> list(URL url, String path) throws IOException { 056 String urlString = URLDecoder.decode(url.toString(), urlDecodingCharset.name()); 057 String baseUrlString = urlString.endsWith("/") ? urlString : urlString.concat("/"); 058 Resource[] resources = resourceResolver.getResources(baseUrlString + "**/*.class"); 059 return Stream.of(resources).map(resource -> preserveSubpackageName(baseUrlString, resource, path)) 060 .collect(Collectors.toList()); 061 } 062 063 /** 064 * Set the charset for decoding an encoded URL string. 065 * <p> 066 * Default is system default charset. 067 * </p> 068 * 069 * @param charset 070 * the charset for decoding an encoded URL string 071 * 072 * @since 2.3.0 073 */ 074 public static void setUrlDecodingCharset(Charset charset) { 075 urlDecodingCharset = charset; 076 } 077 078 private static String preserveSubpackageName(final String baseUrlString, final Resource resource, 079 final String rootPath) { 080 try { 081 return rootPath + (rootPath.endsWith("/") ? "" : "/") + Normalizer 082 .normalize(URLDecoder.decode(resource.getURL().toString(), urlDecodingCharset.name()), Normalizer.Form.NFC) 083 .substring(baseUrlString.length()); 084 } catch (IOException e) { 085 throw new UncheckedIOException(e); 086 } 087 } 088 089}