Skip to content

Commit 8487f05

Browse files
committed
增加循环依赖检测. 支持非连通依赖图的循环依赖检测
1 parent e8c25de commit 8487f05

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

spring-boot-data-aggregator-autoconfigure/src/main/java/io/github/lvyahui8/spring/autoconfigure/BeanAggregateAutoConfiguration.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.github.lvyahui8.spring.aggregate.interceptor.AggregateQueryInterceptor;
77
import io.github.lvyahui8.spring.aggregate.interceptor.AggregateQueryInterceptorChain;
88
import io.github.lvyahui8.spring.aggregate.interceptor.impl.AggregateQueryInterceptorChainImpl;
9+
import io.github.lvyahui8.spring.aggregate.model.DataConsumeDefinition;
910
import io.github.lvyahui8.spring.aggregate.model.DataProvideDefinition;
1011
import io.github.lvyahui8.spring.aggregate.repository.DataProviderRepository;
1112
import io.github.lvyahui8.spring.aggregate.repository.impl.DataProviderRepositoryImpl;
@@ -14,6 +15,7 @@
1415
import io.github.lvyahui8.spring.aggregate.util.DefinitionUtils;
1516
import io.github.lvyahui8.spring.annotation.DataProvider;
1617
import lombok.extern.slf4j.Slf4j;
18+
import org.apache.commons.lang3.StringUtils;
1719
import org.reflections.Reflections;
1820
import org.reflections.scanners.MethodAnnotationsScanner;
1921
import org.springframework.beans.BeansException;
@@ -30,7 +32,6 @@
3032
import org.springframework.core.annotation.Order;
3133
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
3234
import org.springframework.util.Assert;
33-
import org.springframework.util.StringUtils;
3435

3536
import java.lang.reflect.Method;
3637
import java.lang.reflect.Modifier;
@@ -39,6 +40,7 @@
3940
import java.util.concurrent.LinkedBlockingDeque;
4041
import java.util.concurrent.ThreadPoolExecutor;
4142
import java.util.concurrent.TimeUnit;
43+
import java.util.stream.Collectors;
4244

4345
/**
4446
@@ -65,11 +67,44 @@ public DataBeanAggregateQueryFacade dataBeanAggregateQueryFacade(
6567
return new DataBeanAggregateQueryFacadeImpl(dataBeanAggregateQueryService(dataProviderRepository));
6668
}
6769

70+
private void checkCycle(Map<String,Set<String>> graphAdjMap) {
71+
Map<String,Integer> visitStatusMap = new HashMap<>(graphAdjMap.size() * 2);
72+
Stack<String> stack = new Stack<>();
73+
for (Map.Entry<String, Set<String>> item : graphAdjMap.entrySet()) {
74+
if (visitStatusMap.containsKey(item.getKey())) {
75+
continue;
76+
}
77+
dfs(graphAdjMap,visitStatusMap,item.getKey());
78+
}
79+
}
80+
81+
private void dfs(Map<String,Set<String>> graphAdjMap,Map<String,Integer> visitStatusMap, String node) {
82+
if (visitStatusMap.containsKey(node)) {
83+
if(visitStatusMap.get(node) == 1) {
84+
List<String> relatedNodes = new ArrayList<>();
85+
for (Map.Entry<String,Integer> item : visitStatusMap.entrySet()) {
86+
if (item.getValue() == 1) {
87+
relatedNodes.add(item.getKey());
88+
}
89+
}
90+
throw new IllegalStateException("There are loops in the dependency graph. Related nodes:" + StringUtils.join(relatedNodes));
91+
}
92+
return ;
93+
}
94+
visitStatusMap.put(node,1);
95+
log.info("visited:{}", node);
96+
for (String relateNode : graphAdjMap.get(node)) {
97+
dfs(graphAdjMap,visitStatusMap,relateNode);
98+
}
99+
visitStatusMap.put(node,2);
100+
}
101+
68102
@Bean
69103
@ConditionalOnMissingBean
70104
public DataBeanAggregateQueryService dataBeanAggregateQueryService (
71105
@Qualifier("dataProviderRepository") DataProviderRepository dataProviderRepository) {
72106
if(properties.getBasePackages() != null) {
107+
Map<String,Set<String>> provideDependMap = new HashMap<>();
73108
for (String basePackage : properties.getBasePackages()) {
74109
Reflections reflections = new Reflections(basePackage, new MethodAnnotationsScanner());
75110
Set<Method> providerMethods = reflections.getMethodsAnnotatedWith(DataProvider.class);
@@ -80,13 +115,18 @@ public DataBeanAggregateQueryService dataBeanAggregateQueryService (
80115
Assert.isTrue(Modifier.isPublic(method.getModifiers()),"data provider method must be public");
81116
Assert.isTrue(! StringUtils.isEmpty(dataId),"data id must be not null!");
82117
DataProvideDefinition provider = DefinitionUtils.getProvideDefinition(method);
118+
83119
provider.setId(dataId);
84120
provider.setIdempotent(beanProvider.idempotent());
85121
provider.setTimeout(beanProvider.timeout() > 0 ? beanProvider.timeout() : properties.getDefaultTimeout());
122+
Assert.isTrue(! dataProviderRepository.contains(dataId), "Data providers with the same name are not allowed. dataId: " + dataId);
123+
provideDependMap.put(dataId,provider.getDepends().stream().map(DataConsumeDefinition::getId).collect(Collectors.toSet()));
86124
dataProviderRepository.put(provider);
87125
}
88126
}
127+
checkCycle(provideDependMap);
89128
}
129+
90130
DataBeanAggregateQueryServiceImpl service = new DataBeanAggregateQueryServiceImpl();
91131
RuntimeSettings runtimeSettings = new RuntimeSettings();
92132
runtimeSettings.setEnableLogging(properties.getEnableLogging() != null

spring-boot-data-aggregator-example/src/main/java/io/github/lvyahui8/spring/example/service/CategoryService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ public interface CategoryService {
1414
String getCategoryTitle(Category category, List<String> topCategoryNames);
1515

1616
List<String> getTopCategoryNames();
17+
18+
Object cycleDependA(Object dependB);
19+
20+
Object cycleDependB(Object dependA);
1721
}

spring-boot-data-aggregator-example/src/main/java/io/github/lvyahui8/spring/example/service/impl/CategoryServiceImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,17 @@ public List<String> getTopCategoryNames() {
4949
return Stream.of("feego", "figo", "sam").map(item -> item+r.nextInt(1000))
5050
.collect(Collectors.toList());
5151
}
52+
53+
54+
/// @DataProvider("cycleDependA")
55+
@Override
56+
public Object cycleDependA(@DataConsumer("cycleDependB") Object dependB) {
57+
return null;
58+
}
59+
60+
/// @DataProvider("cycleDependB")
61+
@Override
62+
public Object cycleDependB(@DataConsumer("cycleDependA") Object dependA) {
63+
return null;
64+
}
5265
}

0 commit comments

Comments
 (0)