feat: 彻底解耦reflections

This commit is contained in:
wangyu 2024-07-01 16:53:22 +08:00
parent 274febda39
commit 7fcd7c6e02
9 changed files with 88 additions and 103 deletions

View File

@ -13,7 +13,11 @@ import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
/**
* 内部的包扫描器提供特定注解扫描
@ -61,6 +65,13 @@ public class ClassPathResourceScanner extends ClassPathScanningCandidateComponen
return true;
}
/**
* 解析具体类型
*
* @param bf bean定义
* @param cl 类加载器
* @return 结果
*/
private Class<?> resolveType(BeanDefinition bf, ClassLoader cl) {
if (null != bf.getBeanClassName()) {
try {
@ -78,23 +89,19 @@ public class ClassPathResourceScanner extends ClassPathScanningCandidateComponen
* @param packageNames 包名
* @return 结果
*/
public Set<Class<?>> scan(Collection<String> packageNames) {
public Stream<Class<?>> scan(Collection<String> packageNames) {
// 获取扫描器的ClassLoader保证同源
ClassLoader cl = this.getClass().getClassLoader();
Set<Class<?>> scanned = new HashSet<>();
for (String packageName : packageNames) {
Set<BeanDefinition> bfs = findCandidateComponents(packageName);
// 不存在不要浪费性能
if (CollectionUtils.isEmpty(bfs)) continue;
// 代理并生成子类并注册到ioc容器
for (BeanDefinition bf : bfs) {
Class<?> resolved = resolveType(bf, cl);
if (null != resolved) {
scanned.add(resolved);
}
}
}
return scanned;
Stream<Class<?>> stream = packageNames.stream()
.flatMap(packageName -> {
Set<BeanDefinition> bfs = findCandidateComponents(packageName);
// 不存在不要浪费性能
if (CollectionUtils.isEmpty(bfs)) return Stream.empty();
// 代理并生成子类并注册到ioc容器
return bfs.stream().map(bf -> resolveType(bf, cl)).filter(Objects::nonNull);
});
// 额外返回以保证类型正确
return stream.distinct();
}
/**
@ -103,7 +110,7 @@ public class ClassPathResourceScanner extends ClassPathScanningCandidateComponen
* @param packageNames 包名
* @return 结果
*/
public Set<Class<?>> scan(String... packageNames) {
public Stream<Class<?>> scan(String... packageNames) {
return scan(Arrays.asList(packageNames));
}
}

View File

@ -25,17 +25,18 @@ public class EnumConfig implements InitializingBean, ImportBeanDefinitionRegistr
public EnumConfig() {
// 得到枚举类
Set<Class<?>> classSet = ClassPathResourceScanner.forSuperType(NamedEnum.class).scan("com.flyfish.project");
// 注入
classSet.stream().filter(clazz -> ClassUtils.isAssignable(clazz, Enum.class)).forEach(clazz -> {
String name = StringFormats.camel2Line(clazz.getSimpleName());
List<EnumValue> values = Arrays.stream(clazz.getEnumConstants()).reduce(new ArrayList<>(), (result, item) -> {
result.add(new EnumValue(((Enum<?>) item).name(), ((NamedEnum) item).getName()));
return result;
}, (a, b) -> a);
this.mapper.put(name, values.stream().collect(Collectors.toMap(EnumValue::getValue, EnumValue::getText)));
this.values.put(name, values);
});
ClassPathResourceScanner.forSuperType(NamedEnum.class)
.scan("com.flyfish.project")
.filter(clazz -> ClassUtils.isAssignable(clazz, Enum.class))
.forEach(clazz -> {
String name = StringFormats.camel2Line(clazz.getSimpleName());
List<EnumValue> values = Arrays.stream(clazz.getEnumConstants()).reduce(new ArrayList<>(), (result, item) -> {
result.add(new EnumValue(((Enum<?>) item).name(), ((NamedEnum) item).getName()));
return result;
}, (a, b) -> a);
this.mapper.put(name, values.stream().collect(Collectors.toMap(EnumValue::getValue, EnumValue::getText)));
this.values.put(name, values);
});
}
@Override

View File

@ -34,9 +34,5 @@
<version>${revision}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -4,29 +4,25 @@ import com.flyfish.framework.annotations.DictValue;
import com.flyfish.framework.dict.domain.Dictionary;
import com.flyfish.framework.dict.domain.DictionaryValue;
import com.flyfish.framework.dict.service.DictionaryService;
import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.enums.BlankEnum;
import com.flyfish.framework.enums.NamedEnum;
import com.flyfish.framework.utils.Assert;
import com.flyfish.framework.utils.ClassPathResourceScanner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 字典处理器
@ -45,28 +41,24 @@ public class DictionaryProcessor implements InitializingBean {
public void afterPropertiesSet() {
String[] basePackages = DictionaryConfig.basePackages();
Assert.notNull(basePackages, "未指定明确的字典扫描路径!");
Collection<URL> urls = Arrays.stream(basePackages).flatMap(pack -> ClasspathHelper.forPackage(pack).stream())
.collect(Collectors.toList());
Reflections reflections = new Reflections(new ConfigurationBuilder()
.setUrls(urls)
.setScanners(Scanners.FieldsAnnotated));
Set<Field> fields = reflections.getFieldsAnnotatedWith(DictValue.class);
if (CollectionUtils.isNotEmpty(fields)) {
Flux.fromIterable(fields)
.map(field -> field.getAnnotation(DictValue.class))
.filter(annotation -> null != annotation && StringUtils.isNotBlank(annotation.value()) &&
BlankEnum.class != annotation.enumType())
.distinct(DictValue::enumType)
// 查找是否存在不存在插入存在无视
.flatMap(annotation -> dictionaryService
.getByCode(annotation.value())
.map(dictionary -> applyDictProps(dictionary, annotation))
.switchIfEmpty(Mono.fromCallable(() -> applyDictProps(new Dictionary(), annotation)))
)
.collectList()
.flatMapMany(dictionaryService::updateBatch)
.subscribe();
}
Stream<DictValue> annotations = ClassPathResourceScanner.forSuperType(Domain.class)
.scan(basePackages)
.flatMap(clazz -> Arrays.stream(FieldUtils.getFieldsWithAnnotation(clazz, DictValue.class)))
.map(field -> field.getAnnotation(DictValue.class))
.filter(annotation -> null != annotation && StringUtils.isNotBlank(annotation.value()) &&
BlankEnum.class != annotation.enumType());
// 查找并更新
Flux.fromStream(annotations)
.distinct(DictValue::enumType)
// 查找是否存在不存在插入存在无视
.flatMap(annotation -> dictionaryService
.getByCode(annotation.value())
.map(dictionary -> applyDictProps(dictionary, annotation))
.switchIfEmpty(Mono.fromCallable(() -> applyDictProps(new Dictionary(), annotation)))
)
.collectList()
.flatMapMany(dictionaryService::updateBatch)
.subscribe();
}
/**

View File

@ -132,24 +132,18 @@ public class WebSecurityConfig {
ReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
return http
.securityContextRepository(contextRepository())
.authorizeExchange()
.pathMatchers(ArrayUtils.addAll(properties.getAllowUris(), "/api/logout", "/api/login")).permitAll()
.pathMatchers("/api/users/**").authenticated()
.anyExchange().authenticated()
.and()
.formLogin() // 配置登录节点
.disable()
.httpBasic()
.disable()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(new JsonLogoutSuccessHandler(authenticationAuditor, tokenProvider))
.and()
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
.accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.UNAUTHORIZED))
.and()
.csrf().disable()
.authorizeExchange(spec -> spec.pathMatchers(ArrayUtils.addAll(properties.getAllowUris(), "/api/logout", "/api/login")).permitAll()
.pathMatchers("/api/users/**").authenticated()
.anyExchange().authenticated())
.formLogin(spec -> spec.disable()) // 配置登录节点
.httpBasic(spec -> spec.disable())
.logout(spec -> spec
.logoutUrl("/api/logout")
.logoutSuccessHandler(new JsonLogoutSuccessHandler(authenticationAuditor, tokenProvider)))
.exceptionHandling(spec -> spec
.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
.accessDeniedHandler(new HttpStatusServerAccessDeniedHandler(HttpStatus.UNAUTHORIZED)))
.csrf(spec -> spec.disable())
.addFilterAt(
configure(authenticationManager, authenticationAuditor, authenticationConverter),
SecurityWebFiltersOrder.FORM_LOGIN)

View File

@ -84,7 +84,7 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
// 寻找基本包路径
String basePackage = basePackages.stream().findFirst().orElse("com.flyfish.project");
// 获取被注解的类开始编译
Set<Class<?>> classes = ClassPathResourceScanner.forAnnotation(RestBean.class).scan(basePackages);
Stream<Class<?>> classes = ClassPathResourceScanner.forAnnotation(RestBean.class).scan(basePackages);
// 并发编译快速接入准备注册的repo
Map<RestBeanCandidate, List<String>> compiledClasses = compile(basePackage, classes);
// 从repo开始注册bean
@ -110,7 +110,7 @@ public class RestBeanAutoConfigure implements ImportBeanDefinitionRegistrar, Res
* @param classes 类集合
* @return 结果
*/
private Map<RestBeanCandidate, List<String>> compile(String basePackage, Set<Class<?>> classes) {
private Map<RestBeanCandidate, List<String>> compile(String basePackage, Stream<Class<?>> classes) {
Map<RestBeanCandidate, List<String>> compiled = new ConcurrentHashMap<>();
classes.forEach(clazz -> {
RestBean restBean = clazz.getDeclaredAnnotation(RestBean.class);

View File

@ -8,12 +8,11 @@ import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
public class JacksonRedisSerializerFactory {
public static Jackson2JsonRedisSerializer<Object> produce() {
// 设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
// 构造object mapper
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
// 设置序列化
return new Jackson2JsonRedisSerializer<Object>(om, Object.class);
}
}

View File

@ -30,17 +30,19 @@ public class EnumController {
private final Map<String, List<EnumValue>> values = new HashMap<>();
public EnumController() {
Set<Class<?>> classSet = ClassPathResourceScanner.forSuperType(NamedEnum.class).scan("com.flyfish.project", "com.flyfish.framework");
// 注入
classSet.stream().filter(clazz -> ClassUtils.isAssignable(clazz, Enum.class)).forEach(clazz -> {
String name = StringFormats.camel2Line(ClassUtils.getShortClassName(clazz));
List<EnumValue> values = Arrays.stream(clazz.getEnumConstants()).reduce(new ArrayList<>(), (result, item) -> {
result.add(new EnumValue(((Enum<?>) item).name(), ((NamedEnum) item).getName()));
return result;
}, (a, b) -> a);
this.mapper.put(name, values.stream().collect(Collectors.toMap(EnumValue::getValue, EnumValue::getText)));
this.values.put(name, values);
});
ClassPathResourceScanner.forSuperType(NamedEnum.class)
.scan("com.flyfish.project", "com.flyfish.framework")
.filter(clazz -> ClassUtils.isAssignable(clazz, Enum.class))
.forEach(clazz -> {
String name = StringFormats.camel2Line(ClassUtils.getShortClassName(clazz));
List<EnumValue> values = Arrays.stream(clazz.getEnumConstants()).reduce(new ArrayList<>(), (result, item) -> {
result.add(new EnumValue(((Enum<?>) item).name(), ((NamedEnum) item).getName()));
return result;
}, (a, b) -> a);
this.mapper.put(name, values.stream().collect(Collectors.toMap(EnumValue::getValue, EnumValue::getText)));
this.values.put(name, values);
});
}
/**

View File

@ -25,7 +25,6 @@
<java.version>17</java.version>
<jasypt.version>3.1.0</jasypt.version>
<jjwt.version>0.12.6</jjwt.version>
<reflection.version>0.10.2</reflection.version>
<r2dbc-mysql.version>1.1.3</r2dbc-mysql.version>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<captcha.version>1.3.0</captcha.version>
@ -112,11 +111,6 @@
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflection.version}</version>
</dependency>
<dependency>
<groupId>io.springboot.plugin</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>