Spring Websocket TaskExecutor-beans displaces "applicationTaskExecutor"-bean - spring-boot

I have a Spring Boot 2.1.8 Application that uses #Async-Tasks. All #Async-Tasks used to be executed by an automatically configured ThreadPoolTaskExecutor-bean named applicationTaskExecutor.
What did I change?
With spring-boot-starter-websocket in the class path and a #EnableWebSocketMessageBroker configuration the applicationTaskExecutor-bean is gone and replaced by four beans with the names
clientInboundChannelExecutor,
clientOutboundChannelExecutor,
brokerChannelExecutor,
and messageBrokerTaskScheduler.
Spring logs to the console: AnnotationAsyncExecutionInterceptor : More than one TaskExecutor bean found within the context, and none is named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly as an alias) in order to use it for async processing: [clientInboundChannelExecutor, clientOutboundChannelExecutor, brokerChannelExecutor, messageBrokerTaskScheduler]
#Async-tasks are now executed by SimpleAsyncTaskExecutor.
Question
Why can't all beans co-exist? Why won't Spring create a applicationTaskExecutor-bean when spring-websockets is configured?

As #M. Deinum mentioned in the comments a #ConditionalOnMissingBean in TaskExecutionAutoConfiguration.java [0] results in the described behavior
I solved it by creating the bean by myself.
#ConditionalOnClass(ThreadPoolTaskExecutor.class)
#Configuration
public class ApplicationTaskExecutorBeanConfig {
#Lazy
#Bean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME, DEFAULT_TASK_EXECUTOR_BEAN_NAME})
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
}
First I tried to prefer the bean from TaskExecutionAutoConfiguration.java by annotating my bean factory method with
#ConditionalOnMissingBean(name = {APPLICATION_TASK_EXECUTOR_BEAN_NAME, DEFAULT_TASK_EXECUTOR_BEAN_NAME})
but it didn't worked because my bean factory method gets invoked earlier than the one in TaskExecutionAutoConfiguration.java.
[0] https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java#L78
Thanks to #M. Deinum for your comment.

Related

#ConditionalOnBean not work for spring boot test

Hy everyone! I'm trying to solve the problem for a very long time.
There is very simple spring boot test
public class ApplicationTest {
#Test
void testContext() {
SpringApplication.run(Application.class);
}
}
And several beans...
#Service
#ConditionalOnBean(CommonService.class)
#RequiredArgsConstructor
public class SimpleHelper {
...
#Service
#RequiredArgsConstructor
#ConditionalOnBean(CommonFeignClient.class)
public class CommonService {
...
#FeignClient(
name = "CommonClient",
url = "localhost:8080"
)
public interface CommonFeignClient {
And the main class look as
#SpringBootApplication
#EnableFeignClients(clients = AnotherFeignClient.class)
public class Application {
When the spring application starts everything works ok. SimpleHelper does not created.
But in the spring boot test throw the exception:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'simpleHelper' defined in URL [...]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'foo.bar.CommonService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Please help, I don't understand what's going on anymore =)
Spring boot version is 2.3.0.RELEASE.
To use #ConditionalOnBean correctly, CommonService needs to be an auto-configuration class or defined as a bean by an auto-configured class rather than a service that's found by component scanning. This ensures that the bean on which CommonService is conditional has been defined before the condition is evaluated. The need for this is described in the annotation's javadoc:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.

Transactions, Spring Boot Starter JDBC & R2DBC

I am trying to migrate a Spring Boot project, version 2.3.0.M3, that have used JDBC template to R2DBC. The project also uses Liquibase so I cannot get rid of JDBC altogether.
I have both the spring-boot-starter-data-r2dbc and the spring-boot-starter-jdbc dependencies in the project with which I get the following exception when trying to run one of my tests:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: expected single matching bean but found 2: transactionManager,connectionFactoryTransactionManager
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1180)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:416)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:480)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:335)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
...
The bean connectionFactoryTransaction manager is defined like this in the Spring class R2dbcTransactionManagerAutoConfiguration:
#Bean
#ConditionalOnMissingBean(ReactiveTransactionManager.class)
public R2dbcTransactionManager connectionFactoryTransactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
The bean transactionManager is defined like this in the Spring class DataSourceTransactionManagerAutoConfiguration:
#Bean
#ConditionalOnMissingBean(PlatformTransactionManager.class)
DataSourceTransactionManager transactionManager(DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
As can be seen, the #ConditionalOnMissingBean annotation contains different types which will cause an instance of both beans to be created.
However, in the Spring class TransactionAspectSupport there is this line of code in the determineTransactionManager method:
defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
Since both of the transaction manager types, DataSourceTransactionManager and R2dbcTransactionManager, implement the TransactionManager interface, both the transaction manager beans as above will be matched and the error will occur.
I am now reaching out to hear if there is anyone who has managed to solve or work around this issue?
Thanks in advance!
With inspiration from M. Deinums answer (thanks!), I applied the following steps to my project and the test that failed earlier now runs successfully:
Remove the spring-boot-starter-jdbc dependency.
Add a dependency to spring-jdbc.
Add a dependency to HikariCP (com.zaxxer).
Add spring.liquibase user and password properties (I already had the url and change-log properties).
Remove all spring.datasource properties (I had url and drive-class-name).
I had the spring.r2dbc properties username, password and url defined which I did not need to change.
Update:
In addition, I used Testcontainers in the tests and could not assign a static port. In order to be able to configure the database port on Liquibase, I overrode a bean name liquibase of the type SpringLiquibase and created a DataSource (not exposed as a bean) in the liquibase bean creation method and set it on the liquibase bean.
It's possible to have spring-boot-starter-jdbc and spring-boot-starter-data-r2dbc co-exist. There is a class org.springframework.transaction.annotation.TransactionManagementConfigurer that can be used to resolve the conflict.
Spring Boot 2.3.0 seems to disable automatic datasource config when r2dbc is present. It's possible to manually import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration class to make both co-exist.
#Bean
TransactionManagementConfigurer transactionManagementConfigurer(ReactiveTransactionManager reactiveTransactionManager) {
return new TransactionManagementConfigurer() {
#Override
public TransactionManager annotationDrivenTransactionManager() {
return reactiveTransactionManager;
}
};
}

Determine Springs bean instantiation order in Grails 3.3 with GORM 6.1.x

I am using a Grails 3.3 application that uses GORM 6.1.6.RELEASE, Spring-Boot 1.5.4.RELEASE and Spring Core 4.3.9.RELEASE behind the scene. I am trying to declare a Spring bean that get initialized just before Hibernate starts to validate the underlying database schema.
Here is what I like to do. I want to register my Flyway as a Spring bean and inject the dataSource bean into it. In order to have Flyway run before Hibernate starts to validate the current database schema, I add my flyway bean as a dependency onto the sessionFactory bean. The order would be as follows:
dataSource bean
flyway bean
hiberateDatastore bean
GORM 6.1 uses org.grails.orm.hibernate.HibernateDatastore as a Spring bean to initialize the Hibernate ORM and the database. The sessionFactory bean declares the hibernateDatastore#getSessionFactory as factory class.
Therefore the hibernateDatastore always is created first.
What is the way in Grails 3.3 to create a custom Spring bean that has to run after the connection to the database is available but before the Hibernate stuff gets initialized?
In previous versions of Grails 3.x it was possible to declare it in resources.groovy like this.
beans = {
if (Environment.current == Environment.PRODUCTION) {
flyway(Flyway) { bean ->
bean.initMethod = 'migrate'
dataSource = ref('dataSource')
locations = 'classpath:db/h2'
baselineOnMigrate = true
}
BeanDefinition sessionFactoryBeanDef = getBeanDefinition('hibernateDatastore')
if (sessionFactoryBeanDef) {
def dependsOnList = ['flyway'] as Set
if (sessionFactoryBeanDef.dependsOn?.length > 0) {
dependsOnList.addAll(sessionFactoryBeanDef.dependsOn)
}
sessionFactoryBeanDef.dependsOn = dependsOnList as String[]
}
}
}
I don't think Spring provides a visualisation of a 'bean instantion tree' however you could set the log level for org.springframework.beans.factory.support.DefaultListableBeanFactory to DEBUG and you'll get output like this:
Creating shared instance of singleton bean '...fully qualified class name...'
Returning cached instance of singleton bean '...fully qualified class name...'
You could review this log output for beans from the Hibernate namespace.
I presume you'll use the results to declare a DependsOn relationship so just for completeness this would look like:
#Bean
public SomeHibernateClass createHibernate() {
...
}
#Bean
#DependsOn("createHibernate")
public MyClass createMine() {
...
}
Grails 3.3.0 changed the mechanism of the dataSource bean creation. The Grails project lead stated in the related issue:
Previous versions of Grails created the dataSource bean separately from the session factory. We would need to restore this behaviour I guess so the dataSource can be referenced without triggering the creation of the sessionFactory
After upgrading to Grails 3.3.1 the dataSource bean is again available before the session factory gets created. This solves the problem.

Spring Boot voodoo required instantiating JPA with DataNucleus and Hikari

Any help getting this config to work would be welcome.
I am trying to take over the automatic connection pool, datasource and JPA configuration from Spring Boot to allow me to bring DataNucleus into the mix instead of Hibernate.
My approach is to code up the pieces Boot says are missing on a trial and error basis. I had to remove the Hibernate dependencies to allow DataNucleus to run.
Maybe I've now coded up too much or maybe not I'm not far enough.
Spring falls over with the error:
Exception encountered during context initialization - cancelling refresh attempt:
[huge SNIP]
nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.data.repository.support.Repositories]:
Factory method 'repositories' threw exception;
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'symbolRepositoryImpl':
Unsatisfied dependency expressed through field 'entityManager';
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type 'javax.persistence.EntityManager' available:
expected single matching bean but found 2:
org.springframework.orm.jpa.SharedEntityManagerCreator#0,
org.springframework.orm.jpa.SharedEntityManagerCreator#1
[SNIP]
2017-06-01 09:43:09.675 ERROR 9108 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter
***************************
APPLICATION FAILED TO START
***************************
Description:
Field entityManager in com.bp.gis.tardis.repository.SymbolRepositoryImpl
required a single bean, but 2 were found:
- org.springframework.orm.jpa.SharedEntityManagerCreator#0: defined by method 'createSharedEntityManager' in null
- org.springframework.orm.jpa.SharedEntityManagerCreator#1: defined by method 'createSharedEntityManager' in null
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans,
or using #Qualifier to identify the bean that should be consumed
I could spend hours debugging this further but the breakpoint comes in the initialisation of one of the repositories which should have an entityManager injected.
This is what I'm manually instantiating:
#Configuration
#EnableJpaRepositories(
basePackages = {"org.adam.repository"}
)
public class DataSourceConfig {
#Bean
#ConfigurationProperties(prefix = "adam.datasource")
public AdamDataSourceProperties getDataSourceProperties() {
return new AdamDataSourceProperties();
}
#Bean
public DataSource getDataSource() {
AdamDataSourceProperties props = getDataSourceProperties();
return new HikariDataSource(props.getHikariConfig());
}
#Bean
public LocalContainerEntityManagerFactoryBean getEmfBean() {
LocalContainerEntityManagerFactoryBean emfBean =
new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(getDataSource());
emfBean.setPersistenceUnitName("adam");
return emfBean;
}
#Bean
public EntityManagerFactory getEmf() {
LocalContainerEntityManagerFactoryBean emfBean = getEmfBean();
return emfBean.getNativeEntityManagerFactory();
}
}
My AdamDatasourceProperties is initialised by Spring using the "adam.datasource" prefixed values in application.properties, and it can then create a HikariConfig object to use to instantiate the HikariDataSource. That bit is actually fine, it's the entity manager factory that is probably causing issues - or something else.
I've got no evidence that my last method getEmf() is actually helping.
Also, I'm dubious that the error
Required a single bean, but 2 were found
or the suggested action are helpful - I don't fancy going into the Spring source code in order to annotate one of those methods on Spring's SharedEntityManagerCreator as #Primary.
UPDATE
DataNucleus won't run if it finds other JPA API classes on the classpath - it insists on its own version of the persistence API - hence removing the Hibernate packages was necessary.
Caused by: org.datanucleus.exceptions.NucleusUserException:
Found Meta-Data for class org.adam.entity.TimeSeriesEntity
but this class is either not enhanced or you have multiple copies
of the persistence API jar in your CLASSPATH!!
Make sure all persistable classes are enhanced before running
DataNucleus and/or the CLASSPATH is correct.
at org.datanucleus.metadata.MetaDataManagerImpl
.initialiseClassMetaData(MetaDataManagerImpl.java:2814)
so I have excluded Hibernate from spring-boot-starter-data-jpa and the error disappears.
I changed the LocalContainerEntityManagerFactoryBean method name to entityManagerFactory:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emfBean =
new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(getDataSource());
emfBean.setPersistenceUnitName("adam");
return emfBean;
}
and to enable testing, I have to copy this #Configuration class and change the EMF method to accept Spring's test database:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
#Qualifier("dataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emfBean =
new LocalContainerEntityManagerFactoryBean();
emfBean.setDataSource(dataSource);
emfBean.setPersistenceUnitName("adam");
return emfBean;
}
That #Qualifier is for the sake of Intellij whose Spring facet complains about 2 candidates for injection here.
I also discovered that with this configuration, the repository/DTO dependency injection for the EntityManager doesn't work with #Autowired. It has to be the native-JPA annotation:
#PersistenceContext
private EntityManager entityManager;
With my previous Hibernate and OpenJPA configurations, Spring was happy to inject its own self-instantiated EntityManager in the presence of #Autowire.
This adds more fuel to my beef with Spring. It just too often doesn't do what it says on the tin. The Spring tests should find the #Configuration classes in the package hierarchy, but doesn't - I need to use #Import. Spring should also find dependency injection candidates based on type (EntityManager, DataSource etc) but it doesn't - in some cases they have to be produced by methods named a particular name or with #Bean annotations declaring a name.
Still, it's done.

Spring default behavior for lazy-init

I am beginner to spring, ESP Inversion of control. I was puzzled understanding the difference between the following
<bean id="demo" class="Demo" lazy-init="false"/>
<bean id="demo" class="Demo" lazy-init="true"/>
<bean id="demo" class="Demo" lazy-init="default"/>
To my understanding : lazy-init=false creates the bean at the startup and lazy-init=true doesn't create a bean at the startup rather creates the bean upon request for a particular bean.
Correct me here, If my interpretation is wrong.
what exactly the default behavior of lazy-init is? How would it instantiate?
The default behaviour is false:
By default, ApplicationContext implementations eagerly create and
configure all singleton beans as part of the initialization process.
Generally, this pre-instantiation is desirable, because errors in the
configuration or surrounding environment are discovered immediately,
as opposed to hours or even days later. When this behavior is not
desirable, you can prevent pre-instantiation of a singleton bean by
marking the bean definition as lazy-initialized. A lazy-initialized
bean tells the IoC container to create a bean instance when it is
first requested, rather than at startup.
I suggest reading up
For those coming here and are using Java config you can set the Bean to lazy-init using annotations like this:
In the configuration class:
#Configuration
// #Lazy - For all Beans to load lazily
public class AppConf {
#Bean
#Lazy
public Demo demo() {
return new Demo();
}
}
For component scanning and auto-wiring:
#Component
#Lazy
public class Demo {
....
....
}
#Component
public class B {
#Autowired
#Lazy // If this is not here, Demo will still get eagerly instantiated to satisfy this request.
private Demo demo;
.......
}
The lazy-init="default" setting on a bean only refers to what is set by the default-lazy-init attribute of the enclosing beans element. The implicit default value of default-lazy-init is false.
If there is no lazy-init attribute specified on a bean, it's always eagerly instantiated.
lazy-init is the attribute of bean. The values of lazy-init can be true and false. If lazy-init is true, then that bean will be initialized when a request is made to bean. This bean will not be initialized when the spring container is initialized
and
if lazy-init is false then the bean will be initialized with the spring container initialization.
When we use lazy-init="default" as an attribute in element, the container picks up the value specified by default-lazy-init="true|false" attribute of element and uses it as lazy-init="true|false".
If default-lazy-init attribute is not present in element than lazy-init="default" in element will behave as if lazy-init-"false".

Resources