最近公司內部系統要作數據對接,故使用 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; }