Implicit dependency between BeanNameAutoProxyCreator and imported configuration - spring

At my company, we're working on an aspect-oriented trace interceptor, similar to DebugInterceptor. We're configuring a CustomizableTraceInterceptor and using a BeanNameAutoProxyCreator to auto-proxy beans for AOP.
The problem we're facing is that, when we introduce the BeanNameAutoProxyCreator in the configuration:
#Configuration
#Import(BConfig.class)
#EnableAspectJAutoProxy
public class AConfig {
#Bean
public static BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setInterceptorNames(new String[] {DEBUG_INTERCEPTOR_NAME});
beanNameAutoProxyCreator.setBeanNames(new String[] {BEANS_NAMES_EXPRESSION});
return beanNameAutoProxyCreator;
}
}
We get a org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [X], where X is a Resteasy Proxy. This Resteasy Proxy is declared in BConfig.
Now, if I move the Resteasy Proxy bean configuration up to AConfig, this issue is solved, and #DependsOn solves the issue too.
My questions are 3: when is Spring able to resolve dependencies between beans? Why using a BeanNameAutoProxyCreator changes this behavior? What is the recommended way of solving this issue (BeanPostProcessor, #DependsOn, etc.).

The static BeanNameAutoProxyCreator depends on a normal bean (probably due to the BEANS_NAMES_EXPRESSION). Because it is static it is loaded/bootstrapped before any other beans and especially before the bean processing #Import. So basically when analyzing which beans to process, BConfig hasn't yet been loaded. That is why it works when you move the bean to AConfig or at a depends-on for this bean.
I would probably revert the use of a BeanNameAutoProxyCreator and rely on the #EnableAspectJAutoProxy together with an aspect using the bean pointcut to attach the desired interceptor.
There is also another risk in introducing the BeanNameAutoProxyCreator next to #EnableAspectJAutoProxy it can lead to a proxy of a proxy being created, due to 2 different AOP strategies/mechanisms.

Related

Disabling a DatabaseInitializerDetector in Spring Boot

I am defining my own Liquibase auto configuration to manage multitenancy, which involves a prototype SpringLiquibase bean:
#Bean
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
SpringLiquibase tenantCreatedLiquibase(String tenantId) {
// construct a SpringLiquibase instance
}
The tenantCreatedLiquibase prototype bean is instantiated at runtime via an ObjectProvider<SpringLiquibase>.
But LiquibaseDatabaseInitializerDetector creates a dependency from jdbcTemplate on my tenantCreatedLiquibase bean. The ApplicationContext fails to load because the tenantCreatedLiquibase prototype bean requires a tenantId argument.
How can I disable the LiquibaseDatabaseInitializerDetector? Or otherwise work around this problem?
One workaround is to exclude any auto configuration that references DatabaseInitializationDependencyConfigurer. In my case, that meant excluding JdbcTemplateAutoConfiguration and SqlInitializationAutoConfiguration (in addition to LiquibaseAutoConfiguration, which is excluded because I provide my own custom replacement).
I'd still prefer direct control over the registration of DatabaseInitializerDetectors, but I'm not sure that's possible.

Spring Boot external DataSource bean

I am trying to create a common library that includes several stuff needed by my microservices. One of those things is the ACL functionality provided with spring-security. My initial thought was to initialize all ACL-related beans from a #Configuration file in the common library and each time a microservice needs this functionality i could use the #Import annotation(to my microservice project) to "enable" it.
Some of these beans require the famous javax.sql.DataSource to work, so in my common library i autowired it as follows:
#Configuration
public class AclConfiguration {
#Autowired
DataSource dataSource
When i decide that i want this configuration to take place i go to my microservice project (let's say RulesApplication) and on the main class (annotated with #SpringBootApplication) i do the following
#SpringBootApplication
#EnableJpaRepositories
#EnableJpaAuditing
#EnableCaching
#Import(AclConfiguration.class)
public class RulesApplication {
.
.
.
The problem is that the DataSource bean cannot be seen from the common library, although it is being created as expected (validated just by removing the #Import).
Everytime i import the configuration from the common library i get a :
Caused by: java.lang.IllegalArgumentException: DataSource required
at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
Indicating that the DataSource bean is null.
What am i missing here?
Coming up from some digging.. There was never a real problem with the DataSource bean. All the frustration was created by double-defining another Bean in the same class, which led to all the #Autowired beans failure to initialize.
As a result from this research(as other posts mentioned) the bean initialization is working smoothly between shared projects, so most of the times this error will occur from double-defining/badly-defining other beans.

Disabling Transaction Management in Spring JMS listener

I have a spring boot application as a Spring JMS listener. i have configured multiple datasource manager one for Oracle and another one for DB2 .
whenever i am starting app ,jms listener container is looking for a transaction manager bean and giving below error as it find two bean.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jms.JmsAnnotationDrivenConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.transaction.PlatformTransactionManager org.springframework.boot.autoconfigure.jms.JmsAnnotationDrivenConfiguration.transactionManager; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: db2TransactionManager,oracleTransactionManager
i dont want to maintain JMS transaction. how could i achieve it or how can we disable jms transaction feature?
below are the annotation i have added on my main spring boot class. also i am using Spring Data repository
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class})
#ComponentScan(basePackages = "com.deere.oracledataupdate.*")
//#EnableJpaRepositories(basePackages ="com.deere.oracledataupdate.dao.springdata")
#EntityScan(basePackages = "com.deere.oracledataupdate.*")
#PropertySource({ "classpath:application-${IafConfigSuffix}.properties" })
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Looking to the current Spring Boot code we have (JmsAnnotationDrivenConfiguration):
#Autowired(required = false)
private JtaTransactionManager transactionManager;
So, right now it requires only the bean which is exactly JtaTransactionManager by type. I guess both yours are DataSourceTransactionManager.
I'm sure that was correct fix to worry only about the XA tx-manager for auto-config.
Seems for me you can fix your issue with something like #Primary on one of your tx-manager beans.
But... Do you need a JMS Annotation support in your application at all?
Maybe it would be just enough to exclude JmsAnnotationDrivenConfiguration as well?
If need it anyway, I see only one way to fix it: disable JmsAnnotationDrivenConfiguration and configure #EnableJms manually, bypassing the tx-manager issue and just don't configure it for the DefaultJmsListenerContainerFactory as you request.
See JmsAnnotationDrivenConfiguration source code for more information.

Spring Java config, #Autowire vs. Constructor Injection, #Transactional and CGLIB

We've been using #Autowired plus Java-based Spring configuration with some success but now, we're losing control. Everyone is starting to add autowired dependencies everywhere, creating cycles and strange bugs.
So we are considering using constructor injection and autowiring of Spring configurations.
Old:
class Bean {
#Autowired Foo foo;
}
#Configuration
#Import( FooCfg.class )
class BeanCfg {
#Bean public Bean bean() { return new Bean(); }
}
New:
class Bean {
public Bean(Foo foo) {...}
}
#Configuration
class BeanCfg {
#Autowired FooCfg fooCfg;
#Bean public Bean bean() { return new Bean(fooCfg.foo()); }
}
This works pretty well (and it drives people to split beans instead of creating monsters with 10+ constructor arguments).
But it fails when Bean has a method annotated with #Transactional since CGLIB then tries to create a proxy which fails since it can't find a no-argument constructor.
What's the solution for this?
You have a couple of possible of solutions
Introduce interfaces your classes
Upgrade the Spring Version to at least 4.0
Add protected no-arg constructor
Introduce Interfaces
When introducing interfaces for your classes you can drop the usage of CgLib. Spring will then be able to use JDK Dynamic Proxies which work around interfaces. It creates a proxy around an already existing bean instance and that proxy implements all the interfaces of the class it is wrapping. That way it doesn't matter if your class has a no-arg constructor or not.
Upgrade to Spring 4
In Spring 4.0 support was added to allow proxying of classes with a missing no-arg constructor (See SPR-10594). To enable this upgrade your Spring version and add Objenesis to your classpath, Spring 4 comes with its own repacked cglib version so that shouldn't be needed anymore.
One thing to note is that you should have a constructor with no logic if you do null checks or init logic in the constructor it might fail in the case where cglib creates an instance. I would suspect that it pass null to all the constructor arguments (or some default for primitives).
Added protected no-arg constructor
Cglib needs to be able to create an instance which is used to wrap the actual class. It should be enough to have a protected constructor in your classes so that cglib can call it.

Injected bean reset to NULL in the Aspect

I am new Spring AOP and Aspectj. I have seen various posts related to injected bean in an aspect being null and I have run into a similar problem. I am still not clear how I should proceed to get past the problem I am currently encountering.
Issue: Currently we are using Spring 3.2.3 and all injection is through Annotation. In my case, the dependent bean is injected properly by Spring but at the point of execution the injected bean is NULL. BTW, this doesn't happen all the time but what I can say is the stack trace when it fails and when it succeeds is slightly different. When the injected bean is not null (I can successfully use the injected bean service), the call to the before advice (in the aspect) always happens before the target method is called as it should.When the injected bean is NULL, the call to the aspect is from the first statement of the target method. At this point, I think another aspect is instantiated and has no reference to the injected bean. Here is the aspect I have created:
#Component
#Aspect
public class Enable{
private NameService nameService;
#Autowired
public void SetNameService(NameSerice service){
// service is injected properly
this.nameSerice = service;
}
#Before("* *.*(..)")
public void callBefore(JoinPoint jp){
//sometimes nameService is null and sometimes it not not
this.nameService.lookup(...);
}
}
Examining the various posts, one way to get around this (as suggested in the post) is to configure the aspect in the XML configuration file and use the factory-method ="aspectOf" and in the configuration inject the reference to the NameService bean as a property. Our whole project uses Annotation based injection (as stated earlier). Assuming I can still configure the above aspect in an XML configuration file, how can I get the reference NameService bean Id so that I can add it to the configuration. I also saw a post related to using Configurable annotation but I assume that is for objects created outside the Spring IOC.
Currently, the aspects are woven using Aspectj compile option in pom.xml. Our root-context.xml contains the entry context:annotation-config and the aspect is injected into Spring IOC because component-scan is turned on for the folder where the aspect resides. Any help will be appreciated
This is well common error when use aspects in spring, you should add
<context:spring-configured/>
and
<aop:aspectj-autoproxy />
also add
#Configurable
#Aspect
public class Enable
To your appContext.xml
aspectOf is another style to do the above but I prefer use the nature of context.
It might be too late to answer this question. But i have come across the same situation and i fixed it as below.
1) Have a setter and getter for "NameService" in your aspect class.
2) Mark "NameService" with #Component ("nameService")
3) Configure "nameService" in xml configuration using setter injection.
4) Re-Start your server after making changes.
This should resolve the problem of getting null for "NameService" in aspect.

Resources