@@ -50,6 +50,8 @@ class SpringUtExecutionInstrumentation(
50
50
private val userSourcesClassLoader = URLClassLoader (buildDirs, null )
51
51
52
52
private val relatedBeansCache = mutableMapOf<Class <* >, Set <String >>()
53
+ private val nonRepositoryRelatedBeansCache = mutableMapOf<Class <* >, Set <String >>()
54
+ private val repositoryDescriptionsCache = mutableMapOf<Class <* >, Set <SpringRepositoryId >>()
53
55
54
56
private val springApi: SpringApi get() = instrumentationContext.springApi
55
57
@@ -69,7 +71,7 @@ class SpringUtExecutionInstrumentation(
69
71
}
70
72
}
71
73
72
- fun tryLoadingSpringContext (): ConcreteContextLoadingResult {
74
+ private fun tryLoadingSpringContext (): ConcreteContextLoadingResult {
73
75
val apiProviderResult = instrumentationContext.springApiProviderResult
74
76
return ConcreteContextLoadingResult (
75
77
contextLoaded = apiProviderResult.result.isSuccess,
@@ -100,21 +102,24 @@ class SpringUtExecutionInstrumentation(
100
102
detectedMockingCandidates = emptySet()
101
103
)
102
104
103
- getRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) }
104
- return delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases ->
105
- phasesWrapper {
106
- // NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases,
107
- // so they are executed in one thread with method under test
108
- // NB! beforeTestMethod() and afterTestMethod() are executed without timeout, because:
109
- // - if the invokeBasePhases() times out, we still want to execute afterTestMethod()
110
- // - first call to beforeTestMethod() can take significant amount of time due to class loading & transformation
111
- executePhaseWithoutTimeout(SpringBeforeTestMethodPhase ) { springApi.beforeTestMethod() }
112
- try {
113
- invokeBasePhases()
114
- } finally {
115
- executePhaseWithoutTimeout(SpringAfterTestMethodPhase ) { springApi.afterTestMethod() }
105
+ return try {
106
+ delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases ->
107
+ phasesWrapper {
108
+ // NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases,
109
+ // so they are executed in one thread with method under test
110
+ // NB! beforeTestMethod() and afterTestMethod() are executed without timeout, because:
111
+ // - if the invokeBasePhases() times out, we still want to execute afterTestMethod()
112
+ // - first call to beforeTestMethod() can take significant amount of time due to class loading & transformation
113
+ executePhaseWithoutTimeout(SpringBeforeTestMethodPhase ) { springApi.beforeTestMethod() }
114
+ try {
115
+ invokeBasePhases()
116
+ } finally {
117
+ executePhaseWithoutTimeout(SpringAfterTestMethodPhase ) { springApi.afterTestMethod() }
118
+ }
116
119
}
117
120
}
121
+ } finally {
122
+ getNonRepositoryRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) }
118
123
}
119
124
}
120
125
@@ -131,8 +136,12 @@ class SpringUtExecutionInstrumentation(
131
136
.also { logger.info { " Detected relevant beans for class ${clazz.name} : $it " } }
132
137
}
133
138
134
- fun getRepositoryDescriptions (classId : ClassId ): Set <SpringRepositoryId > {
135
- val relevantBeanNames = getRelevantBeans(classId.jClass)
139
+ private fun getNonRepositoryRelevantBeans (clazz : Class <* >): Set <String > = nonRepositoryRelatedBeansCache.getOrPut(clazz) {
140
+ getRelevantBeans(clazz).subtract(getRepositoryDescriptions(clazz).map { it.repositoryBeanName }.toSet())
141
+ }
142
+
143
+ private fun getRepositoryDescriptions (clazz : Class <* >): Set <SpringRepositoryId > = repositoryDescriptionsCache.getOrPut(clazz) {
144
+ val relevantBeanNames = getRelevantBeans(clazz)
136
145
val repositoryDescriptions = springApi.resolveRepositories(relevantBeanNames.toSet(), userSourcesClassLoader)
137
146
return repositoryDescriptions.map { repositoryDescription ->
138
147
SpringRepositoryId (
@@ -168,7 +177,7 @@ class SpringUtExecutionInstrumentation(
168
177
override fun InstrumentedProcessModel.setupAdditionalRdResponses (kryoHelper : KryoHelper , watchdog : IdleWatchdog ) {
169
178
watchdog.measureTimeForActiveCall(getRelevantSpringRepositories, " Getting Spring repositories" ) { params ->
170
179
val classId: ClassId = kryoHelper.readObject(params.classId)
171
- val repositoryDescriptions = getRepositoryDescriptions(classId)
180
+ val repositoryDescriptions = getRepositoryDescriptions(classId.jClass )
172
181
GetSpringRepositoriesResult (kryoHelper.writeObject(repositoryDescriptions))
173
182
}
174
183
watchdog.measureTimeForActiveCall(tryLoadingSpringContext, " Trying to load Spring application context" ) { params ->
0 commit comments