I'm migrating services from spring boot 1.5 to spring boot 2.1 and I'm getting an error during this process. I have the following class for configuring my spring beans:
#Configuration
public class CompanyTransactionConfiguration {
public CompanyTransactionConfiguration() {
}
#Bean
public TransactionTaskRunner transactionTaskRunner(PlatformTransactionManager transactionManager) {
return new TransactionTaskRunnerImpl(this.readWriteTransactionTemplate(transactionManager), this.readOnlyTransactionTemplate(transactionManager), this.newReadWriteTransactionTemplate(transactionManager));
}
}
And, of course, a test class to check that everything work as expected:
#RunWith(SpringRunner.class)
public class ReferrerActivityRepositoryIT extends AbstractDomainIT {
#Autowired
private ReferrerActivityRepository referrerActivityRepository;
#Autowired
private TransactionTaskRunner transactionTaskRunner;
...
}
The issue is that this test was working fine after I changed my dependencies to a newer spring boot version (2.1), but now I'm getting the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method transactionTaskRunner in com.company.core.server.config.CompanyTransactionConfiguration required a bean of type 'org.springframework.transaction.PlatformTransactionManager' that could not be found.
The following candidates were found but could not be injected:
- Bean method 'transactionManager' in 'DataSourceTransactionManagerAutoConfiguration.DataSourceTransactionManagerConfiguration' not loaded because #ConditionalOnSingleCandidate (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans
- Bean method 'kafkaTransactionManager' in 'KafkaAutoConfiguration' not loaded because #ConditionalOnProperty (spring.kafka.producer.transaction-id-prefix) did not find property 'spring.kafka.producer.transaction-id-prefix'
...
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.transaction.PlatformTransactionManager' in your configuration.
I don't know what is going on, maybe I need to add another dependency because of changes in spring boot or change my application.properties file. The question is why is this happening? What should I change to get this working?
Thanks!
You didn't define PlatformTransactionManager bean. I assume you don't want to make it by yourself. You have to add spring.kafka.producer.transaction-id-prefix property to property file in order to use KafkaAutoConfiguration for PlatformTransactionManager.
Bean method 'kafkaTransactionManager' in 'KafkaAutoConfiguration' not loaded because #ConditionalOnProperty (spring.kafka.producer.transaction-id-prefix) did not find property spring.kafka.producer.transaction-id-prefix
By the way your's CompanyTransactionConfiguration constructor is redundant as long as it doesn't have parameters. If there's no constructor in class compiler will create default one without parameters.
Related
I am using
#Service
public class Service{
#Autowired
private CacheManager cacheManager;
}
I included org.ehcache:ehcache and spring-boot-starter-cache library in the build.gradle file.
on running the application I am getting the error:
Field cacheManager required a bean of type org.springframework.cache.CacheManager that could not be found.
I am not sure how to go about resolving this error
My Thoughts:
Looks like I need to declare a class annotated with #Configuration and with methods #Bean which returns an object of type CacheManager. I am using EhCache here. Not sure exactly how to do this.
I encountered this error while starting my SpringBootApplication.
Description:
Field XXX in com.helloworld.www.batch.SomeServiceImpl required a bean of type 'com.helloworld.www.batch.repository.SomeRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.helloworld.www.batch.repository.SomeRepository' in your configuration.
This is my project layout where I have my SpringBootApplication and the Repository. I have annotated my SpringBootApplication with "#SpringBootApplication", but it was not able to find "SomeRepository" bean class.
Do I have to annotate it with "EnableJpaRepositories" and indicate the repository packages before it can detect the "SomeRepository" bean class?
com.helloworld.www
+SpringBootApplicationName.java
com.helloworld.www.batch.repository
+SomeRepositoryName.java
Updates to the post, I have annotated my Repository with #Repository, and it only contains an interface without any implementation classes.
#Repository
public interface SomeRepository extends PagingAndSortingRepository<SomeModel, Integer> { }
Here's my main application class under package "com.helloworld.www"
#SpringBootApplication
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class);
}
}
I think its not a "simple" injection problem here.
It looks like you're using spring data.
And the Repository that must be injected is actually an interface rather than concrete bean implementation.
Spring Data engine should be able to "find" your repository and create a real implementation in runtime (backed by Proxy mechanism). Then it puts this generated proxy onto an application context and only after that it can be injected into other beans.
In this case it looks like this "engine" that turns the repository into bean didn't work for some reason, and hence at the moment of instantiation of the service (SomeServiceImpl) There is no actual bean that represent (has been generated out of) repository.
Its hard to tell more, because you don't show any spring data related configurations and do not provide the code of that Repository...
I have implemented ImportBeanDefinitionRegistrar to create bean definitions from external source. It has to be registrar since I do not know in advance how many bean definitions will be created.
When I use the registrar on my Application class like this:
#SpringBootApplication
#Import(GenesysRegistrar.class)
public class IntegrationServer {
...
}
everything works fine.
I wanted to make the import automatic for every application that uses the JAR that contains the registrar so I have created following class:
#Configuration
#Import(GenesysRegistrar.class)
public class GenesysAutoConfiguration {
/**/
}
and registered it in META-INF/spring.factories under key org.springframework.boot.autoconfigure.EnableAutoConfiguration.
Now auto-configuration works, but I get following messages in log:
Bean 'xxx' of type [XXX] is not eligible for getting processed by all BeanPostProcessors
What is the difference between importing the registry from aplication class and from autoconfiguration class? I have found that following post processors are created AFTER my beans:
methodValidationPostProcessor
persistenceExceptionTranslationPostProcessor
webServerFactoryCustomizerBeanPostProcessor
errorPageRegistrarBeanPostProcessor
I have tried to put #AutoConfigureOrder(Integer.MAX_VALUE) on my auto-configuration class, but it does not change anything.
Any ideas how can I fix the order so beans from definitions created by my registrar are processed after all post processors? Why Spring creates them before all post processors? Any why the issue does not happen when using #Import on application class?
Early initialized beans are dependencies injected to other bean factoy by constructor injection.
If the bean factory uses property injection instead of construcotr injection it does not happen.
Question is if constructor injection on factory bean is "bad practise" - have not found anything that prohibits it.
Created ticket to spring-boot (as it happens only during boot auto-configuraiton):
Issue #20219
We´re working on multi-module project where each module is a cutom spring boot starter that holds several retryable tasks. (Using spring-retry)
In order to ensure that retry annotations are activated only once cross starters, a configuration bean is added to each starter auto configuration submodule:
#EnableRetry
#Configuration
#ConditionalOnMissingBean(RetryConfiguration.class)
public class StarterRetryAutoConfiguation {}
The solution is working as expected.
Question: What's the difference between #ConditionalOnSingleCandidate and #ConditionalOnMissingBean ?
I've read the Spring documentation more then once. However, I didn't get when and where should we use each one of them.
ConditionalOnSingleCandidate:
#Conditional that only matches when a bean of the specified class is
already contained in the BeanFactory and a single candidate can be
determined. The condition will also match if multiple matching bean
instances are already contained in the BeanFactory but a primary
candidate has been defined; essentially, the condition match if
auto-wiring a bean with the defined type will succeed.
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.
ConditionalOnMissingBean:
#Conditional that only matches when no beans meeting the specified
requirements are already contained in the BeanFactory. None of the
requirements must be met for the condition to match and the
requirements do not have to be met by the same bean.
We can use #ConditionalOnMissingBean if we want to load a bean only if a certain other bean is not in the application context:
#Configuration
class OnMissingBeanModule {
#Bean
#ConditionalOnMissingBean
DataSource dataSource() {
return new InMemoryDataSource();
}
}
In this example, we’re only injecting an in-memory datasource into the application context if there is not already a datasource available. This is very similar to what Spring Boot does internally to provide an in-memory database in a test context.
We can use #ConditionalOnSingleCandidate if we want to load a bean only if a single candidate for the given bean class has been determined.
#Configuration
#ConditionalOnSingleCandidate(DataSource.class)
class OnSingleCandidateModule {
...
}
In this example, the condition matches if there is exactly one primary DataSource bean specified in your application context.
I have multiple Spring Boot Starters, each of which define a DataSource like this:
#Bean
#ConfigurationProperties(prefix = "some.unique.namespace.datasource")
public DataSource someUniqueNamespaceDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public SomeOtherBean someOtherBean() {
return new SomeOtherBean(someUniqueNamespaceDataSource())
}
As you can see, the bean method someUniqueNamespaceDataSource() is being called directly in another bean method, within the same configuration class. However, Spring Boot is intercepting the method, and then performing its own internal injection. This time, it injects with a type of DataSource.
When an application uses one of these starters, it works without issue. However, when it uses multiple starters, I get errors like this:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: someUniqueNamespaceDataSource,someOtherUniqueNamespaceDataSource
I believe this is because Spring Boot is internally injected by type, even though my code injects a qualified bean.
Is there some way that the starter libraries can indicate that the DataSources should not be considered candidates for auto-configuration?
Is there some way that an application depending on more than one of these starter libraries can exclude them from auto-configuration?
Disabling auto-configuration entirely is not really viable. Additionally, manually excluding all current auto-configurations that trigger on existence of a DataSource bean is far too brittle because the addition of dependencies later, especially transitive dependencies, which trigger based on a DataSource bean, will reintroduce the error.
In your #SpringBootApplication or #EnableAutoConfiguration annotations set exclude property to:
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
That should do the trick.