Strange spring-boot behaviour when trying to autowire javax.validation.Validator - java

Here's my problem:
I develop a web application based on spring-boot, the autowired annotation of spring works in every layers except for this interface "javax.validation.Validator".
When I'm trying to autowired "javax.validation.Validator" in my validator like this:
#Component
public class BrandValidator{
#Autowired
private javax.validation.Validator validator;
I've got this exception: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.validation.Validator] found for dependency
First I was thinking that no bean was available for this class in the spring context but actually when I list the bean available at the run time (after commenting the Autowired annotation on my validator) I see that this bean is available :
"mvcValidator" with the following type OptionalValidatorFactoryBean (this class extend LocalValidatorFactoryBean and LocalValidatorFactoryBean implements javax.validation.Validator and org.springframework.validation.Validator you can see here http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html)
I have try to add the #Qualifier(value ="mvcValidator") annotation but I got the same exception.
The strange part is that I m able to autowire the "spring" validator (org.springframework.validation.Validator) class perfectly well :
#Autowired
private org.springframework.validation.Validator validatorSpring;
I see in debug mode that the OptionalValidatorFactoryBean present in the context is injected.
And finally if I autowire org.springframework.validation.Validator just before javax.validation.Validator like this :
#Component
public class BrandValidator{
//workaround
#Autowired
private org.springframework.validation.Validator validatorSpring;
#Autowired
private javax.validation.Validator validator;
Now javax.validation.Validator is correctly injected(In debug mode I see that both validators the same object OptionalValidatorFactoryBean). I really don't understand what is going when the context is loaded and I really don't like this workaround (I don't need org.springframework.validation.Validator in my class).
Any idea how correctly inject javax.validation.Validator with spring boot ?

Try adding a #DependsOn(value="mvcValidator") on the BrandValidator class.

Related

Spring dependency injection #Autowired VS dependency injection of an object without #Autowired

what is the main difference between injecting objects with #Autowired and injecting without it ?
I know that spring will initialize the bean , but what it is really offering ?
There are several ways to configure Spring beans and inject dependencies using Spring. One way is by using constructor injection, where the constructor of your Spring bean has arguments which are the dependencies that should be injected:
#Component
public class MyBean {
private final SomeDependency something;
#Autowired
public MyBean(SomeDependency something) {
this.something = something;
}
}
However, since Spring 4.3, it is not necessary anymore to use #Autowired on such a constructor (click link for Spring documentation). So you can write it without the #Autowired:
#Component
public class MyBean {
private final SomeDependency something;
public MyBean(SomeDependency something) {
this.something = something;
}
}
This will work exactly the same as the code above - Spring will automatically understand that you want the dependency to be injected via the constructor. The fact that you can leave out #Autowired is just for convenience.
So, to answer your question: there is no difference.
#Autowired (so the injection) in some situation cannot be used, an example is if your autowired bean not ready because of some async stuff but in the target bean you want to use that.
So in this situation do not use inject (#Autowired) it is better to inject the ApplicationContext and in the exact moment get your bean from there by name or by class (there is a lot off possibilities there).
You can consider the #Autowired with the #Lazy annotation too.

Testing a spring boot controller which has dependencies? (jUnit)

I have this test class :
#RunWith(SpringRunner.class)
#WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {
#Autowired
private MockMvc mockMvc;
#Test
public void simpleTestMethodToGetClassWorking(){
Assert.assertTrue(true);
}
}
but in the class I want to test, I have this line :
#Autowired
AnnoyingServiceWhichIsADependency annoyingDependency;
So when I try to run the test class - I get this error :
java.lang.IllegalStateException: Failed to load ApplicationContext
and the cause by line seems to throw this up :
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ClassToBeTested': Unsatisfied dependency expressed through field 'AnnoyingServiceWhichIsADependency'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '<package-path>.AnnoyingServiceWhichIsADependency' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I will add that the actual class does work, and does what it is meant to do, but I am having trouble making it work in the unit test world.
All help appreciated.
The reason a bean is not created for the dependency class is that you're using #WebMvcTest and not #SpringBootTest: only controllers and the MVC infrastructure classes are scanned. From the docs:
Can be used when a test focuses only on Spring MVC components.
Since it's an MVC test, you can mock the service dependency.
Example: https://reflectoring.io/spring-boot-web-controller-test/
#WebMvcTest is only going to scan the web layer- the MVC infrastructure and #Controller classes. That's it. So if your controller has some dependency to other beans from, e.g. form your service layer, they won't be found to be injected.
If you want a more comprehensive integration test, use #SpringBootTest instead of #WebMvcTest
If you want something closer to a unit test, mock your dependency.
Also note that Field injection (#Autowired directly on the field) is not recommended exactly for these reasons. I recommend you change to constructor injeciton ( add a constructor for Classtobetested and place the #Autowired annotation on it. ) Then for a unit test you can pass in a mock. Constructor injection leads to a more testable and configurable design.
Your test application context is trying to load your ClassToBeTested but is unable to find one of its dependencies and complains about it via that error. Basically you need to have a #Bean of that type in your test context. An option will be to create a TestConfig class which provides a Mock/Spy of that dependency via #Bean annotation. In your test you will have to load inside the context via the #ContextConfiguration annotation this test config you just created.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-testing-annotation-contextconfiguration
Just mock that dependency. Assuming that AnnoyingServiceWhichIsADependency is an interface:
#RunWith(SpringRunner.class)
#WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private AnnoyingServiceWhichIsADependency annoyingDependency;
#Test
public void simpleTestMethodToGetClassWorking(){
Assert.assertTrue(true);
}
}
Use Mockito when and thenReturn methods to instruct the mock.

Unsatisfied dependency expressed through field 'fooService': No qualifying bean of type [aaa.FooService] found for dependency [aaa.FooService]

I have encountered this weird behavior of Spring in Spring boot 1.4.0. Spring basically tells me that it cannot autowire a bean to the resource, because it did not found itself for dependency.
UnsatisfiedDependencyException: Error creating bean with name 'restResource': Unsatisfied dependency expressed through field
'fooService': No qualifying bean of type [**aaa.FooService**] found for dependency [**aaa.FooService**]
FooService is autowired in the resource. When I #Autowire it into #Configuration file, which creates the resource, it is injected there as expected.
This works:
public class ServiceMocksRestConfig extends WebMvcConfigurerAdapter {
#Autowired
private FooService fooService; //instance here
#Bean
public FooResource fooResource() {
return new FooResource(); // debuger stop here
}
//Debugger step into
#RestController
public class FooResource {
#Autowired
private FooService fooService; //bang
Does someone has any idea, what might went wrong?
Funny stuff is that when I run the app from tests using boot spring runner, it also works (everything, including this resource)
I managed today to find the root cause. Its Spring Boot devtools - more precisely its split classloader (related bug: https://github.com/spring-projects/spring-boot/issues/3316)
When I put a breakpoint in ListableBeanFactory when the child REST #Configuration was about to #Autowire the FooService
and did FooService instanceof FooServiceInterface, it returned false.
And when I did FooService.class.getClassLoader() and beanfactory.getBean("fooService" /cannot use class here, would trigger not found exception/).getClass().getClassloader() these were different (one was AppClassLoader and the other was devtools restartable class loader).
Solution: remove boot devtools from classpath.
The exact reason for this is that, there is no bean initialized for type FooService inside the Spring IOC container at the moment the run-time tries to autowire FooService into ServiceMocksRestConfig .
It can be cause by different mistakes in development. This article addresses each and every possible mistakes that can cause this problem.

#Autowired is not working with beans defined in application context file

I defined a bean in spring context file 'applicationContext.xml' like below :
<bean id="daoBean" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.xxx.DAOImpl" />
</bean>
In my service class (ServiceImpl), I am using this bean like below:
#Component("serviceImpl")
public class ServiceImpl{
// other code here
#Autowired
private transient DAOImpl daoBean;
// other code here
}
My service class is being accessed from my JUnit test class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/applicationContext.xml" })
public class JUnitTest{
// other code here
#Autowired
private transient ServiceImpl serviceImpl;
// test cases are here
}
When I execute the test case, it gives error saying:
Error creating bean with name 'ServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private transient com.xxx.DAOImpl
When I remove #Autowired from service class and use #Resource(name = "daoBean") the test case works fine.
public class ServiceImpl{
// other code here
#Resource(name = "daoBean")
private transient DAOImpl daoBean;
// other code here
}
My question is why #Autowired is not working in this case? Do I need to configure any thing else with #Autowired, so that it can work properly. I don't want to change my service layer classes to replace #Autowired to #Resource.
Mockito.mock() has a generic return type T which is erased at runtime, so Spring cannot infer the type of the created mock that will be simply registered as Object in the Spring context. That's why #Autowired doesn't work (as it tries to look up the dependency by its type).
Check out this answer for a solution to the problem.

Java/Spring Problem with #Service and #Autowired annotations

[spring 3.0.5]
[jboss 5.1]
I have several classes labeled as #Service, which implements thet same interface.
For example,
#Service(value="test1")
public TestImpl1 implements Test {}
#Service(value="test2")
public TestImpl2 implements Test {}
Next, I have the following structure
public SomeClass {
#Autowired
#Qualifier("test1")
Test test1;
#Autowired
#Qualifier("test2")
Test test2;
I am getting an exception (at deployment)
10:36:58,277 ERROR [[/test-web]] Servlet /test-web threw load()
exception
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
unique bean of type [pl.tests] is defined: expected single matching
bean but found 2: [test1, test2]
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doReso lveDependency(DefaultListableBeanFactory.java:
796)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolv eDependency(DefaultListableBeanFactory.java:
703)
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostPro cessor
$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:
474)
Anyone know how to solve this?
T.
A few options:
Use #Resource(name="test1") in the injection point
can use the javax.inject.Qualifer mechanism. In short - you define an annotation (#Test) and annotate the annotation with #Qualifier. Then use #Autowired #Test on the injection point.
explicitly set qualifiers on the target bean. The docs say show only the xml version <qualifier />, but try adding #Qualifier("test1") on the service definition
Here is the documentation about it

Categories

Resources