You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the TestContext framework, the DependencyInjectionTestExecutionListener (DITEL) is primarily responsible for performing dependency injection into the current test instance, but DITEL also initializes the test instance using AutowireCapableBeanFactory.initializeBean().
Javadoc for initializeBean():
Initialize the given raw bean, applying factory callbacks such as setBeanName and setBeanFactory, also applying all bean post processors (including ones which might wrap the given raw bean).
Note that no bean definition of the given name has to exist in the bean factory. The passed-in bean name will simply be used for callbacks but not checked against the registered bean definitions.
The fact that all registered bean post processors are applied to the test instance as if it were a bean in the ApplicationContext leads to unexpected side effects. For example, a CGLIB enhanced version of the test class may be created in order to proxy a transactional test class that does not implement any interfaces. The generated proxy is never actually used by the TestContext framework, and as such its creation is unnecessary and in fact unintentional.
Case Study Involving Transactional Tests and CGLIB
... Spring proxies the test class since it discovers the @Transactional annotation. This is inappropriate since @Transactional is already handled by TransactionalTestExecutionListener via reflection. For every test method execution DependencyInjectionTestExecutionListenerindirectly uses CGLIB to enhance the test class. This results in a potentially large number of instrumented CGLIB classes -- the total = number of transactional tests methods per test * number of tests -- each holding a reference to the data inside the test class which are not released but bound to the current class loader.
In our current project we have several classes (around 50-100) marked transactional which in our case results in a huge memory leak (1GB and more). We worked around this issue by letting the test case implement org.springframework.aop.Advisor, which is very dirty but blocks Spring from instrumenting the test class and works fine.
Side Note regarding Dynamic Proxies
Note, however, that if the test class implements any interface then a JDK dynamic proxy will be used instead of CGLIB enhancement. This may occur, for example, if the test class implements ApplicationContextAware like AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringContextTests.
Deliverables
Investigate an alternative for bean initialization of test instances that results in neither CGLIB enhancement of the test instances nor creation of dynamic proxies for test instances.
This issue is related to #9309 and #10719 since all of these issues are dependent on how the TestContext framework achieves dependency injection for and initialization of test instances.
We recently ran into this issue with a fairly large integration test suite. The application that was being tested was using @SpringBootTest due to it being a Spring Boot application. However the use of @MockBean leads to creation of new ApplicationContext s, which will get cached. To reduce the memory footprint we reduced the number of cached contexts, however memory was still leaking due to the proxies created held on to parts of the context. Leading to leaking 30MB per test proxy (times 1000 testcases and it starts to add up quite quickly).
I'm quite aware of the trade-off here between ease of use and implementing something custom for tests. But it would be nice that there would be some workaround in the testcontext framework that those proxies would get discarded (and gc'ed ) as quickly as possible.
We now implemented the workaround (implementing Advisor) which works, but still it is a workaround (and it probably come back and bite us again).
Peter Ertl opened SPR-9478 and commented
Background
In the TestContext framework, the
DependencyInjectionTestExecutionListener
(DITEL) is primarily responsible for performing dependency injection into the current test instance, but DITEL also initializes the test instance usingAutowireCapableBeanFactory.initializeBean()
.Javadoc for
initializeBean()
:The fact that all registered bean post processors are applied to the test instance as if it were a bean in the ApplicationContext leads to unexpected side effects. For example, a CGLIB enhanced version of the test class may be created in order to proxy a transactional test class that does not implement any interfaces. The generated proxy is never actually used by the TestContext framework, and as such its creation is unnecessary and in fact unintentional.
Case Study Involving Transactional Tests and CGLIB
With a test class like the following:
... Spring proxies the test class since it discovers the
@Transactional
annotation. This is inappropriate since@Transactional
is already handled byTransactionalTestExecutionListener
via reflection. For every test method executionDependencyInjectionTestExecutionListener
indirectly uses CGLIB to enhance the test class. This results in a potentially large number of instrumented CGLIB classes -- the total = number of transactional tests methods per test * number of tests -- each holding a reference to the data inside the test class which are not released but bound to the current class loader.In our current project we have several classes (around 50-100) marked transactional which in our case results in a huge memory leak (1GB and more). We worked around this issue by letting the test case implement
org.springframework.aop.Advisor
, which is very dirty but blocks Spring from instrumenting the test class and works fine.Side Note regarding Dynamic Proxies
Note, however, that if the test class implements any interface then a JDK dynamic proxy will be used instead of CGLIB enhancement. This may occur, for example, if the test class implements
ApplicationContextAware
likeAbstractTransactionalJUnit4SpringContextTests
andAbstractTransactionalTestNGSpringContextTests
.Deliverables
Affects: 3.0 GA
Issue Links:
3 votes, 4 watchers
The text was updated successfully, but these errors were encountered: