@@ -50,6 +50,8 @@ class SpringUtExecutionInstrumentation(
5050 private val userSourcesClassLoader = URLClassLoader (buildDirs, null )
5151
5252 private val relatedBeansCache = mutableMapOf<Class <* >, Set <String >>()
53+ private val nonRepositoryRelatedBeansCache = mutableMapOf<Class <* >, Set <String >>()
54+ private val repositoryDescriptionsCache = mutableMapOf<Class <* >, Set <SpringRepositoryId >>()
5355
5456 private val springApi: SpringApi get() = instrumentationContext.springApi
5557
@@ -69,7 +71,7 @@ class SpringUtExecutionInstrumentation(
6971 }
7072 }
7173
72- fun tryLoadingSpringContext (): ConcreteContextLoadingResult {
74+ private fun tryLoadingSpringContext (): ConcreteContextLoadingResult {
7375 val apiProviderResult = instrumentationContext.springApiProviderResult
7476 return ConcreteContextLoadingResult (
7577 contextLoaded = apiProviderResult.result.isSuccess,
@@ -100,21 +102,24 @@ class SpringUtExecutionInstrumentation(
100102 detectedMockingCandidates = emptySet()
101103 )
102104
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+ }
116119 }
117120 }
121+ } finally {
122+ getNonRepositoryRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) }
118123 }
119124 }
120125
@@ -131,8 +136,12 @@ class SpringUtExecutionInstrumentation(
131136 .also { logger.info { " Detected relevant beans for class ${clazz.name} : $it " } }
132137 }
133138
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)
136145 val repositoryDescriptions = springApi.resolveRepositories(relevantBeanNames.toSet(), userSourcesClassLoader)
137146 return repositoryDescriptions.map { repositoryDescription ->
138147 SpringRepositoryId (
@@ -168,7 +177,7 @@ class SpringUtExecutionInstrumentation(
168177 override fun InstrumentedProcessModel.setupAdditionalRdResponses (kryoHelper : KryoHelper , watchdog : IdleWatchdog ) {
169178 watchdog.measureTimeForActiveCall(getRelevantSpringRepositories, " Getting Spring repositories" ) { params ->
170179 val classId: ClassId = kryoHelper.readObject(params.classId)
171- val repositoryDescriptions = getRepositoryDescriptions(classId)
180+ val repositoryDescriptions = getRepositoryDescriptions(classId.jClass )
172181 GetSpringRepositoriesResult (kryoHelper.writeObject(repositoryDescriptions))
173182 }
174183 watchdog.measureTimeForActiveCall(tryLoadingSpringContext, " Trying to load Spring application context" ) { params ->
0 commit comments