feat[graalvm]: storage graalvm support

This commit is contained in:
godotg
2023-09-02 15:55:14 +08:00
parent c1fe64cdb2
commit 5bf5387ffa
8 changed files with 156 additions and 27 deletions
@@ -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 "";
}