I'm trying to inject a service-annotated class into a configuration class in a Spring Boot application, but it doesn't get injected (is set to null), which I assume is due to the Spring lifeycle.
Also, this service has an overloaded constructor that uses constructor injection, and I guess this is also a problem, as autowiring acts upon a default constructor. However, the service needs to be Spring-configured, so I don't think one can create a new instance in a Bean annotated method.
How can one solve this?
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SessionService sessionService;
#Bean
public SessionService sessionService() {
return sessionService;
}
}
public interface SessionService extends BaseCacheService<Session> {
void extendExpiration(String key);
String getSessionId(String key);
}
#Service
public class SessionServiceImpl implements SessionService {
private Environment environment;
private UserService userService;
#Autowired
public SessionServiceImpl(Environment environment, UserService userService) {
this.environment = environment;
this.userService = userService;
}
}
If I exclude the #Bean method, then I get a compilation error:
Your error is the following (you are returning a null value):
#Bean
public SessionService sessionService() {
return sessionService;
}
Solution
Since your SessionServiceImpl is annotated with #Service, you can just remove the #Bean method and let spring create it. Spring already makes it available for you.
Or, If your SessionServiceImpl wasn't annotated with #Service, you would need the following :
#Bean
public SessionService sessionService() {
return new SessionService();
}
If this doesn't work, it may just be that your SessionServiceImpl is in a package not being scanned by spring (as suggested by #Miloš Milivojević)
You may add #ComponentScan to your Configuration class
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#ComponentScan("com.package.to.sessionServiceImpl-or-higher")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
Expanding on #Alex's answer, when you annotate a method with #Bean, it tells Spring that this method will produce that type. So, you essentially told Spring to give you the null reference you already had for all Beans of type SessionService.
If you are using Annotation-based context configuration, you can Autowire any #Component Bean (not just #Service) that can be constructed without runtime parameters (e.g. has a default constructor or an Autowired Constructor). If you need to do something to create the bean (e.g. runtime configuration required), you would either create a method as #Alex suggested, or you can use getBean and pass in the Type and Constructor arguments. The former is generally preferred.
I was facing similar issue while writing an integration test class for a spring boot application. RestTemplate class and CounterService of metrics API are autowired in my service class. I could use #ContextConfiguration(Classes={RestTemplate.class}) for injecting RestTemplate to my service, but adding CounterService.class to above annotation does not help, maybe because CounterService is an interface not a concrete class, Hence I was getting "No bean of type CounterService found" issue.
Thanks to answer by Milos, I included #EnableAutoConfiguration to my integration test class, issue was resolved!
If Alex's answer does not work (removing the #Bean method), you're probably not using #EnableAutoConfiguration or your Application is not in the root-hierarchy package so it's not scanning the whole classpath. Try adding #ComponentScan("service.class.package") to your configuration (in addition to removing the sessionService method) and see if it helps.
Related
I am trying to create a #WebFluxTest in spring to test my controllers.
#WebFluxTest(value = {MyController.class})
public class MyControllerTest {
#MockBean
private MyService service;
#Autowired
private WebTestClient webTestClient;
#Test
void testSomething() {
...
}
However, when I execute the test, I get a lot of: org.springframework.beans.factory.NoSuchBeanDefinitionException for dependencies of #Component s. Meaning that, Spring is trying to find dependencies of #Component when it should ignore those.
I understand that if I use #WebFluxTest, spring should not scan the classpath for any #Component.
My Application class is only annotated with #SpringBootApplication.
What could I be missing here?
SOLUTION UPDATE:
So, I know what was happening. Actually, the class that I had annotated with #Component was an implementation of a WebFilter, and if I check the filter configured for a WebFluxTest (WebFluxTypeExcludeFilter) it adds WebFilter to the include part.
That is why Spring was picking it up.
The error that you're getting could be exactly due to the annotation #WebFluxTest not loading your #Component classes. Could it be that your MyService is instantiating any object that has #Component? Maybe your MyController
Exemplifying, supposing your MyService is like that:
#Service
#AllArgsConstructor
public class MyService {
private final MyRepository repository;
private final Env env;
public void insert(...) {
System.out.println(env.getApplicationName() + " random stuff");
...
}
}
And your Env is annotated with #Component, you will get the same error (NoSuchBeanDefinitionException) if you try to use any method from the Env class since it's null due to #MockBean in MyControllerTest.
The same goes for MyController if it's instantiating any #Component object
If that is the case, then in your MyControllerTest you could try adding #Import(Env.class) or even trying to use when() from mockito with a .thenReturn()
If all that doesn't work, could you please provide more info about your error log and service/controller classes?
So, I know what was happening. Actually, the class that I had annotated with #Component was an implementation of a WebFilter, and if I check the filter configured for a WebFluxTest (WebFluxTypeExcludeFilter) it adds WebFilter to the include part. That is why Spring was picking it up.
I have a classA which implements an interfaceA, with a methodA, then I have a classB in which I call classA with an #Autowired to be able to use methodA, but it gives me a warning that I must create a method for classA. Why is this happening? Doesn't #Autowired work like this in this case? Should I just instantiate classA? Thank you very much for your answers.
ClassA
#RequiredArgsConstructor
public class RepositoryImpl implements IRepository {
#Autowired
private final TransactionDataMapper transactionDataMapper;
#Autowired
private SpringDataColminvoice springDataColminvoice;
#Override
public <S extends TransactionDto> S save(S s) {
Colm colm = transactionDataMapper.toEntity(s);
//methodA
springDataColminvoice.save(colm);
return null;
}
}
InterfaceA
public interface IRepository extends IRepository<TransactionDto, Integer> {}
ClassB
#Service
#RequiredArgsConstructor
public class ServiceImpl implements IInvoiceService {
#Autowired
private RepositoryImpl repositoryImpl;
#Override
public void save(CMessage cMessage) throws HandlerException {
try {
TransactionDto transactionDto = cMessage.getTransaction();
// methodA
repositoryImpl.save(transactionDto);
} catch (Exception e) {
throw new HandlerException(e.getMessage());
}
}
}
Exception
Action:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field RepositoryImpl in com.st.ms.yyu.d.binvoce.infraestructure.rest.spring.services.impl.InvoiceServiceImpl required a bean of type 'com.st.ms.yyu.d.binvoce.infraestructure.db.springdata.repository.impl.ServiceImpl' 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.st.ms.yyu.d.binvoce.infraestructure.db.springdata.repository.impl.RepositoryImpl' in your configuration.
(posting this as an answer since I do not have enough reputation to comment)
As others have pointed out already, a code sample would help tremendously.
That being said, though, it sounds like you're missing implementation for "ClassA".
If you have an interface that "ClassA" implements, you have to implement the interface's methods in "ClassA" before you can use them.
I assume your code currently looks somewhat like this?
public interface InterfaceA {
void methodA();
}
public class ClassA implements InterfaceA {
}
public class ClassB {
#Autowired
ClassA classA; // Cannot use {#link InterfaceA#methodA} because the class does not implement the function
}
If this is your code, make sure you add an implementation for your "methodA()" function in "ClassA". Somewhat like so:
public class ClassA implements InterfaceA {
#Override
public void methodA() {
}
}
Additionally, in order to autowire in Spring (Boot), you need to ensure that the class you'd like to autowire is marked as such. You can autowire beans.
To make "ClassA" in the example eligible for autowiring, make sure to instantiate it either as:
A bean (using the #Bean annotation).
A component (using the #Component annotation).
A service (using the #Service annotation).
Any of the other annotations that may match your use case the best.
In our example, this would look somewhat like this:
#Component // Or #Service / whatever you may need
public class ClassA implements InterfaceA {
#Override
public void methodA() {
}
}
Hope you've found any of this helpful. All the best!
-T
As what I have understood, #Autowire means injecting the value/instance of the specific property where you put the annotation #Autowire. In this case, #Autowire only happens when there is defined/created Bean within your basePackage of your Spring Boot project that can match it, i.e. where your #Autowire referred to (meaning there is no conflict issue like ambiguity, etc. and the DataType(Class) can be implicitly casted). In your example, first you treat the IRepository and/or RepositoryImpl as Repository without using the #Repository annotation to inform the Spring Boot default configuration that this is a Repository bean. As you didn't put the POM.xml or posted the related code, I presumed you are creating your own repository class. I think it's much better to post your dependencies here.
But as what others pointed out. You need to create a bean that can match the #Autowired you've put on TransactDataManager & SpringDataColminvoice. You need also to inform the Spring Boot or register it that your class A is a Bean by annotating
#Bean - defining a regular bean,
#Component - a Component in the Project,
#Service - a Service in the Project,
#Repository - a Repository (if you're using Spring JPA), etc.
#<Other Annotations depending of what other Spring Project/Dependencies your using>
Since newer versions of Spring is moving to annotation based from XML mapping, we need to put proper annotation for each class/object that we want to be auto injected/instantiated from #Autowired using the above sample annotations depending on the role/purpose of your class/object is.
I suggest if you're not sure. Then create a typical bean using common annotation #Bean. So your class A might be
#Component //Insert proper Annotation for your class if necessary. This is just a sample
#RequiredArgsConstructor
public class RepositoryImpl implements IRepository {
#Autowired
private final TransactionDataMapper transactionDataMapper;
#Autowired
private SpringDataColminvoice
springDataColminvoice;//segunda
#Override
public <S extends TransactionDto> S save(S s) {
//Some implementation
}
#Bean
private TransactionDataMapper getTransactionDataMapper(<Some parameters if needed>){
return <an instance for TransactionDataManager>;
}
#Bean
private SpringDataColminvoice getSpringDataColmInvoice(<Some parameters if needed>){
return <an instance for SpringDataColminvoice>;
}
}
Note that 2 beans definition are optional if there are already a Beans define on outside class or if it was marked by other annotation like #Service, #Component or other proper annotations and the other one bean is just a reference parameter for the other bean in order to properly instantiated.
In your class B is the following:
public class ClassB {
#Autowired
ClassA classA;
/*Note: ignore this Bean definition if Class A is annotated with #Component,
#Service, or other proper #Annotation for Class A.
*/
#Bean
private ClassA getClassA(<Some Parameters if Needed>){
return <Instance of Class A>
}
}
Take note that, you don't need to put a Bean definition inside the Class B if you put a proper annotation for your Class A like #Component, #Service, etc.
With Spring Boot 2.1 bean overriding is disabled by default, which is a good thing.
However I do have some tests where I replace beans with mocked instances using Mockito. With the default setting Tests with such a configuration will fail due to bean overriding.
The only way I found worked, was to enable bean overriding through application properties:
spring.main.allow-bean-definition-overriding=true
However I would really like to ensure minimal bean definition setup for my test configuration, which would be pointed out by spring with the overriding disabled.
The beans that I am overriding are either
Defined in another configuration that imported into my test configuration
Auto-discovered bean by annotation scanning
What I was thinking should work in the test configuration overriding the bean and slap a #Primary on it, as we are used to for data source configurations. This however has no effect and got me wondering: Is the #Primary and the disabled bean overriding contradictory?
Some example:
package com.stackoverflow.foo;
#Service
public class AService {
}
package com.stackoverflow.foo;
public class BService {
}
package com.stackoverflow.foo;
#Configuration
public BaseConfiguration {
#Bean
#Lazy
public BService bService() {
return new BService();
}
}
package com.stackoverflow.bar;
#Configuration
#Import({BaseConfiguration.class})
public class TestConfiguration {
#Bean
public BService bService() {
return Mockito.mock(BService.class);
}
}
spring.main.allow-bean-definition-overriding=true can be placed in test configurations. If you need extensive integration testing, you will need to override beans at some point. It's inevitable.
Though the correct answer has already been provided, it implies that your bean will have different names. So, technically, it's not an override.
If you need a real override (because you use #Qualifiers, #Resources or something similar), since Spring Boot 2.X is only possible using the spring.main.allow-bean-definition-overriding=true property.
Update:
Be careful with Kotlin Bean Definition DSL. In Spring Boot it will require a custom ApplicationContextInitializer, like so:
class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {
override fun initialize(context: GenericApplicationContext) =
beans.initialize(context)
}
Now if you decide to override one of such DSL-based beans in your test via #Primary #Bean method, it will not do. The initializer will kick in after #Bean methods and you'd still get the initial, DSL-based bean in your tests even with #Primary on the test #Bean.
One other option would be to also create a test initializer for your tests and list them all in your test properties, like so(order matters):
context:
initializer:
classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer
Bean Definition DSL also supports primary property via:
bean(isPrimary=true) {...}
- which you'll need to eliminate ambiguity when you try to inject a bean, however main:allow-bean-definition-overriding: true is not needed if you go pure DSL way.
(Spring Boot 2.1.3)
Overriding beans means that there may be only one bean with a unique name or id in the context. So you can provide two beans in the following way:
package com.stackoverflow.foo;
#Configuration
public class BaseConfiguration {
#Bean
#Lazy
public BService bService1() {
return new BService();
}
}
package com.stackoverflow.bar;
#Configuration
#Import({BaseConfiguration.class})
public class TestConfiguration {
#Bean
public BService bService2() {
return Mockito.mock(BService.class);
}
}
If you add #Primary then primary bean will be injected by default in:
#Autowired
BService bService;
I make the testing beans available only in test profile, and allow overriding for just while testing, like this:
#ActiveProfiles("test")
#SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true"})
class FooBarApplicationTests {
#Test
void contextLoads() {}
}
The bean I am mocking in the test configuration:
#Profile("test")
#Configuration
public class FooBarApplicationTestConfiguration {
#Bean
#Primary
public SomeBean someBean() {
return Mockito.mock(SomeBean.class);
}
}
It is allowed to override #Component with #Bean by default. In your case
#Service
public class AService {
}
#Component
public class BService {
#Autowired
public BService() { ... }
}
#Configuration
#ComponentScan
public BaseConfiguration {
}
#Configuration
// WARNING! Doesn't work with #SpringBootTest annotation
#Import({BaseConfiguration.class})
public class TestConfiguration {
#Bean // you allowed to override #Component with #Bean.
public BService bService() {
return Mockito.mock(BService.class);
}
}
I am developing OAuth implementation with Jwt tokens.
It's kind of weird but for class TokenAuthenticationService When I try to Autowired this class in a different package, I get
Consider defining a bean of type 'com.company.security.TokenAuthenticationService' in your configuration.
I did a workaround and added #Bean TokenAuthenticationService in that class.Now when I am trying to initialize an interface in the TokenAuthenticationService class, it gives the same type of error for that interface.
Consider defining a bean of type 'com.company.security.UserService' in your configuration.
ComponentScan annotation is configured like #ComponentScan({"com.company"})
What I am missing here and why?
You have two ways to define beans for autowiring in your project.
With classes defined by you, you can use the #Component annotation (or, for service classes, #Service annotation) this way:
#Service
public class TokenAuthenticationService { ... }
If you are using third party classes, you can configure them in a configuration class:
#Configuration
public MyProjectConfig {
#Bean
public ThirdPartyClass serviceClass() { new ThirdPartyClass(); }
}
(Using #Bean annotation is not a workround. You just need to understand its purpose...)
This way autowiring should work...
Pay attention to difference between #Component and #Bean annotations.
I have many spring services with this autowire:
#Autowired
private SmartCardService smartCardService;
I need a dummy class for testing and I defined this class extending the original:
#Service
public class DummySmartCardService extends SmartCardService{
...
}
How can I be sure that all autowire will take the dummy instead of original service without changing all Autowired annotation?
Thanks.
Consider using the #Primary annotation. See here
Load your DummySmartCardService bean from a test version of your application context file instead so that no changes to the code under test are necessary
#ContextConfiguration(locations = {"classpath:test-services.xml"})
Use the #Resource annotation or a #Qualifier, With #Qualifier which discriminates bean types:
#Autowired
#Qualifier("testing")
private SmartCardService smartCardService;
#Service
#Qualifier("testing")
public class DummySmartCardService extends SmartCardService{
...
}
Or with #Resource which uses by-name semantics:
#Resource("dummySmartCardService")
private SmartCardService smartCardService;
#Service("dummySmartCardService")
public class DummySmartCardService extends SmartCardService{
...
}
Theoretically you could use #Qualifier("beanName") but it is discouraged.
But it think would be better if you had a Spring profile to load only test related stubs in your tests:
#Service
#Profile("test")
public class DummySmartCardService extends SmartCardService{
...
}
#ContextConfiguration(locations = {"classpath:services.xml"})
#ActiveProfiles("test")
public class TestSuite{
#Autowired
private SmartCardService smartCardService;
}
IMHO you should take a look at Springockio for proper and rather easy mocking of Spring beans.
You can replace your bean with a mock or wrap with a Spy this way:
#ContextConfiguration(loader = SpringockitoContextLoader.class,
locations = "classpath:/context.xml")
public class SpringockitoAnnotationsMocksIntegrationTest extends
AbstractJUnit4SpringContextTests {
#ReplaceWithMock
#Autowired
private InnerBean innerBean;
#WrapWithSpy
#Autowired
private AnotherInnerBean anotherInnerBean;
....
}
This not only is a clean way (you do not need to change the code being tested by adding qualifiers or profiles) but also allows you to use the capabilities of Mockito for mocking, verifying and spying which is great.