jersey在 spring boot 添加 packages 掃描路徑支持

最近公司內部系統要作數據對接,故使用 jersey 來作 restful webservice 接口設計。因爲 spring boot 已經集成 jersey,估計直接導入 spring-boot-starter-jersey 就行了。java

在測試時候除了遇到中文亂碼以外花費了比較長的時間,其他暫時沒遇到大的問題。然而發佈的時候發現了一個坑。web

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig(){
        //registerClasses(findAllClasses());
        packages("com.xx");
        // 註冊日誌
        register(LoggingFeature.class);
        // 異常處理
        register(ExceptionHandler.class);
        // 跨域過濾器註冊
        register(CorsFilter.class);

    }
}

jersey 是使用 register(Class clazz) 或者 packages("packageName") 來註冊或者掃描來加載所需的類到容器中的。然而在實際項目中,使用 register(Class clazz) 會寫大量的註冊類,依賴性太強,並且在maven多模塊中更很差處理。spring

而使用 packages 來掃描,在開發測試的時候倒沒發現什麼問題,可是使用 spring boot 打成 jar 包運行時,會產生 FileNotFound 的錯誤,這是由於 使用 jersey 的 packages 掃描的機制是沒法掃描到 jar 中的 class 文件的。跨域

因爲使用的是 spring boot,spring 的 component scan 是能夠作到的。故參考 spring 掃描機制的源碼,將 jar 中的 class 文件加載進來就行了。restful

代碼以下:maven

import lombok.extern.slf4j.Slf4j;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * @author gongtao
 * @version 2018-04-23 10:15
 **/
@Configuration
@Slf4j
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig(){
        //掃描註冊
        registerClasses(findAllClasses());
        // 註冊日誌
        register(LoggingFeature.class);
        // 異常處理
        register(ExceptionHandler.class);
        // 跨域過濾器註冊
        register(CorsFilter.class);

    }

    /**
     * 因爲spring boot 打包爲jar包,jersey packages 沒法掃描jar對應的文件夾的文件,故自定義包掃描
     * @return
     */
    private Set<Class<?>> findAllClasses(){
        String scanPackages = "com.xxx.eoms.innerinterface.interfaces.resource.*";
        ClassLoader loader = this.getClass().getClassLoader();
        Resource[] resources = new Resource[0];
        try {
            resources = scan(loader, scanPackages);
        } catch (IOException e) {
            log.error("加載class異常",e);
        }
        return convert(loader, resources);
    }

    /**
     * 掃描 jar 包
     * @param loader
     * @param packageName
     * @return
     * @throws IOException
     */
    private Resource[] scan(ClassLoader loader, String packageName) throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(loader);
        String pattern = "classpath*:" + ClassUtils.convertClassNameToResourcePath(packageName) + "/**/*.class";
        return resolver.getResources(pattern);
    }

    /**
     * 加載 class
     * @param loader
     * @param resource
     * @return
     */
    private Class<?> loadClass(ClassLoader loader,Resource resource) {
        try {
            CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(loader);
            MetadataReader reader = metadataReaderFactory.getMetadataReader(resource);
            return ClassUtils.forName(reader.getClassMetadata().getClassName(), loader);
        } catch (LinkageError | ClassNotFoundException e) {
            if (log.isDebugEnabled()) {
                log.debug("Ignoring candidate class resource " + resource + " due to " + e);
            }
            return null;
        } catch (Throwable e) {
            if (log.isWarnEnabled()) {
                log.warn("Unexpected failure when loading class resource " + resource, e);
            }
            return null;
        }
    }

    /**
     * resources 轉換爲 Set<Class>
     * @param loader
     * @param resources
     * @return
     */
    private Set<Class<?>> convert(ClassLoader loader,Resource[] resources){
        Set<Class<?>> classSet = new HashSet<>(resources.length);
        for (Resource resource : resources){
            Class<?> clazz = loadClass(loader, resource);
            if (clazz != null){
                classSet.add(clazz);
            }
        }
        return classSet;
    }
相關文章
相關標籤/搜索