I have the following classes in different packages:
#EnableWebMvc
public class ActuatorConfig
{
// empty
// only needed to get REST so I can hit Actuator endpoints
}
#Component // REMOVE THIS LINE?
#EnableBatchProcessing
public class BatchConfig
{
// empty
}
#Component
#Configuration
public class ScheduleJob
{
#Autowired
private JobBuilderFactory jobBuilder;
#Bean
protected JobExecutionListener scheduleJobListener()
{
return new ScheduleJobListener();
}
}
(EDIT: Added Application class)
#SpringBootApplication
public class AfxApplication
{
public static void main(String[] args)
{
SpringApplication.run(AfxApplication.class, args);
}
}
The first class turns on Actuator features. I don't need any other annotations.
However, if I remove #Component from BatchConfig as noted above, Boot won't start because it can't find the dependency on JobBuilderFactory that it needs in order to populate the #Autowired ScheduleJob.jobBuilder.
The exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.batch.core.configuration.annotation.JobBuilderFactory] 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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
... 25 common frames omitted
When is #Component needed and when isn't it needed?
(I pulled out the #Enable* annotations because I'm trying to run unit tests that don't need all features enabled, and this way I can specify the features I want to enable by using #ComponentScan(basePackageClasses).)
Thanks to M. Deinum, I finally have the answer.
By having #Enable*, I was disabling #EnableAutoConfiguration. The only exception is #EnableBatchProcessing, which is explicitly required. (I understand that it is so, but I am not sure why it cannot be handled by #EnableAutoConfiguration.)
The answer was a combination of the Application class listed above, and this class:
#Configuration
#EnableBatchProcessing
public class BatchConfig
{
}
Once this was in place, everything else worked.
How this will affect my test code will remain a task for another day...
Related
I am using spring-boot (2.2.7.RELEASE) with webflux for a small-ish rest service with mongodb. I have 2 repositories (ARepository, BRepository) implemented something like this:
#Repository
public interface ARepository extends ReactiveMongoRepository<DataDTO, Integer> {
}
I also have an extra service which is using these 2 and a ReactiveMongoTemplate instance. It's wired something like this:
#Slf4j
#Service
public class DefaultTheService implements TheService {
private final ARepository aRepository;
private final BRepository bRepository;
private final ReactiveMongoTemplate mongoTemplate;
#Autowired
public DefaultTheService(ARepository aRepository, BRepository bRepository, ReactiveMongoTemplate mongoTemplate) {
this.aRepository = aRepository;
this.bRepository = bRepository;
this.mongoTemplate = mongoTemplate;
}
}
All is good, it works as it should, no problems there.
Now, I want to write some integration tests and I started like this:
#DataMongoTest
#Slf4j
class DefaultTheServiceTest {
#Autowired
private ARepository aRepository;
#Autowired
private BRepository bRepository;
#Autowired
private ReactiveMongoTemplate reactiveMongoTemplate;
#Autowired
private DefaultTheService defaultTheService;
#Test
void runTheMagicTest() {
// empty body, I just want to see if everything wires up correctly.
}
}
When I want to execute runTheMagicTest (junit5), I am always getting this error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.DefaultTheServiceTest': Unsatisfied dependency expressed through field 'defaultTheService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.DefaultTheService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.DefaultTheService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1716)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1272)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
*! notice the bean name: DefaultTheServiceTest
Normally, I could maybe get away simply creating an instance of DefaultTheService before each test and then calling the methods I want to test, but I'd like to give it a try using spring.
If I simply remove the private DefaultTheService defaultTheService declaration - the test is "running". I am pretty sure I am missing something stupid and I am chasing my tail.
So, can someone ease my pain and point me to the (possibly?) obvious error I am making?
Thanks!
#DataMongoTest:
Annotation that can be used for a MongoDB test that focuses only on MongoDB components.
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MongoDB tests.
Try a #SpringBootTest for a "full"/default application context instead.
For general information refer to:
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing
For (auto-)configuration details & refinement to:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing
... setting #DataMongoTest(useDefaultFilters = false) (+ fine tuning include-/excludeFilters) can also do the desired.
I've specified Qualifier on both autowired et bean method.
So What am i missing ?
#Configuration
#EnableWebSecurity
public class CustomSecurityCOnfig {
#Bean
#Qualifier("entryPoint")
AuthenticationEntryPoint loginUrlAuthenticationEntryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
}
I autowire the field this way
#Autowired
#Qualifier("entryPoint")
private AuthenticationEntryPoint loginUrlAuthenticationEntryPoint;
Stacktrace of the error :
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.core.env.Environment' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1716) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1272) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
EDIT
I Have another implementation of AuthenticationEntryPoint interface :
#Component
public class CustomAuthenticationEntryPoint extends BasicAuthenticationEntryPoint
But in my opinion it doesn't explain the error (as long as i specify the qualifiers)
You're mixing bean name and Qualifier
#Bean(name="someFancyBean")
public ClassXyx fooBar(){
return new ClassXyz()
}
In this example, method fooBar creates a bean of type ClassXyx and it's named someFancyBean. If you want to auto-wire this bean then you have to use
#Service
class SomeFancyService{
#Autowired #Qualifier("someFancyBean") ClassXyx xyz;
}
A configuration class can create multiple beans of the same type but their names are derived from function name. There's no point in using Bean annotation with name="XYZ" unless you want to rename that bean.
The Qualifier annotation is used for referring one of the beans of the same type.
Now coming back to your code
#Configuration
#EnableWebSecurity
public class CustomSecurityCOnfig {
#Bean
public AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
}
In your service you have to Autowired as.
#Autowired
#Qualifier("entryPoint")
private AuthenticationEntryPoint loginUrlAuthenticationEntryPoint;
Also, I would like to point one more thing bean accessibility across package/class.
Generally, all beans created by Spring IOC are public, but it has the same access modifier like Java classes. If you're creating a bean with package scope then you can't auto-wire in another package. Similarly, if a bean is created using private then that bean can be only auto-wired in that class.
I get a classic org.springframework.beans.factory.BeanCreationException:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Server': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private MyClass Server.myClass; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [MyClass] 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.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
at root.package.Application.main(Application.java:14)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private MyClass Server.myClass; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [MyClass] 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:573)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 12 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [MyClass] 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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 14 more
"MyClass" in the stack trace above is of course the FQN of my class, which I replaced in this case (I can't show my package). It's a simple Netty Handler:
#Service
public class MyClass extends SimpleChannelInboundHandler<Protocol> implements Visitor {
#Autowired
private RestTemplate restTemplate;
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Protocol protocol) throws Exception {
channelHandlerContext.writeAndFlush(protocol.accept(this, channelHandlerContext));
}
public Object visit(Protocol protocol, ChannelHandlerContext channelHandlerContext) {
return restTemplate.exchange(...);
}
}
The class that depends on MyClass:
#Component
public class Server {
#Autowired
private MyClass myClass;
private ChannelFuture serverChannelFuture;
#PostConstruct
public void start() throws Exception {
serverChannelFuture = bootstrap().bind(tcpPort()).sync();
}
#PreDestroy
public void stop() throws Exception {
serverChannelFuture.channel().close();
serverChannelFuture.channel().closeFuture().sync();
}
private ServerBootstrap bootstrap() {
// Bootstrap setup
}
}
And my configuration:
#Configuration
#ComponentScan("root.package")
public class SpringConfig {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
public Logger logger() {
return Logger.getLogger(InternalLoggerFactory.class.toString());
}
}
All my classes are in either root.package or a subpackage of it. MyClass is in a subpackage as well.
I have no idea why this would cause the above Exception. I have other Services in my root.package package that are being autowired just fine! Does any one have an idea?
EDIT: after some further testing, it appears the "visit" method is the problem. If I rename it, the application boots fine. Could this be a bug in Spring?
EDIT 2: I celebrated too early. I renamed the method in my Visitor interface and now I'm back to the same problem. Even if I comment out my interface, I cannot have any method with the same name as that interface. MyClass is the only class that implements Visitor that is a Spring bean.
Your class MyClass has no package name?
"No qualifying bean of type [MyClass]"
in your Configuration you set the componentscan to a specififc package
#ComponentScan("root.package")
so Spring could not found your beans.
to fix: remove ("root.package") or add the package "root.package" to your classes
The thing about Spring annotations is that classes must reside in the same package tree as the runner or context.
For example, if your Spring application class is in com.dippity.dot.myapp all other classes annotated as #Service, #Component, etc must also be down the com.dippity.dot.myapp package tree.
A #Service annotated package could live com.dippity.dot.myapp.service and work.
This answer set me on my way: Spring instantiates bean but can't use it
The problem was that I use Spring aspects as well, which are configured with
#EnableAspectJAutoProxy
However, by default, this enables JDK proxies, which work differently (see the answer of that question for more info).
#EnableAspectJAutoProxy(proxyTargetClass=true)
Enables CGLIB proxies, which was the solution to my problem.
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.
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.