SpringBoot集成jersey打包jar找不到class的處理方法
環(huán)境 java17 + springboot 3.x
如題,簡單來說,jersey官方希望用戶通過 register 的方式,將所有的資源類注冊到j(luò)ersey中,但是,一般開發(fā)中,可能定義了N個(gè)Resource類,一個(gè)一個(gè)的加入,太麻煩,也可能遺漏,解決方案就是,寫個(gè)方法,掃描到resource包下的所有資源類,然后 register 到j(luò)ersey中
特別注意,是 registerClasses 方法,不是 register 方法
以下兩種方法
使用java自帶的掃描
1.1 核心代碼
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
public class ClassUtil {
/**
* 由于spring boot 打包為jar包,jersey packages 無法掃描jar對應(yīng)的文件夾的文件,故自定義包掃描
*
* @return class[]
*/
public static Set<Class<?>> findAllClasses(String... scanPackages) {
Set<Class<?>> classes = new HashSet<>();
for (String scanPackage : scanPackages) {
ClassLoader loader = ClassUtil.class.getClassLoader();
Resource[] resources = new Resource[0];
try {
resources = scan(loader, scanPackage);
} catch (IOException e) {
log.error("加載class異常", e);
}
classes.addAll(convert(loader, resources));
}
return classes;
}
/**
* 掃描 jar 包
*
* @param loader ClassLoader
* @param packageName packageName
*/
private static 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 ClassLoader
* @param resource resource
*/
private static 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 轉(zhuǎn)換為 Set<Class>
*
* @param loader ClassLoader
* @param resources Resource
*/
private static 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;
}
}
1.2 使用
@Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// 注冊包掃描 這個(gè)方法在開發(fā)使用沒問題,但是打包jar后,找不到 class 文件
// packages("com.xxx.xxx.api");
// 定義掃描包含接口資源包
registerClasses(ClassUtil.findAllClasses("com.xxx.xxx.api"));
}
}
使用JerseyScan
2.1 核心代碼
注意中間的 scanner.addIncludeFilter(new AnnotationTypeFilter(Path.class)); 這一行代碼?。?!
public class JerseyServiceAutoScanner {
private JerseyServiceAutoScanner() {}
public static Class[] getPublishJerseyServiceClasses(ApplicationContext context, String... scanPackages) {
// 傳入applicationContext對象,在整個(gè)spring容器中撈我們需要的controller
// 傳入的第二個(gè)參數(shù)是可變參數(shù),字符串,用于傳入需要掃描的包路徑
List<Class> jerseyServiceClasses = new ArrayList<>();
if (scanPackages == null || scanPackages.length == 0) {
return jerseyServiceClasses.toArray(new Class[jerseyServiceClasses.size()]);
}
ClassPathScanningCandidateComponentProvider scanner = new JerseyScanningComponentProvider(false);
// 我只需要掃描使用了@Path注解的controller,如果還有其他的組合條件,可以在這里增加
scanner.addIncludeFilter(new AnnotationTypeFilter(Path.class));
for (var scanPackage : scanPackages) {
jerseyServiceClasses.addAll(scanner.findCandidateComponents(scanPackage).stream()
.map(beanDefinition -> ClassUtils
.resolveClassName(beanDefinition.getBeanClassName(), applicationContext.getClassLoader()))
.collect(Collectors.toSet()));
}
// 返回符合條件的spring容器中的全部的類對象
return jerseyServiceClasses.toArray(new Class[jerseyServiceClasses.size()]);
}
private static class JerseyScanningComponentProvider extends ClassPathScanningCandidateComponentProvider {
public JerseyScanningComponentProvider(boolean useDefaultFilters) {
super(useDefaultFilters);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
// 注意這里的值,最好debug一下,我使用的時(shí)候,只有第一個(gè)metadata.isIndependent()是true
return (metadata.isIndependent() && metadata.isAbstract() && !beanDefinition.getMetadata().isAnnotation());
}
}
}
2.2 引用
@Configuration
public class JerseyConfig extends ResourceConfig implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@PostConstruct
public void init() {
registerClasses(JerseyServiceAutoScanner.getPublishJerseyServiceClasses(applicationContext, "com.xxx.xxx.api"));
}
public JerseyConfig() {
// 。。。 其他的 。。。
}
}
完結(jié)?。?!
以上就是SpringBoot集成jersey打包jar找不到class的處理方法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot jersey找不到class的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mybatis-plus IdWorker生成的Id和返回給前臺的不一致的解決
這篇文章主要介紹了mybatis-plus IdWorker生成的Id和返回給前臺的不一致的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法
在Java中將String類型轉(zhuǎn)換為int類型是一個(gè)常見的操作,下面這篇文章主要給大家介紹了關(guān)于Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
Java前后端的JSON傳輸方式(前后端JSON格式轉(zhuǎn)換)
這篇文章主要介紹了Java前后端的JSON傳輸方式(前后端JSON格式轉(zhuǎn)換),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

