Creating A pointcut using args designator with no types results in BeanCurrentlyInCreationException - spring

Using Spring AOP, when attempting to create a Pointcut using the designator args while not providing any types results in a series of exceptions starting with BeanCurrentlyInCreationException
The Example
object _001_Spring_AOP_Pointcut_Args_NoArgs {
open class BeanA {
open fun m() {
println("BeanA#m()")
}
}
#Aspect
class AspectA {
#Pointcut("args()")
private fun pc_noArgs() = Unit
#After("sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs.AspectA.pc_noArgs()")
private fun ac_noArgs() = println("ac_noArgs")
}
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
open class Config {
#Bean
open fun beanA(): BeanA = BeanA()
#Bean
open fun aspectA(): AspectA = AspectA()
}
fun runJava() {
AnnotationConfigApplicationContext(Config::class.java)
}
}
The Run Method
#Test
fun test_run() {
_001_Spring_AOP_Pointcut_Args_NoArgs.runJava()
}
The Exception Summary
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanA' defined in sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$Config: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$BeanA]: Factory method 'beanA' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'aspectA' defined in sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$Config: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs$AspectA]: Factory method 'aspectA' threw exception; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aspectA': Requested bean is currently in creation: Is there an unresolvable circular reference?

args() vs. execution(* *())
This is an unusual way of intercepting all methods without parameters. I would rather make it more explicit and use execution(* *()) instead. If you ever migrate from Spring AOP to AspectJ, you will notice that args() matches all kinds of pointcuts there, e.g. call, initialization, preinitialization, staticinitialization, get and constructor calls/executions without parameters.
Pointcut scoping
Here in Spring AOP your problem might be another one, though: Your pointcut is too broad. It matches many joinpoints in Spring beans, also in Spring or third-party classes themselves. So you want to limit the scope of your pointcut, e.g. something like
execution(* *()) && within(my.own.package.**)
When to (not) use fully qualified class names in pointcuts
BTW, if your pointcut is defined in the very same class as the advice using it, it should not be necessary to use a fully qualified class name, i.e. instead of
#After("sero4.spring.z_added._001_Spring_AOP_Pointcut_Args_NoArgs.AspectA.pc_noArgs()")
you could use
#After("pc_noArgs()")
Inline pointcuts
If you have no plans to re-use the same pointcut in other advices, just get rid of #Pointcut and define an inline pointcut such as
#After("execution(* *()) && within(my.own.package.**)")
Advices should be public
While it is OK to define the #Pointcut method as private if you do not refer to it from other classes, the #After advice should be public. It might work in Spring AOP, but is against conventions. Again, if you ever migrate to AspectJ, you will even get a compile error because an AspectJ advice must be public.

The code shared fails with the following exception -
org.springframework.beans.factory.BeanCurrentlyInCreationException
, when the pointcut expression has a scope within the package the aspect class resides.
Example:
Fully qualified names for the beans :
rg.spring.boot.AspectA
rg.spring.boot.bean.BeanA
And a pointcut expression like the following would fail:
#Pointcut("args() && within(rg.spring.boot..*)")
whereas, the following pointcut expression succeeds :
#Pointcut("args() && within(rg.spring.boot.bean..*)")
The exception, as per me, is because the pointcut targets framework methods of the aspect during initialization. To resolve the exception, you may move the bean to a specific package and fine tune the scope of the pointcut expression for that package.

Related

#WebMvcTest with #Import does not work. Test context always asks for #Repository beans

Using Spring Boot 2.7.3 I can not create a simple integration test for my API using #WebMvcTest.
Here is my setup:
// GameServerApplicationTests.kt
#SpringBootTest
class GameServerApplicationTests {
#Test
fun contextLoads() { }
}
// CraftService.kt
#Service
class CraftService {
fun getAll(): List<String> {
return listOf("foo", "bar")
}
}
// CraftApiTest.kt
#WebMvcTest
#Import(value = [CraftService::class])
class CraftApiTest {
#Autowired
private lateinit var testRestTemplate: TestRestTemplate
#Test
fun `should do accept craft all endpoint`() {
val response = testRestTemplate.getForEntity("/craft/all", String::class.java)
assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
}
}
When I run the test I see this exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'itemRepository' defined in com.gameserver.item.ItemRepository defined in #EnableJpaRepositories declared on GameServerApplication: Cannot create inner bean '(inner bean)#3fba233d' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3fba233d': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
I have no idea why it is looking for the itemRepository bean at all. I never asked for that.
I then added this
#WebMvcTest
#ComponentScan(excludeFilters = [ComponentScan.Filter(Repository::class)]) // <<
#Import(value = [CraftService::class])
Which resulted in this exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'playerRepository' defined in com.gameserver.player.PlayerRepository defined in #EnableJpaRepositories declared on GameServerApplication: Cannot create inner bean '(inner bean)#30c1da48' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#30c1da48': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
Which confuses me even more. I explictly excluded all #Repository beans - but it just skipped ItemRepository and then asked for PlayerRepository now.
I am totally lost and have no idea why I am not able to setup a simple integration test for my API endpoint.
EDIT #1:
Other tests run just fine:
EDIT #2:
I tried to use a #Configuration bean for #Import.
// CraftApiTestConfiguration
#Configuration
class CraftApiTestConfiguration {
#Bean
fun getCraftService(): CraftService {
return CraftService()
}
}
// CraftApiTest.kt
#WebMvcTest
#Import(CraftApiTestConfiguration::class)
class CraftApiTest { // ... }
That did not help either. It just gave me the second exception mentioned above (the one asking for playerRepository)
I'll try to answer although without seeing the actual code it might not be correct.
So #WebMvcTest loads a "slice" of your application with all the beans annotated with #RestControllers. It doesn't load #Service or #Repository annotated beans.
When you run the test with #WebMvcTest annotation it will load all the controllers, and if, by accident the controller references others than the reference on the service (here I can't say for sure what it is), you might end up loading the stuff that you don't actually need.
Now when you use #WebMvcTest there are two things you can/should do:
Work with MockMvc instead of rest template that queries a web server, its not a full-fledged web layer test anyway.
Try using #WebMvcTest with your controller only:
#WebMvcTest(CraftApisController.class)
Also instead of injecting the real implementation of service, you can use #MockBean so that the real service implementation will be covered by a regular unit test (without spring at all, just plain JUnit/Mockito) and this test could check that your annotations are defined correctly

Spring tries to initialize AutoConfiguration beans using default constructor

We are having issues starting up our Spring Boot Web application. The main problem to properly diagnose the startup is that it only seems to happen in 1% of the startups. In 99% of the startup procedures all works fine and we end up having a properly working spring boot application. However in those 1% of those cases we see issues like this:
WARN o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'errorPageFilterRegistration' defined in org.springframework.boot.web.servlet.support.Error
PageFilterConfiguration: Unsatisfied dependency expressed through method 'errorPageFilterRegistration' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorPageFilter' defined in org.springframework.boot.web.servlet.support.ErrorPageFilterConfiguration: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.spring
framework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.autoconfigu
re.web.servlet.error.ErrorMvcAutoConfiguration]: No default constructor found; nested exception is java.lang.NoSuchMethodException: org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration.<init>() []
For some reason it tries to initialize AutoConfiguration beans by using a default constructor which obviously is not present. There is a constructor present which should be autowired.
Also the AutoConfiguration that is in the stacktrace can be different. Sometimes it is another one like e.g. org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
Any help or ideas on why this could be happening is appreciated. As this happens very occasionally this is hard to debug as we cannot relyably reproduce. Note that the stacktrace does not contain any custom code. Our application is quite big and we rely mostly on #Configuration classes to do configure the Beans.
Why would spring attempt to initialize an AutoConfiguration bean with a default constructor ?
The errorPageFilterConfiguration source of spring looks like this:
#Configuration(proxyBeanMethods = false)
class ErrorPageFilterConfiguration {
#Bean
ErrorPageFilter errorPageFilter() {
return new ErrorPageFilter();
}
#Bean
FilterRegistrationBean<ErrorPageFilter> errorPageFilterRegistration(ErrorPageFilter filter) {
FilterRegistrationBean<ErrorPageFilter> registration = new FilterRegistrationBean<>(filter);
registration.setOrder(filter.getOrder());
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
return registration;
}
}
According to the stack on creation of the errorPageFilter it is initializing the ErrorMvcAutoConfiguration as a prerequisite ? Why ?
We are not initializing these beans manually. The only relevant code for error page handling that we have is this following:
#Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> webServerFactoryCustomizer() {
return webServerFactory -> {
ErrorPage errorPage = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error");
webServerFactory.addErrorPages(errorPage);
};
}
This is a bug in Spring framework, introduced in version 5.3 in AbstractBeanFactory.
BeanPostProcessorCacheAwareList and accesses to the beanPostProcessors instance are not Thread safe. If multiple Threads are running during initialization and a Thread calls getBeanPostProcessorCache() while another Thread is calling addBeanPostProcessors, you can create a cache which does not contain all BeanPostProcessor instances and thus doesn't find the appropriate constructor.
I will submit a bug for this to spring-framework.
https://github.com/spring-projects/spring-framework/blob/16ea4692bab551800b9ba994ac08099e8acfd6cd/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java#L964
Issue created : https://github.com/spring-projects/spring-framework/issues/29299

How to autowire mongoTemplate into custom type converter?

I'm trying to create a converter that will fetch object from DB by it's ObjectId. But the mongoTemplate is always empty in converter:
org.springframework.core.convert.ConversionFailedException:
Failed to
convert from type org.bson.types.ObjectId to type
com.atlas.mymodule.datadomain.MyObject for value
'130000000000000000000013';
nested exception is
java.lang.NullPointerException
Code:
#Component
public class ObjectIdToMyObjectConverter implements Converter<ObjectId, MyObject> {
#Autowired
private MongoTemplate mongoTemplate; // null ???
public MyObject convert(ObjectId objectId) {
return mongoTemplate.findById(objectId, MyObject.class); // <- NullPointerException
}
}
Configuration:
#Configuration
#ComponentScan
#EnableMongoRepositories
public abstract class MyModuleConfiguration extends AbstractMongoConfiguration {
#Override
public MongoClient mongo() throws Exception {
List<MongoCredential> mongoCredential = getMongoCredentials();
return mongoCredential == null ?
new MongoClient(getMongoServerAddresses()) :
new MongoClient(getMongoServerAddresses(), mongoCredential, getMongoClientOptions());
}
protected abstract List<MongoCredential> getMongoCredentials();
protected abstract MongoClientOptions getMongoClientOptions();
protected abstract List<ServerAddress> getMongoServerAddresses() throws UnknownHostException;
#Bean
public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
return new ObjectIdToMyObjectConverter());
}
#Override
public CustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
converters.add(objectIdToMyObjectConverter());
return new CustomConversions(converters);
}
}
Test Configuration:
public class MyModuleTestConfiguration extends MyModuleConfiguration {
// overrides abstract methods, defines connection details...
}
update:
I've updated the code according to #mavarazy suggestion (added ObjectIdToMyObjectConverter bean definition) but got an exception:
Error creating bean with name 'mongoTemplate': Requested bean is
currently in creation: Is there an unresolvable circular reference?
Full exception:
Error creating bean with name 'mongoTemplate' defined in com.atlas.MyModule.MyModuleTestConfiguration:
Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.MongoTemplate]: Factory method 'mongoTemplate' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mappingMongoConverter' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.convert.MappingMongoConverter]: Factory method 'mappingMongoConverter' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mongoMappingContext' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.mapping.MongoMappingContext]: Factory method 'mongoMappingContext' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'customConversions' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.mongodb.core.convert.CustomConversions]: Factory method 'customConversions' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'objectIdToMyObjectConverter': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: private org.springframework.data.mongodb.core.MongoTemplate com.atlas.MyModule.ObjectIdToMyObjectConverter.mongoTemplate;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'mongoTemplate': Requested bean is currently in creation: Is there an unresolvable circular reference?
Thanks.
ObjectIdToMyObjectConverter is not a spring bean. If you want #Autowired to work, create ObjectIdToMyObjectConverter as Spring bean, like this:
#Bean
public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
return new ObjectIdToMyObjectConverter());
}
and #Autowire it in your configuration.
Following #Savash update
I have not paid enough attention to your configurations.
What you see is happening because you are trying to create MongoTemplate, which depends on CustomConversions, and at the same time CustomConversions depend on MongoTemplate, spring can't and should not do that.
As a solution:
You can create your CustomConversions with ApplicationContextAware, and extract MongoTemplate reference lazily on a first call.
I thought you are using CustomConversions as part of spring-integration or something. If so it does not need to be part of converters for Mongo. If you need it as MongoConverters, you are doing something really strange.
What is exact use case, you need this for?
Following comments:
Do I understand right, that you want MongoTemplate to read object with user reference as User object, and write object with User value as user reference?
I think.
You have a bad data model (you are trying to emulate JOIN operation in your MongoTemplate, which means you are missing something in your data model, and this is not how you should work with mongo).
Just call User explicitly when you need it, don't overload your DB with additional work, you'll have problems with performance
You can use another object, which you'll enrich with current user, as needed
Maybe SQL & ORM like Hibernate is a better approach for you ?
Try Hibernate OGM for your purpose, it might provide functionality, you need (Not sure, though, have not worked with it)

ApplicationContext with same bean having #RequestMapping fails

In my applicationContext.xml I have 2 beans with same class and different id(test and test1). The application context gets loaded correctly, but when I add #RequestMapping to one method then the bean creation fails with the below error. This used to work with AnnotationMethodHandlerAdapter but its failing with RequestMappingHandlerMapping and RequestMappingHandlerAdapter.
Error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'test1' bean method
public java.lang.String com.test.render()
to {[/render],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'test' bean method
Please suggest how to fix this.
Code:
applicationContext.xml
<bean id="test" class="com.abc.test" />
<bean id="test1" class="com.abc.test" />
Controller
#Controller
#RequestMapping( value ={"/test/", "/test1/"})
public class test {
#RequestMapping("render")
public String render ()
{
//some code here.
}
}
You can do it like this...
switch from singleton approach to prototype and inside the xml do
While programmatically define
class P1 {
#Autowire
NotSoSingleton prototypedBean;
}
class P2 {
#Autowire
NotSoSingleton prototypedBean;
}
class P3 {
#Autowire
NotSoSingleton prototypedBean;
}
Would this approach do?
I could find a work around with this. This solution if for the comments I posted on 21-May
I used order property to instantiate the custom defined RequestMappingHandlerMapping in the xml file and it worked!!
It took my custom defined RequestMappingHandlerMapping instead of the default one loaded by <annotation-driven>.

spring aop - exception while creating advice around JdbcTemplate methods

I've a web application that uses apache dbcp and spring jdbc to perform database operations on an oracle database. I need to write a performance logger that logs the individual times of each database operation. I tried writing an around advice on all 'execute' methods of org.springframework.jdbc.core.JdbcTemplate but it results in an error when spring gets initialized. The logger class and the exception stacktrace is as follows:-
I also tried to use CGLIB proxies by enabling but it errors out on dao classes that extends from spring's StoredProcedure class and use constructor injection.
#Aspect
public class Logger {
#Around("this(org.springframework.jdbc.core.JdbcTemplate) && execution(* execute(*))")
public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
long time = System.currentTimeMillis();
Object result = pjp.proceed();
LOGGER.debug("time consumed = " + (System.currentTimeMillis() - time));
return result;
}
Exception stacktrace:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'myDao' defined in class path resource [spring/my-dao.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.jdbc.core.JdbcTemplate]:
Could not convert constructor argument value of type [$Proxy7] to required type [org.springframework.jdbc.core.JdbcTemplate]:
Failed to convert value of type '$Proxy7 implementing org.springframework.jdbc.core.JdbcOperations,org.springframework.beans.factory.InitializingBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised'
to required type 'org.springframework.jdbc.core.JdbcTemplate';
nested exception is
java.lang.IllegalStateException: Cannot convert value of type [$Proxy7 implementing org.springframework.jdbc.core.JdbcOperations,org.springframework.beans.factory.InitializingBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised]
to required type [org.springframework.jdbc.core.JdbcTemplate]:
no matching editors or conversion strategy found
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:702)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:196)

Resources