Scanning #Configuration beans in Spring 4 - java

Hy
I am upgrading my web app from Spring 3.1 to 4.1.8 but having issues. My code has not changed (only my pom.xml)
I have a configuration bean in my main context that looks like:
#Configuration
public class StorableServiceConfiguration {
...
#Bean
public StorableService<Template, Long> templateService(ITemplateJpaDao dao) {
return new DaoService<Template, Long>(Template.class, dao);
}
}
And obviously somewhere else in my web app, I have this statement:
#Autowired
#Qualifier("templateService")
private StorableService<Template, String> templateService;
Now this all worked fine with Spring 3.1.1 but after updating the version to 4.1.8, I am getting this error:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [w.wexpense.service.StorableService] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true),
#org.springframework.beans.factory.annotation.Qualifier(value=templateService)}
Anybody got a clue?
I read somewhere that there was a change in Spring 4 on how the context:component-scan behave regarding the #Configuration annotation but can't remember what. Is anybody aware of that?
Thanks

Spring 4 autowire beans using Java generics as form of #Qualifier.
So you have a Bean #Autowired with StorableService<Template, String> but in your #Configuration class your #Bean declares StorableService<Template, Long>.
If you want a StorableService<Template, String> instance you should create another #Bean at your #Configuration class, for example:
#Bean
public StorableService<Template, String> templateService(ITemplateJpaDao dao) {
return new DaoService<Template, String>(Template.class, dao);
}
and autowire it without the #Qualifier annotation:
#Autowired
private StorableService<Template, String> templateService;
Spring 4 will inject it perfectly. Look at this blog post to see this new feature of Spring 4.

Related

Spring AutoWire not working when invoking CRUD Repository

I am having a spring boot app which connects to database using Spring JPA and retreives the data for processing. When I try to autowire using I am getting below error. Can someone please throw somelight.
I have added application entry point , Repo and Tasklet below. Please check it.
Application Entry Point
package com.printbatch;
#SpringBootApplication
#EnableJpaRepositories("com.printbatch.config")
public class AgencyBillPayFileUploadApplication implements
CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(AgencyBillPayFileUploadApplication.class, args);
}
//access command line arguments
#Override
public void run(String... args) throws Exception {
System.out.println("args");
System.out.println(args[0]);
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"ABPBatchInfrastructure.xml",
"AgencyBillPayFileUploadApp.xml"
);
JobLauncher jobLauncher = ctx.getBean(JobLauncher.class);
Job job = ctx.getBean(Job.class);
/*
* jobLauncher.run(job, new JobParametersBuilder().addString("inputResource",
* "file:./products.zip") .addString("targetDirectory",
* "./importproductsbatch/").addString("targetFile", "products.txt")
* .addString("date", "2020-06-02").toJobParameters());
*/
jobLauncher.run(job,
new JobParametersBuilder().addString("inputResource", "file:./products.zip")
.addString("targetDirectory", "./importproductsbatch/").addString("targetFile", "products.txt")
.addString("date", "2005-07-29").toJobParameters());
}
}
package com.printbatch.config;
#Component
public interface PrintJobItemRepo extends CrudRepository<PrintJobItem, Integer> {
List<PrintJobItem> findByPrintStatusEquals(String printStatus );
}
Calling this component in this class
package com.printbatch.tasklet;
public class ReadInputFile implements Tasklet {
private Resource inputResource;
private String targetDirectory;
#Autowired
private PrintJobItemRepo pjr;
}
Error below
Error creating bean with name 'scopedTarget.decompressTasklet': Unsatisfied dependency expressed
through field 'pjr'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'com.printbatch.repo.PrintJobItemRepo' available: expected at least 1 bean which qualifies as
autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'com.printbatch.repo.PrintJobItemRepo' available: expected at least 1 bean which qualifies as
autowire candidate. Dependency annotations:
Stack trace tells pretty well what is going on. com.printbatch.repo.PrintJobItemRepo
You are trying to autowire a class that is not in the JPA repository path. Move your class into com.printbatch.config package.
Remove #Component from PrintJobItemRepo. Is ReadInputFile a managed bean? If not, you should annotate it with Component, Service, or make it as a Bean, so it must become managed object. Spring can only inject managed bean into another managed bean.
Add #Component to ReadInputFile class.
Remove #Component from PrintJobItemRepo class.
We generally use #Repository annotation over such classes.
Check out some examples to see how this works -
Spring boot crud example
Another Spring boot crud example

Updating from springboot 2.0.2 to springboot 2.1.2 results in No qualifying bean of type exception

I updated parent pom to use spring boot 2.1.2 release. Among other errors and deprecations that I fixed there is one that's bothering me most:
Error creating bean with name 'adminServiceImpl': Unsatisfied
dependency expressed through field 'taskExecutor'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true),
#org.springframework.beans.factory.annotation.Qualifier(value="taskExecutor")}
My configuration class looks like this:
#EnableScheduling
#EnableAsync
#Configuration
#ConfigurationProperties("thread.pool")
public class MyAsyncConfig extends AsyncConfigurerSupport {
...
#Bean(name = "taskExecutor")
#Override
#Primary
public TaskExecutor getAsyncExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
return executor;
}
And the class where I'm autowiring has the executor field defined:
#Autowired
#Qualifier("taskExecutor")
private ThreadPoolTaskExecutor taskExecutor;
This used to work in springboot 2.0.2, but when I moved to 2.1.2 release, I got
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor'
available.
One way I fixed this is in configuration class declaring return type of getAsyncExecutor() method to be ThreadPoolTaskExecutor. After doing that, it works. But I am wondering why wouldn't it work as it did in 2.0.2?
An interesting arrangement here that works but it could very well not depending on how the ApplicationContext processes the bean definitions.
On one hand a TaskExecutor is exposed and on the other you are requesting that very specific bean with a different type. The context may not be able to honour that and if that's what you want, you must narrow down the return type of the bean definition to ThreadPoolTaskExecutor.
It'd be tempting to think the new auto-configuration support in Spring Boot is related to the change of behaviour but this breaks as well when they are both excluded.
Since you've upgraded to Spring Boot 2.1, you can reduce your configuration anyway:
#EnableScheduling
#EnableAsync
#Configuration
public class MyConfiguration {}
Please don't use #ConfigurationProperties on configuration classes, these are regular components that we use to bind the environment to.
You can still inject the async ThreadPoolTaskExecutor with that name or the more refined one that we promote, applicationTaskExecutor.

How to Autowired in ConversionService in springboot

Trying to access the ConversionControl in model in springboot, no luck.
#Component
public class CityHelperService {
#Autowired
ConversionService conversionService;// = ConversionServiceFactory.registerConverters();
public City toEntity(CityDTO dto){
City entity = conversionService.convert(dto, City.class);
return entity;
}
public CityDTO toDTO(City entity){
CityDTO dto = conversionService.convert(entity, CityDTO.class);
return dto;
}
}
It shows the following error:
Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.lumiin.mytalk.model.CityModel com.lumiin.mytalk.controllers.CityController.cityModel;
nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cityModel' defined in file : Unsatisfied dependency expressed through constructor argument with index 1 of type [com.lumiin.mytalk.dao.CityHelperService]: : Error creating bean with name 'cityHelperService': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.core.convert.ConversionService com.lumiin.mytalk.dao.CityHelperService.conversionService;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.core.convert.ConversionService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)};
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityHelperService': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.core.convert.ConversionService com.lumiin.mytalk.dao.CityHelperService.conversionService;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.core.convert.ConversionService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Apparently there is no ConversionService bean available, judging by the last nested exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.core.convert.ConversionService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
A look into the Spring documentation reveals, that you should declare a ConversionService bean. In the XML configuration it would look like this:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
And since you're using Spring Boot, I assume you are creating the context programatically, so you should create a method annotated with #Bean, which returns a ConverstionService, like this (explained here):
#Bean(name="conversionService")
public ConversionService getConversionService() {
ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
bean.setConverters(...); //add converters
bean.afterPropertiesSet();
return bean.getObject();
}
Not totally agreed with the accepted answers, because there would be a default ConverstionService named mvcConversionService so you would get duplicate bean exception. Instead addConverter to FormatterRegistry, here is the link for the part answer:
Java Config equivalent for conversionService / FormattingConversionServiceFactoryBean
Also you would need (in some cases) to define to at least an empty Component for ConversionService, something like below:
#Component #Primary
public class MyConversionService extends DefaultConversionService implements ConversionService {
// an empty ConversionService to initiate call to register converters
}
This is to force spring container to initiate a call to:
class WebMvcConfigurerAdapter {
...
public void addFormatters(FormatterRegistry registry) {
//registry.addConverter(...);
}
}
Existing answers didn't work for me:
Customizing via WebMvcConfigurerAdapter.addFormatters (or simply annotating the converter with #Component) only works in the WebMvc context and I want my custom converter to be available everywhere, including #Value injections on any bean.
Defining a ConversionService bean (via ConversionServiceFactoryBean #Bean or #Component) causes Spring Boot to replace the default ApplicationConversionService on the SpringApplication bean factory with the custom bean you've defined, which will probably be based on DefaultConversionService (in AbstractApplicationContext.finishBeanFactoryInitialization). The problem is that Spring Boot adds some handy converters such as StringToDurationConverter to the standard set in DefaultConversionService, so by replacing it you lose those conversions. This may not be an issue for you if you don't use them, but it means that solution won't work for everyone.
I created the following #Configuration class which did the trick for me. It basically adds custom converters to the ConversionService instance used by Environment (which is then passed on to BeanFactory). This maintains as much backwards compatibility as possible while still adding your custom converter into the conversion services in use.
#Configuration
public class ConversionServiceConfiguration {
#Autowired
private ConfigurableEnvironment environment;
#PostConstruct
public void addCustomConverters() {
ConfigurableConversionService conversionService = environment.getConversionService();
conversionService.addConverter(new MyCustomConverter());
}
}
Obviously you can autowire a list of custom converters into this configuration class and loop over them to add them to the conversion service instead of the hard-coded way of doing it above, if you want the process to be more automatic.
To make sure this configuration class gets run before any beans are instantiated that might require the converter to have been added to the ConversionService, add it as a primary source in your spring application's run() call:
#SpringBootApplication
public class MySpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(new Class<?>[] { MySpringBootApplication.class, ConversionServiceConfiguration.class }, args);
}
}
If you don't do this, it might work, or not, depending on the order in which your classes end up in the Spring Boot JAR, which determines the order in which they are scanned. (I found this out the hard way: it worked when compiling locally with an Oracle JDK, but not on our CI server which was using a Azul Zulu JDK.)
Note that for this to work in #WebMvcTests, I had to also combine this configuration class along with my Spring Boot application class into a #ContextConfiguration:
#WebMvcTest(controllers = MyController.class)
#ContextConfiguration(classes = { MySpringBootApplication.class, ConversionServiceConfiguration.class })
#TestPropertySource(properties = { /* ... properties to inject into beans, possibly using your custom converter ... */ })
class MyControllerTest {
// ...
}

How to change the property signUpUrl in ProviderSignInController in Spring Boot

In short
I need to change the property value of a bean defined in the auto configuration of the spring boot, which is not available to configure from application.properties
Descriptive
I want to change the signUpUrl of the bean ProviderSignInController. This is not available to change in the properties file according to the documentation.
http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
So I did somthing like this.
#Configuration
public class SomeConfig {
#Autowired
public void configureProviderSignInController(ProviderSignInController signInController){
signInController.setSignUpUrl("/register");
}
and ended up with the following error
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.social.connect.web.ProviderSignInController] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
But according to the autoconfiguration this bean should be available
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/social/SocialWebAutoConfiguration.java
Please help and guild me if I'm doing this incorrectly.
The error describes that the problem is that you have not instantiated a bean with the type of ProviderSignInController in your method signature for configureProviderSignInController. Instead, you need to instantiate and configure the controller in one of your configurations with the proper constructor signatures required by the controller:
#Configuration
#EnableSocial
public class SocialConfig implements SocialConfigurer {
...
#Bean
public ProviderSignInController providerSignInController(
ConnectionFactoryLocator connectionFactoryLocator,
UsersConnectionRepository usersConnectionRepository) {
ProviderSignInController controller = new ProviderSignInController(
connectionFactoryLocator, usersConnectionRepository,
new SimpleSignInAdapter());
controller.setSignUpUrl("/register");
return controller;
}
}
Alternatively, if you are using XML configurations:
<bean class="org.springframework.social.connect.signin.web.ProviderSignInController">
<!-- relies on by-type autowiring for the constructor-args -->
<property name="signUpUrl" value="/register" />
</bean>
For more information, consult the Spring Social docs on enabling provider sign in.

How to autowired profile configuration class on startup?

I'd like to create different database profile classes, each for a purpose of development, production and testing.
I tried the following with the help of http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/, but it won't wire correctly. Why?
interface DataConfig {
DataSource dataSource();
}
#Configuration
#Profile("dev")
public class StandaloneDataConfig implements DataConfig {
#Bean
#Override
public dataSource dataSource() {
//return the ds
}
}
#Configuration
#Profile("prod")
public class JndiDataConfig implements DataConfig { ... }
#Configuration
#PropertySource({"classpath:config.properties"})
class AppConfig {
#Autowired
private DataConfig cfg;
}
#Configuration
#ComponentScan
#Import(AppConfig.class)
#EnableTransactionManagement
public class SpringBootConfig extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
config.properties:
spring.profiles.active=dev
Result: Exception on startup
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private DataConfig dataConfig; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [DataConfig] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
... 34 common frames omitted
Spring 4.0.3.RELEASE
My setup though seem to work in general: if I remove the #Profile annotation on one of the databases, everything wires up correctly.
You need to rename your config.properties file to application.properties for Spring Boot to pick it up automatically.
I haven't tested your configuration, but my best guess (following some investigation I've done for this post) is that #PropertySources are not available for a #Conditional annotated #Configuration class when autowiring occurs.
According to the source code, #Profile is a #Conditional flavor of annotation with a Condition implementation.
For #PropertySource to be available when they are needed I think you would need your own custom #Conditional implementation just like in the SO post I mentioned above, where you define not a Condition but a ConfigurationCondition making sure to be used at ConfigurationPhase.REGISTER_BEAN phase.

Categories

Resources