Error upgrading Spring Boot 2.2.7 to Spring Boot 2.3.0 - spring

I was using Spring Boot 2.2.7.RELEASE and everthing was working well.
When i upgraded to Spring Boot 2.3.0.RELEASE, a few issues start to show up, i was able to resolve them except for this.
I have this class:
#Configuration
#EnableCaching
public class CacheConfiguration extends CachingConfigurerSupport {
public static final String PROPERTY_RESOLVING_CACHE_RESOLVER_BEAN_NAME = "propertyResolvingCacheResolver";
#Value("${my.cache.name}")
private String myCacheName;
#Autowired
private Environment environment;
#Bean(PROPERTY_RESOLVING_CACHE_RESOLVER_BEAN_NAME)
#Override
public CacheResolver cacheResolver() {
return new PropertyResolvingCacheResolver(cacheManager(), environment);
}
#Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache(myCacheName)));
return cacheManager;
}
}
and the error i am getting is this:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'environment' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:814)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1282)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$ShortcutDependencyDescriptor.resolveShortcut(AutowiredAnnotationBeanPostProcessor.java:796)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1238)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.resolvedCachedArgument(AutowiredAnnotationBeanPostProcessor.java:601)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.access$000(AutowiredAnnotationBeanPostProcessor.java:131)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:631)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
... 66 common frames omitted
I thought it was an issue with the 'life-cycle' of the beans, and i was auto-wiring too early, so i did this:
public class CacheConfiguration extends CachingConfigurerSupport implements EnvironmentAware {
...
private Environment environment;
...
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
...
But then, other issues start showing(after doing the above) like:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:814)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1282)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:297)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
at org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor.postProcessBeforeInitialization(ConfigurationClassPostProcessor.java:456)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:416)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
... 45 common frames omitted
This is getting out of my league, any help would be much appreciated, thanks!
Edit 1:
After experimenting a bit, the issue is because the creation of this bean #Bean(PROPERTY_RESOLVING_CACHE_RESOLVER_BEAN_NAME) is deferred to a later time.
It used to be created after cacheManager, but in 2.3.0.RELEASE, it's not anymore.
Edit 2:
DataSourceHealthIndicator is in the application context under bean name dbHealthContributor for both 2.2.7.RELEASE & 2.3.0.RELEASE.
This can be seen under /actuator/beans :
"dbHealthContributor": {
"aliases": [],
"scope": "singleton",
"type": "org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator",
"resource": "class path resource [org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.class]",
"dependencies": [
"dataSource"
]
},

A bit late to the game, but I ran into the same problem when upgrading Spring Boot from 2.2.0 to 2.3.0. You could also solve the problem by using Spring's #Lazy annotation. That way the DataSourceHealthIndicator would be autowired later when it actually gets used and by that time the autoconfiguration would make it available for autowiring. And you wouldn't have to define your own bean like you did in your answer.
So instead of
#Autowired
private DataSourceHealthIndicator dataSourceHealthIndicator;
, you would use
#Autowired
#Lazy
private DataSourceHealthIndicator dataSourceHealthIndicator;

By having trace=true in application.properties, i managed to pin-point the issue.
I used to have this working(provided for free by Spring Boot):
#Autowired
private DataSourceHealthIndicator dataSourceHealthIndicator;
But it seems that dataSourceHealthIndicator 's creation has been delayed so that when my bean(that needs it) is being created, it couldn't find it and autowiring failed.
I therefore created it manually:
#Bean
#Primary
public DataSourceHealthIndicator dataSourceHealthIndicator(DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource, "SELECT 1 FROM DUAL");
}
and my error is gone.
I hope to be able to understand what happened to
dataSourceHealthIndicator between 2.2.7.RELEASE and
2.3.0.RELEASE. I do not see anything mention about it in the
Spring-Boot-2.3-Release-Notes
or i have missed it, but i can still see it being "created" - see my Edit 2 in my question.

Related

After migrating to Spring Boot 2.1.7 RELEASE-initializeBean not working

After migrating spring4.1.7 to spring boot2.1.7 Release.Its showing an error for bean creation while initializing application context.
Error Log
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'localConfig' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:771)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:307)
... 25 more
ServiceConfiguration Class
#Configuration
#Profile(ContextProfileNames.SERVICE)
#EnableWebMvc
#ComponentScan(basePackages = "com.get.services")
#Import(ControllerConfiguration.class)
public class ServiceConfiguration implements InitializingBean
{
#Autowired
private ApplicationContext context;
#Bean(name = "localConfig")
#DependsOn(BeanNames.CONFIGURATION_FACTORY)
#Scope("singleton")
public LocalDataSourceConfiguration getLocalDataSourceConfiguration() throws XEDecryptionException
{
ConfigurationFactory configurationFactory = (ConfigurationFactory) context
.getBean(BeanNames.CONFIGURATION_FACTORY);
LocalDataSourceConfig localDataSourceConfig = configurationFactory.getLocalDataSourceConfiguration();
LocalDataSourceConfiguration localDataSourceConfiguration = new LocalDataSourceConfiguration(
localDataSourceConfig.isMsSqlConfigured(), localDataSourceConfig.isSybaseConfigured(),
localDataSourceConfig.getServiceConfigurationMode(), getLocalDBConfigurationInfo(
localDataSourceConfig.getDbConfigurations().getDbConfigInfo(), configurationFactory));
localDataSourceConfiguration
.setUseRisExamIdAsAccession(Boolean.parseBoolean(localDataSourceConfig.getUseRisExamIdAsAccession()));
localDataSourceConfiguration.setCpacsNameFormat(localDataSourceConfig.getCpacsNameFormat());
localDataSourceConfiguration.setTableCacheRefreshInterval(localDataSourceConfig.getTableCacheRefreshInterval());
localDataSourceConfiguration.setAuthorityMatchingMode(localDataSourceConfig.getAuthorityMatchingMode());
return localDataSourceConfiguration;
}
}
How can i resolve this issue?.did i miss anything?
Added #profile in main class.Issue resolved

why is spring boot's DataJpaTest scanning #Component

Confident this hasn't been asked but reading through the Spring docs and testing utilities I found this annotation and thought I'd start using it. Reading through the fine print I read:
Regular #Component beans will not be loaded into the ApplicationContext.
That sounded good and I even liked the idea of using H2 except from what I found the entity I wanted to use had catalog and schema modifiers to it and the default H2 I couldn't figure out how to support that. I made an H2 datasource for the test branch and use that and override the replace. I wound up with
#RunWith(SpringRunner.class)
#ContextConfiguration(classes=ABCH2Congfiguration.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
public class StatusRepositoryTest {
}
However my tests fails fro Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type.
which leads to:
Error creating bean with name 'customerServiceImpl': Unsatisfied dependency.
However the customerServiceImpl is this bean:
#Component
public class CustomerServiceImpl implements CustomerService {
}
That says #Component. The fine print for DataJpaTest says it doesn't load #Components. Why is it doing that and thus failing the test?
As Kyle and Eugene asked below here's the rest:
package com.xxx.abc.triage;
#Component
public interface CustomerService {
}
Configuration
#ComponentScan("com.xxx.abc")
#EnableJpaRepositories("com.xxx.abc")
//#Profile("h2")
public class ABMH2Congfiguration {
#Primary
#Bean(name = "h2source")
public DataSource dataSource() {
EmbeddedDatabase build = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).setName("ABC").addScript("init.sql").build();
return build;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter bean = new HibernateJpaVendorAdapter();
bean.setDatabase(Database.H2);
bean.setShowSql(true);
bean.setGenerateDdl(true);
return bean;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(dataSource);
bean.setJpaVendorAdapter(jpaVendorAdapter);
bean.setPackagesToScan("com.xxx.abc");
return bean;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
And just to clarify the question, why is #Component being loaded into the context within a #DataJpaTest?
#ComponentScan automatically inject all found #Component and #Service into context. You could override it by separate #Bean:
#Bean
CustomerService customerService{
return null;
}
Or remove #Component annotation from CustomerService and CustomerServiceImpl, but you should add #Bean at your production #Configuration
#DataJpaTest does not load #Component, #Service... by default, only #Repository and internal things needed to configure Spring data JPA.
In your test, you can load any #Configuration you need, and in your case, you load #ABMH2Congfiguration which performs a #ComponentScan that's why Spring try to load your CustomerService.
You should only scanning the #Repository in this configuration class, and scan others #Component, #Service... in another #Configuration like DomainConfiguration. It's always a good practice to separate different types of configurations.

Spring JavaConfig + WebMvcConfigurerAdapter + #Autowired => NPE

I have an application with 2 Contexts. Parent for web agnostic business logic and ChildContext (implicitly created by dispatcher servlet) for web logic.
My setup loks like
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper jacksonMapper() { return new ObjectMapper() }
}
and
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper; // <- is null for some reason
#Override
public configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper); // <- bang!
messageConverters.add(converter);
}
}
I need the the object mapper in the parent context, as I use it also in security configuration. But can someone explain me, why the #Autowired objectMapper is null? Its created in the parent context (the fact that the parent exists is even logged by spring at startup). Also #Autowired has required=true by default, so it should not blow up in the configure method (it should have blown up in construction of the context, if the bean wasn't there for some reason).
It seems to me that there might be some lifecycle problem in spring - in a sense that it calls the overridden methods first, and then #Autowires the dependencies... I have also tried to #Autowire the BusinessConfig (should be perfectly legal according to documentation - the result was the same (null)).
What should I do to make this working?
Thanks in advance!
EDIT - ISSUE FOUND
I found the issue. Unfortunately it had nothing to do with WebMvcConfigurerAdapter nor #Configuration. It was caused by premature initialization of context triggered by missing static modifier for propertyPlaceholderConfigurer... I have created issue in Spring core jira (https://jira.spring.io/browse/SPR-14382)
What about simply renaming the bean declaration method to match with the autowired bean?
#Configuration
public class BusinessConfig {
#Bean
public ObjectMapper objectMapper() { return new ObjectMapper() }
}
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ObjectMapper objectMapper;
[...]
}

Spring: Default or no-arg constructor is missing in #Configuration

I am preparing for a Spring core exam and on one of the mock questions I am getting a very confusing answer.
#Configuration
public class ApplicationConfig {
private DataSource dataSource;
#Autowired
public ApplicationConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean(name="clientRepository")
ClientRepository jpaClientRepository() {
return new JpaClientRepository();
}
}
The answer states: Default or no-arg constructor is missing. Default or no-arg constructor is mandatory. Here, the provided constructor with a dataSource parameter is not taken into account.
I don't understand first why constructor is required and second why ApplicationConfig is no good.
#Configuration specifically is an odd beast. Spring needs to analyze it to build a dependency graph before it has the beans to supply, so you can't use constructor injection with a configuration class.
The reason will be clear if you try to register the above configuration with an application context, e.g.:
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfig.class);
The framework will throw an exception complaining about the missing default constructor, because it will look for it while trying to instantiate the configuration using reflection.
A better way would be to have #Autowired dataSource field:
#Autowired DataSource dataSource;
and no constructor explicitely defined (i.e. having an implicit default no-arg constructor).

Spring #Configuration class needs to be autowired

I've created a Spring #Configuration annotated class and I want to autowire a ResourceLoader to it so that I can use it in one of the #Bean methods to lookup a file given by a String. When I am running the app and initialising the context I get a NPE accessing the autowired field, and in debug mode it is shown as being null/not set. Am I wrong expecting the resourceLoader to be present? Am I wrong asserting the autowiring of the Configuration bean happens before its methods get called? The xml configuration loading this bean is tagged with <context:annotation-config/>
#Configuration
public class ClientConfig {
#Autowired
private ResourceLoader resourceLoader;
public #Bean
String configHome() {
return System.getProperty("CONFIG_HOME");
}
public #Bean
PropertiesFactoryBean appProperties() {
String location = "file:" + configHome() + "/conf/webservice.properties";
PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
factoryBean.setLocation(resourceLoader.getResource(location));
return factoryBean;
}
}
I'm not sure whether this is a bug or is the expected behavior. Sometimes it worked for me, sometimes didn't. Anyway, there is another way of achieving what you want:
public #Bean PropertiesFactoryBean appProperties(ResourceLoader resourceLoader) {
// resourceLoader is injected correctly
...
}

Resources