mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-08-05 18:25:26 +00:00
feat[graalvm]: storage graalvm support
This commit is contained in:
@@ -12,13 +12,24 @@
|
||||
|
||||
package com.zfoo.boot;
|
||||
|
||||
import com.zfoo.protocol.util.ClassUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.storage.StorageContext;
|
||||
import com.zfoo.storage.manager.StorageManager;
|
||||
import com.zfoo.storage.model.anno.GraalvmNativeResource;
|
||||
import com.zfoo.storage.model.config.StorageConfig;
|
||||
import com.zfoo.storage.model.resource.ResourceData;
|
||||
import com.zfoo.storage.model.resource.ResourceEnum;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.aot.hint.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
@@ -26,8 +37,11 @@ import org.springframework.context.annotation.Configuration;
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(StorageConfig.class)
|
||||
@ImportRuntimeHints(StorageAutoConfiguration.StorageHints.class)
|
||||
public class StorageAutoConfiguration {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StorageAutoConfiguration.class);
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(StorageConfig.class)
|
||||
@@ -43,4 +57,46 @@ public class StorageAutoConfiguration {
|
||||
return new StorageContext();
|
||||
}
|
||||
|
||||
|
||||
// Register runtime hints for the token library
|
||||
public static class StorageHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
logger.info("storage aot runtime hints register");
|
||||
|
||||
var classes = new HashSet<Class<?>>();
|
||||
classes.add(ResourceData.class);
|
||||
|
||||
try {
|
||||
for (var className : ClassUtils.getAllClasses("")) {
|
||||
try {
|
||||
var clazz = Class.forName(className);
|
||||
if (!clazz.isAnnotationPresent(GraalvmNativeResource.class)) {
|
||||
continue;
|
||||
}
|
||||
classes.add(clazz);
|
||||
classes.addAll(ClassUtils.relevantClass(clazz));
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
for (var clazz : classes) {
|
||||
this.bindingRegistrar.registerReflectionHints(hints.reflection(), clazz);
|
||||
logger.info("storage aot hints register serialization [{}]", clazz);
|
||||
}
|
||||
|
||||
for (var resource : ResourceEnum.values()) {
|
||||
var include = StringUtils.format("*.{}", resource.getType());
|
||||
hints.resources().registerPattern(include);
|
||||
logger.info("storage aot hints register resources [{}]", include);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,17 +22,19 @@ import com.zfoo.protocol.util.ReflectionUtils;
|
||||
import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.storage.StorageContext;
|
||||
import com.zfoo.storage.model.anno.Id;
|
||||
import com.zfoo.storage.model.anno.GraalvmNativeResource;
|
||||
import com.zfoo.storage.model.anno.ResInjection;
|
||||
import com.zfoo.storage.model.config.StorageConfig;
|
||||
import com.zfoo.storage.model.resource.ResourceEnum;
|
||||
import com.zfoo.storage.model.vo.ResourceDef;
|
||||
import com.zfoo.storage.model.vo.Storage;
|
||||
import com.zfoo.util.GraalVmUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
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.ClassMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -82,20 +84,12 @@ public class StorageManager implements IStorageManager {
|
||||
public void initBefore() {
|
||||
var resourceDefinitionMap = new HashMap<Class<?>, ResourceDef>();
|
||||
|
||||
// 扫描Excel的class类文件
|
||||
var clazzNameSet = scanResourceAnno(StringUtils.tokenize(storageConfig.getScanPackage(), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
|
||||
// 获取需要被映射的Excel的class类文件
|
||||
var clazzSet = resourceClass();
|
||||
|
||||
// 通过class类文件扫描excel文件地址
|
||||
for (var clazzName : clazzNameSet) {
|
||||
Class<?> resourceClazz;
|
||||
try {
|
||||
resourceClazz = Class.forName(clazzName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// 无法获取资源类
|
||||
throw new RuntimeException(StringUtils.format("Unable to get resource [class:{}]", clazzName));
|
||||
}
|
||||
|
||||
var resourceFile = scanResourceFile(resourceClazz);
|
||||
for (var resourceClazz : clazzSet) {
|
||||
var resourceFile = resource(resourceClazz);
|
||||
ResourceDef resourceDef = new ResourceDef(resourceClazz, resourceFile);
|
||||
if (resourceDefinitionMap.containsKey(resourceClazz)) {
|
||||
// 类的资源定义已经存在
|
||||
@@ -148,7 +142,8 @@ public class StorageManager implements IStorageManager {
|
||||
var applicationContext = StorageContext.getApplicationContext();
|
||||
var componentBeans = applicationContext.getBeansWithAnnotation(Component.class);
|
||||
for (var bean : componentBeans.values()) {
|
||||
ReflectionUtils.filterFieldsInClass(bean.getClass(), field -> field.isAnnotationPresent(ResInjection.class), field -> {
|
||||
var clazz = bean.getClass();
|
||||
ReflectionUtils.filterFieldsInClass(clazz, field -> field.isAnnotationPresent(ResInjection.class), field -> {
|
||||
Type type = field.getGenericType();
|
||||
|
||||
if (!(type instanceof ParameterizedType)) {
|
||||
@@ -223,12 +218,23 @@ public class StorageManager implements IStorageManager {
|
||||
return getStorageConfig();
|
||||
}
|
||||
|
||||
private Set<String> scanResourceAnno(String[] scanPackages) {
|
||||
var resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
var metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
|
||||
private Set<Class<?>> resourceClass() {
|
||||
// graalvm环境 PathMatchingResourcePatternResolver/CachingMetadataReaderFactory 无法使用,所以直接在spring容器中获取
|
||||
if (GraalVmUtils.isGraalVM()) {
|
||||
var applicationContext = StorageContext.getApplicationContext();
|
||||
var nativeResources = applicationContext.getBeansWithAnnotation(GraalvmNativeResource.class);
|
||||
return nativeResources.values().stream().map(it -> it.getClass()).collect(Collectors.toSet());
|
||||
} else {
|
||||
return scanResourceAnno();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Class<?>> scanResourceAnno() {
|
||||
var scanPackages = StringUtils.tokenize(storageConfig.getScanPackage(), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
var result = new HashSet<Class<?>>();
|
||||
try {
|
||||
var result = new HashSet<String>();
|
||||
var resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
var metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
|
||||
for (var scanPackage : scanPackages) {
|
||||
var packageSearchPath = "classpath*:" + scanPackage.replace(StringUtils.PERIOD, StringUtils.SLASH) + StringUtils.SLASH + SUFFIX_PATTERN;
|
||||
var resources = resourcePatternResolver.getResources(packageSearchPath);
|
||||
@@ -239,17 +245,28 @@ public class StorageManager implements IStorageManager {
|
||||
var annoMeta = metadataReader.getAnnotationMetadata();
|
||||
if (annoMeta.hasAnnotation(resourceName)) {
|
||||
ClassMetadata clazzMeta = metadataReader.getClassMetadata();
|
||||
result.add(clazzMeta.getClassName());
|
||||
Class<?> resourceClazz = Class.forName(clazzMeta.getClassName());
|
||||
result.add(resourceClazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to read resource information", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Resource resource(Class<?> clazz) {
|
||||
if (GraalVmUtils.isGraalVM()) {
|
||||
var resourceLoader = new DefaultResourceLoader();
|
||||
var resource = resourceLoader.getResource(clazz.getAnnotation(GraalvmNativeResource.class).value());
|
||||
return resource;
|
||||
} else {
|
||||
return scanResourceFile(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
private Resource scanResourceFile(Class<?> clazz) {
|
||||
var resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
var metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.zfoo.storage.model.anno;
|
||||
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 指定文件列名,不指定则默认列名与字段名一致
|
||||
* EN: Specify the file column name. If not specified, the default column name will be consistent with the field name.
|
||||
* CN: 指定文件列名,不指定则默认列名与字段名一致
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD})
|
||||
@Reflective
|
||||
public @interface ExcelFieldName {
|
||||
String value();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package com.zfoo.storage.model.anno;
|
||||
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* graalvm build must use this annotation to specify path, because native build cannot scan classes and files
|
||||
*
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Reflective
|
||||
@Component
|
||||
public @interface GraalvmNativeResource {
|
||||
|
||||
/**
|
||||
* graalvm build must specify resource path
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -14,10 +14,12 @@
|
||||
package com.zfoo.storage.model.anno;
|
||||
|
||||
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
* primary key
|
||||
*
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
@@ -26,5 +28,6 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD})
|
||||
@Reflective
|
||||
public @interface Id {
|
||||
}
|
||||
|
||||
@@ -13,10 +13,13 @@
|
||||
|
||||
package com.zfoo.storage.model.anno;
|
||||
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 索引,索引的名称使用字段属性的名称,用HaspMap实现
|
||||
* EN: the name of the index uses the name of the field attribute, implemented with HaspMap
|
||||
* CN: 索引的名称使用字段属性的名称,用HaspMap实现
|
||||
*
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
@@ -25,6 +28,7 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD})
|
||||
@Reflective
|
||||
public @interface Index {
|
||||
|
||||
boolean unique() default false;
|
||||
|
||||
@@ -18,7 +18,8 @@ import org.springframework.aot.hint.annotation.Reflective;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 静态数据的注入
|
||||
* EN: Injection of static data
|
||||
* CN: 静态数据的注入
|
||||
*
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
|
||||
@@ -18,9 +18,9 @@ import org.springframework.aot.hint.annotation.Reflective;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 资源注解
|
||||
* 可以指定对应的资源文件名(只指定文件名,不需要文件后缀)
|
||||
* 如果不指定资源文件名,则默认通过扫描路径获取与类名相同的文件资源
|
||||
*
|
||||
* EN: You can specify the corresponding resource file name (only the file name is specified, no file suffix is required)
|
||||
* CN: 可以指定对应的资源文件名(只指定文件名,不需要文件后缀),如果不指定资源文件名,则默认通过扫描路径获取与类名相同的文件资源
|
||||
*
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
@@ -31,4 +31,9 @@ import java.lang.annotation.*;
|
||||
@Reflective
|
||||
public @interface Resource {
|
||||
String alias() default "";
|
||||
|
||||
/**
|
||||
* graalvm build must specified path
|
||||
*/
|
||||
String nativeClassPath() default "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user