#Autowired field is null - spring

Autowiring works everywhere in application except inside this custom validation annotation class where it is null when called from inside isValid() method.
javax.validation:validation-api: 2.0.1.Final
org.hibernate:hibernate-validator: 5.0.1.Final
spring: 5.1.4.RELEASE
#Component
public class ValidatorUniqueUsername implements ConstraintValidator<UniqueUsername, String> {
#Autowired
AccountService jpaAccountService;
#Override
public void initialize(UniqueUsername constraintAnnotation) { }
#Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username != null && jpaAccountService.findByUsername(username) == null;
}
}
#Entity
...
public class Account extends BaseEntity<Long> implements Serializable{
#NotEmpty
#UniqueUsername
private String username;
}
#Configuration
public class AppConfig implements AsyncConfigurer {
#Bean
public Validator validatorFactory() {
return new LocalValidatorFactoryBean();
}
#Bean
public static LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}

Your custom annotation #UniqueUsername instantiates and calls your ValidatorUniqueUsername but it does not inject it even it is annotated with #Component.
And because of this none of the resources to be autowired in your ValidatorUniqueUsername will be injected.
You could try to add this to your #Configuration:
#Bean
public Validator validatorFactory() {
return new LocalValidatorFactoryBean();
}
See more here (excerpt below):
In spring if we register LocalValidatorFactoryBean to bootstrap javax.validation.ValidatorFactory then custom ConstraintValidator classes are loaded as Spring Bean. That means we can have benefit of Spring's dependency injection in validator classes.

Related

Spring can't find Autowired interface implementation

I have a main SpringBootApplication class here:
package com.example.springproj;
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
#RestController class here:
package com.example.springproj.controller;
#RestController
#Api("Sample")
public class RefDataController {
#Autowired
#Qualifier("RefDataServiceImpl")
private RefDataService refDataService;
#GetMapping(path = {"/refdata"}, produces = {"application/json"})
public ResponseEntity<Configuration> getRefData() {
// etc
}
}
The controller autowires this interface:
package com.example.springproj.service;
public interface RefDataService {
Configuration getConfiguration(String param);
}
Which is implemented by this class:
package com.example.springproj.services;
#Service
public class RefDataServiceImpl implements RefDataService {
#Autowired
private ConfigRepository config;
#Value("${ENV}")
private String environment;
#Override
public Configuration getConfiguration(String param) {
// etc
}
}
But when I run the App.java file, I get this
***************************
APPLICATION FAILED TO START
***************************
Description:
Field refDataService in com.citi.icrm.risk.springproj.controller.RefDataController required a bean of type 'com.citi.icrm.risk.springproj.service.RefDataService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
- #org.springframework.beans.factory.annotation.Qualifier(value=RefDataServiceImpl)
Action:
Consider defining a bean of type 'com.citi.icrm.risk.springproj.service.RefDataService' in your configuration.
I'm reasonably sure that this auto-wiring should work, and I'm not sure how to go about configuring this bean in a Spring boot app. What am I doing wrong?
EDIT: Things I've already tried include:
Removing all of the #Qualifier annotations
#RestController
#Api("Sample")
public class RefDataController {
#Autowired
private RefDataServiceImpl refDataService;
#GetMapping(path = {"/refdata"}, produces = {"application/json"})
public ResponseEntity<Configuration> getRefData() {
System.err.println("testing.");
return new ResponseEntity<Configuration>(refDataService.getConfiguration("EEMS_USER_DETAIL_URL"), HttpStatus.OK);
}
}
public class RefDataServiceImpl implements RefDataService {
#Autowired
private ConfigRepository config;
#Value("${ENV}")
private String environment;
#Override
public Configuration getConfiguration(String param) {
try {
return config.getConfiguration(param, environment);
} catch (Exception e) {
e.printStackTrace();
throw (RuntimeException) new RuntimeException().initCause(e);
}
}
}
Changing the bean names to match convention
#RestController
#Api("Sample")
public class RefDataController {
#Autowired
#Qualifier("refDataServiceImpl")
private RefDataService refDataService;
#GetMapping(path = {"/refdata"}, produces = {"application/json"})
public ResponseEntity<Configuration> getRefData() {
System.err.println("testing.");
return new ResponseEntity<Configuration>(refDataService.getConfiguration("EEMS_USER_DETAIL_URL"), HttpStatus.OK);
}
}
#Service("refDataServiceImpl")
public class RefDataServiceImpl implements RefDataService {
#Autowired
private ConfigRepository config;
#Value("${ENV}")
private String environment;
#Override
public Configuration getConfiguration(String param) {
try {
return config.getConfiguration(param, environment);
} catch (Exception e) {
e.printStackTrace();
throw (RuntimeException) new RuntimeException().initCause(e);
}
}
}
For reference, the files fall into the app's package structure like so:
com.example.springproj
-> com.example.springproj.controller
--> RefDataController
-> com.example.springproj.services
--> RefDataService
-> com.exampple.springproj.services.impl
---> RefDataServiceImpl
Here's the folder structure, since some people have asked:
Firstly, you don't need #Qualifier("RefDataServiceImpl") if you have only one implementation of the RefDataService interface.
You just need
#Autowired
private RefDataService refDataService;
Secondly, the name of a bean generated on a class name but starts with a lowercase letter. In your example, the name of bean will look like refDataServiceImpl.
So, you can autowired this bean like below
#Autowired
#Qualifier("refDataServiceImpl")
private RefDataService refDataService;
Thirdly, you can specify the name of bean
#Service("youBeanName")
public class RefDataServiceImpl implements RefDataService
and then autowired this bean by the name in you controller, for example
#RestController
#Api("Sample")
public class RefDataController {
#Autowired
#Qualifier("youBeanName")
private RefDataService refDataService;
//....
}
Change the #Service annotation on the RefDataServiceImpl class as follows:
#Service("RefDataServiceImpl")
public class RefDataServiceImpl implements RefDataService
The #Qualifier name in the autowired service does not match a bean in your spring configuration.
The default naming convention is the full path of the class.
Because of this,
the name that Spring is probably using in your config for the RefDataServiceImpl service is this: "com.example.springproj.services.RefDataServiceImpl".
Added:
This page might be a good read: https://www.baeldung.com/spring-qualifier-annotation.
Attempt Two:
Try this
#Service
#Qualifier("RefDataServiceImpl")
#Service("RefDataServiceImpl")
public class RefDataServiceImpl implements RefDataService
I fixed the issue by putting RefDataServiceImpl in the same package as RefDataService.Before this I was keeping it in a sub-folder to the main services package. I'm still certain that I should be able to make this work with an implementation sub-folder, but this works as a solution for now.
I ran into this same issue while trying to implement a class with a database query. Adding #Repository to the top of the implemented class solved my problem.

Spring boot autowiring an interface with multiple implementations

In normal Spring, when we want to autowire an interface, we define it's implementation in Spring context file.
What about Spring boot?
how can we achieve this?
currently we only autowire classes that are not interfaces.
Another part of this question is about using a class in a Junit class inside a Spring boot project.
If we want to use a CalendarUtil for example, if we autowire CalendarUtil, it will throw a null pointer exception. What can we do in this case? I just initialized using "new" for now...
Use #Qualifier annotation is used to differentiate beans of the same interface
Take look at Spring Boot documentation
Also, to inject all beans of the same interface, just autowire List of interface
(The same way in Spring / Spring Boot / SpringBootTest)
Example below:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
public interface MyService {
void doWork();
}
#Service
#Qualifier("firstService")
public static class FirstServiceImpl implements MyService {
#Override
public void doWork() {
System.out.println("firstService work");
}
}
#Service
#Qualifier("secondService")
public static class SecondServiceImpl implements MyService {
#Override
public void doWork() {
System.out.println("secondService work");
}
}
#Component
public static class FirstManager {
private final MyService myService;
#Autowired // inject FirstServiceImpl
public FirstManager(#Qualifier("firstService") MyService myService) {
this.myService = myService;
}
#PostConstruct
public void startWork() {
System.out.println("firstManager start work");
myService.doWork();
}
}
#Component
public static class SecondManager {
private final List<MyService> myServices;
#Autowired // inject MyService all implementations
public SecondManager(List<MyService> myServices) {
this.myServices = myServices;
}
#PostConstruct
public void startWork() {
System.out.println("secondManager start work");
myServices.forEach(MyService::doWork);
}
}
}
For the second part of your question, take look at this useful answers first / second
You can also make it work by giving it the name of the implementation.
Eg:
#Autowired
MyService firstService;
#Autowired
MyService secondService;
Assume that you have a GreetingService
public interface GreetingService {
void doGreetings();
}
And you have 2 implementations HelloService
#Service
#Slf4j
public class HelloService implements GreetingService{
#Override
public void doGreetings() {
log.info("Hello world!");
}
}
and HiService
#Slf4j
#Service
public class HiService implements GreetingService{
#Override
public void doGreetings() {
log.info("Hi world!");
}
}
Then you have another interface, which is BusinessService to call some business
public interface BusinessService {
void doGreetings();
}
There are some ways to do that
#1. Use #Autowired
#Component
public class BusinessServiceImpl implements BusinessService{
#Autowired
private GreetingService hiService; // Spring automatically maps the name for you, if you don't want to change it.
#Autowired
private GreetingService helloService;
#Override
public void doGreetings() {
hiService.doGreetings();
helloService.doGreetings();
}
}
In case you need to change your implementation bean name, refer to other answers, by setting the name to your bean, for example #Service("myCustomName") and applying #Qualifier("myCustomName")
#2. You can also use constructor injection
#Component
public class BusinessServiceImpl implements BusinessService {
private final GreetingService hiService;
private final GreetingService helloService;
public BusinessServiceImpl(GreetingService hiService, GreetingService helloService) {
this.hiService = hiService;
this.helloService = helloService;
}
#Override
public void doGreetings() {
hiService.doGreetings();
helloService.doGreetings();
}
}
This can be
public BusinessServiceImpl(#Qualifier("hiService") GreetingService hiService, #Qualifier("helloService") GreetingService helloService)
But I am using Spring Boot 2.6.5 and
public BusinessServiceImpl(GreetingService hiService, GreetingService helloService)
is working fine, since Spring automatically get the names for us.
#3. You can also use Map for this
#Component
#RequiredArgsConstructor
public class BusinessServiceImpl implements BusinessService {
private final Map<String, GreetingService> servicesMap; // Spring automatically get the bean name as key
#Override
public void doGreetings() {
servicesMap.get("hiService").doGreetings();
servicesMap.get("helloService").doGreetings();
}
}
List also works fine if you run all the services. But there is a case that you want to get some specific implementation, you need to define a name for it or something like that. My reference is here
For this one, I use #RequiredArgsConstructor from Lombok.
As mentioned in the comments, by using the #Qualifier annotation, you can distinguish different implementations as described in the docs.
For testing, you can use also do the same. For example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyClassTests {
#Autowired
private MyClass testClass;
#MockBean
#Qualifier("default")
private MyImplementation defaultImpl;
#Test
public void givenMultipleImpl_whenAutowiring_thenReturnDefaultImpl() {
// your test here....
}
}
There are 2 approaches when we have autowiring of an interface with multiple implementations:
Spring #Primary annotation
In short it tells to our Spring application whenever we try to autowire our interface to use that specific implementation which is marked with the #Primary annotation. It is like a default autowiring setting. It can be used only once per cluster of implementations of an interface. → #Primary Docs
Spring #Qualifier annotation
This Spring annotation is giving us more control to select the exact implementation wherever we define a reference to our interface choosing among its options. → #Qualifier Docs
For more details follow the links to their documentation.
public interface SomeInterfaces {
void send(String message);
String getType();
}
kafka-service
#Component
public class SomeInterfacesKafkaImpl implements SomeInterfaces {
private final String type = "kafka";
#Override
public void send(String message) {
System.out.println(message + "through Kafka");
}
#Override
public String getType() {
return this.type;
}
}
redis-service
#Component
public class SomeInterfacesRedisImpl implements SomeInterfaces {
private final String type = "redis";
#Override
public void send(String message) {
System.out.println(message + "through Redis");
}
#Override
public String getType() {
return this.type;
}
}
master
#Component
public class SomeInterfacesMaster {
private final Set<SomeInterfaces> someInterfaces;
public SomeInterfacesMaster(Set<SomeInterfaces> someInterfaces) {
this.someInterfaces = someInterfaces;
}
public void sendMaster(String type){
Optional<SomeInterfaces> service =
someInterfaces
.stream()
.filter(service ->
service.getType().equals(type)
)
.findFirst();
SomeInterfaces someService =
service
.orElseThrow(() -> new RuntimeException("There is not such way for sending messages."));
someService .send(" Hello. It is a letter to ....");
}
}
test
#SpringBootTest
public class MultiImplementation {
}
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTest extends MultiImplementation {
#Autowired
private SomeInterfacesMaster someInterfacesMaster;
#Test
void sendMaster() {
someInterfacesMaster.sendMaster("kafka");
}
}
Thus, according to the Open/Closed principle, we only need to add an implementation without breaking existing code.
#Component
public class SomeInterfacesRabbitImpl implements SomeInterfaces {
private final String type = "rabbit";
#Override
public void send(String message) {
System.out.println(message + "through Rabbit");
}
#Override
public String getType() {
return this.type;
}
}
test-v2
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeInterfacesMasterTestV2 extends MultiImplementation {
#Autowired
private SomeInterfacesMaster someInterfacesMaster;
#Test
void sendMasterV2() {
someInterfacesMaster.sendMaster("rabbit");
}
}
If we have multiple implementations of the same interface, Spring needs to know which one it should be autowired into a class. Here is a simple example of validator for mobile number and email address of Employee:-
Employee Class:
public class Employee {
private String mobileNumber;
private String emailAddress;
...
/** Getters & Setters omitted **/
}
Interface EmployeeValidator:
public interface EmployeeValidator {
public Employee validate(Employee employee);
}
First implementation class for Mobile Number Validator:
#Component(value="EmployeeMobileValidator")
public class EmployeeMobileValidator implements EmployeeValidator {
#Override
public Employee validate(Employee employee) {
//Mobile number Validation logic goes here.
}
}
Second implementation class for Email address Validator:
#Component(value="EmployeeEmailValidator")
public class EmployeeEmailValidator implements EmployeeValidator {
#Override
public Employee validate(Employee employee) {
//Email address validation logic goes here.
}
}
We can now autowired these above validators individually into a class.
Employee Service Interface:
public interface EmployeeService {
public void handleEmployee(Employee employee);
}
Employee Service Implementation Class
#Service
public class EmployeeServiceImpl implements EmployeeService {
/** Autowire validators individually **/
#Autowired
#Qualifier("EmployeeMobileValidator") // Autowired using qualifier for mobile validator
private EmployeeValidator mobileValidator;
#Autowired
#Qualifier("EmployeeEmailValidator") // Autowired using qualifier for email valodator
private EmployeeValidator emailValidator;
#Override
public void handleEmployee(Employee employee) {
/**You can use just one instance if you need**/
employee = mobileValidator.validate(employee);
}
}

How to use annotation and avoid xml configuration in spring framework

I have designed a packing structure.
Controller
Delegates (which is helper class) - this class do all the business and return the value to Controllers.
Service
Service Implementation
DAO
DAO Implementation.
I want to implement autowired (Annotation) concept and would like to avoid xml configuration such as service and DAO configuration on spring-bean.xml.
This code is not working if I want to avoid xml configuration.
I have done those changes
bean id :loginDelegate, userService, userDao
added the #Service & #Repository annotation to the corresponding service & DAO implementation.
#Controller("loginController")
public class LoginController {
#Autowired
private LoginDelegate loginDelegate;
public LoginDelegate getLoginDelegate() {
return this.loginDelegate;
}
public void setLoginDelegate(LoginDelegate tLoginDelegate) {
this.loginDelegate = tLoginDelegate;
}
#RequestMapping(value="/login.do",method=RequestMethod.GET)
public ModelAndView displayLogin(HttpServletRequest request, HttpServletResponse response) {
log.info("<---displayLogin()--->");
ModelAndView model = new ModelAndView("login");
LoginBean loginBean = new LoginBean();
model.addObject("loginBean", loginBean);
return model;
}
}
public class LoginDelegate {
#Autowired
private IUserService userService;
public IUserService getUserService() {
return this.userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
public boolean isValidUser(String username, String password) throws Exception {
return userService.isValidUser(username, password);
}
}
public interface IUserService {
public boolean isValidUser(UserBean userObj);
public int addUsers(UserBean userObj);
}
public class UserServiceImpl implements IUserService {
#Autowired
private IUserDao userDao;
public IUserDao getUserDao() {
return this.userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
public boolean isValidUser(UserBean userObj) {
return userDao.isExistUser(userObj);
}
#Override
public int addUser(final UserBean userObj) {
return userDao.saveUserDetails(userObj);
}
}
public interface IUserDao {
public boolean isExistUser(UserBean userObj);
public int saveUserDetails(UserBean userObj);
}
public class UserDaoImpl implements IUserDao {
#Autowired
UserBean userObj;
#Autowired
DataSource dataSource ;
public DataSource getDataSource(){
return this.dataSource;
}
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
Use Java-based configuration if you want to completely get rid of XML-based configuration
#Configuration
#ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
The above normal Java class when annotated with #Configuration, makes it a 'Spring Configuration class' (analogous to XML-based configuration).
#ComponentScan annotation scans for classes annotated with #Component, #Controller, #Service, #Repository classes from the package defined during start-up time to get them registered as Spring beans. This can be done in XML also with <context:component-scan base-package="com.acme" />
Refer:http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-java-instantiating-container-scan

Test Custom Validator with Autowired spring Service

I have a custom Hibernate Validator for my entities. One of my validators uses an Autowired Spring #Repository. The application works fine and my repository is Autowired successfully on my validator.
The problem is i can't find a way to test my validator, cause i can't inject my repository inside it.
Person.class:
#Entity
#Table(schema = "dbo", name = "Person")
#PersonNameMustBeUnique
public class Person {
#Id
#GeneratedValue
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column()
#NotBlank()
private String name;
//getters and setters
//...
}
PersonNameMustBeUnique.class
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = { PersonNameMustBeUniqueValidator.class })
#Documented
public #interface PersonNameMustBeUnique{
String message() default "";
Class<?>[] groups() default {};
Class<? extends javax.validation.Payload>[] payload() default {};
}
The validator:
public class PersonNameMustBeUniqueValidatorimplements ConstraintValidator<PersonNameMustBeUnique, Person> {
#Autowired
private PersonRepository repository;
#Override
public void initialize(PersonNameMustBeUnique constraintAnnotation) { }
#Override
public boolean isValid(Person entidade, ConstraintValidatorContext context) {
if ( entidade == null ) {
return true;
}
context.disableDefaultConstraintViolation();
boolean isValid = nameMustBeUnique(entidade, context);
return isValid;
}
private boolean nameMustBeUnique(Person entidade, ConstraintValidatorContext context) {
//CALL REPOSITORY TO CHECK IF THE NAME IS UNIQUE
//ADD errors if not unique...
}
}
And the context file has a validator bean:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
Again, it works fine, but i don't know how to test it.
My test file is:
#RunWith(MockitoJUnitRunner.class)
public class PersonTest {
Person e;
static Validator validator;
#BeforeClass
public static void setUpClass() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
#Test
public void name__must_not_be_null() {
e = new Person();
e.setName(null);
Set<ConstraintViolation<Person>> violations = validator.validate(e);
assertViolacao(violations, "name", "Name must not be null");
}
}
I was facing very similar problem: How to write pure unit test of custom validator wich has autowired configuration bean?
I could manage to solve it by following code (inspired by this answer of user abhishekrvce).
This is pure unit test of custom validator with #Autowired configuration bean, which reads the data from configuration file (not showed in code).
#Import({MyValidator.class})
#ContextConfiguration(classes = MyConfiguration.class, initializers = ConfigFileApplicationContextInitializer.class)
class MyValidatorTest {
private LocalValidatorFactoryBean validator;
#Autowired
private ConfigurableApplicationContext applicationContext;
#BeforeEach
void initialize() {
SpringConstraintValidatorFactory springConstraintValidatorFactory
= new SpringConstraintValidatorFactory(
applicationContext.getAutowireCapableBeanFactory());
validator = new LocalValidatorFactoryBean();
validator.setConstraintValidatorFactory(springConstraintValidatorFactory);
validator.setApplicationContext(applicationContext);
validator.afterPropertiesSet();
}
#Test
void isValid()
{
Set<ConstraintViolation<MyObject>> constraintViolations = validator
.validate(myObjectInstance);
assertThat(constraintViolations).hasSize(1);
}
}
U can add the following bean to your Spring Context in the test:
#RunWith(SpringRunner.class)
#Import(LocalValidatorFactoryBean.class)
public class PersonTest {
#Autowired
private Validator validator;
{
validator.validate(new Person());
}
...
}
On #BeforeClass:
#BeforeClass
public static void setUpClass() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
And in your test you need to replace the beans with your mocked bean:
myValidator.initialize(null);
BeanValidatorTestUtils.replaceValidatorInContext(validator, usuarioValidoValidator, e);
The class that do all the magic:
public class BeanValidatorTestUtils {
#SuppressWarnings({ "rawtypes", "unchecked" })
public static <A extends Annotation, E> void replaceValidatorInContext(Validator validator,
final ConstraintValidator<A, ?> validatorInstance,
E instanceToBeValidated) {
final Class<A> anotacaoDoValidador = (Class<A>)
((ParameterizedType) validatorInstance.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0];
ValidationContextBuilder valCtxBuilder = ReflectionTestUtils.<ValidationContextBuilder>invokeMethod(validator,
"getValidationContext");
ValidationContext<E> validationContext = valCtxBuilder.forValidate(instanceToBeValidated);
ConstraintValidatorManager constraintValidatorManager = validationContext.getConstraintValidatorManager();
final ConcurrentHashMap nonSpyHashMap = new ConcurrentHashMap();
ConcurrentHashMap spyHashMap = spy(nonSpyHashMap);
doAnswer(new Answer<Object>() {
#Override public Object answer(InvocationOnMock invocation) throws Throwable {
Object key = invocation.getArguments()[0];
Object keyAnnotation = ReflectionTestUtils.getField(key, "annotation");
if (anotacaoDoValidador.isInstance(keyAnnotation)) {
return validatorInstance;
}
return nonSpyHashMap.get(key);
}
}).when(spyHashMap).get(any());
ReflectionTestUtils.setField(constraintValidatorManager, "constraintValidatorCache", spyHashMap);
}
}
We also faced the similar problem where #Autowiring was failing (not initialised) in ConstrainValidator Class. Our ConstraintValidator Implemented class was using a value which supposed to be read from the application.yml file. Below solution helped us as this is using a pure spring scope. Hope this helps, with proper SpringJunit4ClassRunner.
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;
import org.springframework.web.context.WebApplicationContext;
#WebAppConfiguration
#ContextConfiguration(classes = {ApplicationConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource(properties = {
"spring.someConfigValue.InApplicationYaml=Value1",
})
public class MyTest {
#Autowired
private WebApplicationContext webApplicationContext;
LocalValidatorFactoryBean validator;
#Before
public void setup() {
SpringConstraintValidatorFactory springConstraintValidatorFactory
= new SpringConstraintValidatorFactory(webApplicationContext.getAutowireCapableBeanFactory());
validator = new LocalValidatorFactoryBean();
validator.setConstraintValidatorFactory(springConstraintValidatorFactory);
validator.setApplicationContext(webApplicationContext);
validator.afterPropertiesSet();
}
#Test
public void should_have_no_violations_for_all_valid_fields() {
Set<ConstraintViolation<PojoClassWhichHaveConstraintValidationAnnotation>> violations = validator.validate(pojoClassObjectWhichHaveConstraintValidationAnnotation);
assertTrue(violations.isEmpty());
}
}
#Configuration
public class ApplicationConfig {
#Value("${spring.someConfigValue.InApplicationYaml=Value1}")
public String configValueToBeReadFromApplicationYamlFile;
}
Recently I had the same problem with my custom validator. I needed to validate a model being passed to a controller's method (method level validation). The validator invoked but the dependencies (#Autowired) could not be injected. It took me some days searching and debugging the whole process. Finally, I could make it work. I hope my experience save some time for others with the same problem. Here is my solution:
Having a jsr-303 custom validator like this:
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD,
ElementType.PARAMETER,
ElementType.TYPE,
ElementType.METHOD,
ElementType.LOCAL_VARIABLE,
ElementType.CONSTRUCTOR,
ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE })
#Constraint(validatedBy = SampleValidator.class)
public #interface ValidSample {
String message() default "Default sample validation error";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class SampleValidator implements ConstraintValidator<ValidSample, SampleModel> {
#Autowired
private SampleService service;
public void initialize(ValidSample constraintAnnotation) {
//init
}
public boolean isValid(SampleModel sample, ConstraintValidatorContext context) {
service.doSomething();
return true;
}
}
You should configure spring test like this:
#ComponentScan(basePackages = { "your base packages" })
#Configurable
#EnableWebMvc
class SpringTestConfig {
#Autowired
private WebApplicationContext wac;
#Bean
public Validator validator() {
SpringConstraintValidatorFactory scvf = new SpringConstraintValidatorFactory(wac.getAutowireCapableBeanFactory());
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setConstraintValidatorFactory(scvf);
validator.setApplicationContext(wac);
validator.afterPropertiesSet();
return validator;
}
#Bean
public MethodValidationPostProcessor mvpp() {
MethodValidationPostProcessor mvpp = new MethodValidationPostProcessor();
mvpp.setValidatorFactory((ValidatorFactory) validator());
return mvpp;
}
#Bean
SampleService sampleService() {
return Mockito.mock(SampleService.class);
}
}
#WebAppConfiguration
#ContextConfiguration(classes = { SpringTestConfig.class, AnotherConfig.class })
public class ASampleSpringTest extends AbstractTestNGSpringContextTests {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#BeforeClass
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.build();
}
#Test
public void testSomeMethodInvokingCustomValidation(){
// test implementation
// for example:
mockMvc.perform(post("/url/mapped/to/controller")
.accept(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json))
.andExpect(status().isOk());
}
}
Note that, here I am using testng, but you can use JUnit 4. The whole configuration would be the same except that you would run the test with #RunWith(SpringJUnit4ClassRunner.class) and do not extend the AbstractTestNGSpringContextTests.
Now, #ValidSample can be used in places mentioned in #Target() of the custom annotation.
Attention: If you are going to use the #ValidSample annotation on method level (like validating method arguments), then you should put class level annotation #Validated on the class where its method is using your annotation, for example on a controller or on a service class.
A solution with JUnit4 and Mockito:
#Import(LocalValidatorFactoryBean.class)
#RunWith(SpringRunner.class)
public class MyCustomValidatorTest {
#Autowired
private Validator validator;
#MockBean
private PersonRepository repository;
#Test
public void name_must_not_be_null() {
// given
when(repository.findByName(any())).thenReturn(Collection.emptyList());
Person person = new Person();
person.setName(null);
// when
Set<ConstraintViolation<Person>> violations = validator.validate(person);
// then
assertViolation(violations, "name", "Name must not be null");
}
}
You can test the validator stand alone and use reflection for inject the autowired attribute.
Constraint annotation:
#Target({ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = EmailAlreadyExistsValidator.class)
public #interface EmailAlreadyExists {
String message() default "Email already exists in the database";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator:
public class EmailAlreadyExistsValidator implements
ConstraintValidator<EmailAlreadyExists, String> {
#Autowired
private UserRepository repository;
#Override
public void initialize(EmailAlreadyExists constraintAnnotation) {}
public boolean isValid(String email, ConstraintValidatorContext context) {
Optional<User> opUser = repository.findByEmail(email);
return (opUser.isEmpty());
}
}
Unit Test (ReflectionTestUtils do the magic):
public class EmailAlreadyExistsValidatorTest {
#Mock
private EmailAlreadyExists emailAlreadyExists;
#Mock
private ConstraintValidatorContext constraintValidatorContext;
#Mock
private UserRepository repository;
private EmailAlreadyExistsValidator validator;
#BeforeEach
public void beforeEach() {
MockitoAnnotations.openMocks(this);
validator = new EmailAlreadyExistsValidator();
validator.initialize(emailAlreadyExists);
ReflectionTestUtils.setField(validator, "repository", repository);
}
#Test
#DisplayName("Given an user with existent email then validation must fail")
public void isValid_existentPassword_mustFail() {
final String existentEmail = "testuser#test.com";
User savedUser = new User("1213443455",
"Test User",
existentEmail,
"12345",
new Date());
Optional<User> opUser = Optional.of(savedUser);
when(repository.findByEmail(anyString())).thenReturn(opUser);
assertFalse(validator.isValid(existentEmail,constraintValidatorContext));
}
}
It might be a bit late but I faced the same issue lately so I'll post how I solved the problem, as this could help other people.
The problem is basically that Hibernate's standard Validator implementation that you get by calling Validation.buildDefaultValidatorFactory().getValidator() does not know anything about Spring's application context so it cannot inject dependencies in your custom constraint validators.
In a Spring application the implementation of both the Validator and the ValidatorFactory interface is the class LocalValidatorFactoryBean, which can delegate to the ApplicationContext to instantiate constraint validators with dependencies injected.
What you need to do is
Instantiate your constraint validators with their (mocked, I presume) dependencies
Create your own ValidatorFactory that holds all the constraint validators from bulletpoint 1
Instantiate your Validator from such factory
This is the custom validator factory
public class CustomLocalValidatorFactoryBean extends LocalValidatorFactoryBean {
private final List<ConstraintValidator<?, ?>> customConstraintValidators;
public CustomLocalValidatorFactoryBean(List<ConstraintValidator<?, ?>> customConstraintValidators) {
this.customConstraintValidators = customConstraintValidators;
setProviderClass(HibernateValidator.class);
afterPropertiesSet();
}
#Override
protected void postProcessConfiguration(Configuration<?> configuration) {
super.postProcessConfiguration(configuration);
ConstraintValidatorFactory defaultConstraintValidatorFactory =
configuration.getDefaultConstraintValidatorFactory();
configuration.constraintValidatorFactory(
new ConstraintValidatorFactory() {
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
for (ConstraintValidator<?, ?> constraintValidator : customConstraintValidators) {
if (key.equals(constraintValidator.getClass())) //noinspection unchecked
return (T) constraintValidator;
}
return defaultConstraintValidatorFactory.getInstance(key);
}
#Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
defaultConstraintValidatorFactory
.releaseInstance(instance);
}
}
);
}
}
then in your test class you'd just do something like this:
class MyTestSuite {
private final PersonRepository mockPersonRepository = Mockito.mock(PersonRepository.class);
private final List<ConstraintValidator<?,?>> customConstraintValidators =
Collections.singletonList(new PersonNameMustBeUniqueValidator(mockPersonRepository));
private final ValidatorFactory customValidatorFactory =
new CustomLocalValidatorFactoryBean(customConstraintValidators);
private final Validator validator = customValidatorFactory.getValidator();
#Test
void myTestCase() {
// mock the dependency: Mockito.when(mockPersonRepository...)
Person p = new Person();
//setters omitted
Set<ConstraintViolation<?>> violations = validator.validate(p);
//assertions on the set of constraint violations
}
}
Hope that helps. You can check out this post of mine for more details: https://codemadeclear.com/index.php/2021/01/26/how-to-mock-dependencies-when-unit-testing-custom-validators/
I've implemented by overriding default Hibernate ConstraintValidatorFactory in my UnitTests
LocalValidatorFactoryBean localValidatorFactory = new LocalValidatorFactoryBean();
localValidatorFactory.setProviderClass(HibernateValidator.class);
localValidatorFactory.setConstraintValidatorFactory(new ConstraintValidatorFactoryImpl() {
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> arg0) {
T ret = super.getInstance(arg0);
if (ret instanceof UniqueEmailValidator) {
((UniqueEmailValidator) ret).setUserService(userService);
}
return ret;
}
});
localValidatorFactory.afterPropertiesSet();
Spring Boot 2 allows to inject Bean in custom Validator without any fuss.The Spring framework automatically detects all classes which implement the ConstraintValidator interface, instantiate them, and wire all dependencies.
I had Similar problem , this is how i have implemented.
Step 1 Interface
#Documented
#Constraint(validatedBy = UniqueFieldValidator.class)
#Target({ ElementType.METHOD,ElementType.ANNOTATION_TYPE,ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface UniqueField {
String message() default "Duplicate Name";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Step 2 Validator
public class UniqueFieldValidator implements ConstraintValidator<UniqueField, Person> {
#Autowired
PersionList personRepository;
private static final Logger log = LoggerFactory.getLogger(PersonRepository.class);
#Override
public boolean isValid(Person object, ConstraintValidatorContext context) {
log.info("Validating Person for Duplicate {}",object);
return personRepository.isPresent(object);
}
}
Usage
#Component
#Validated
public class PersonService {
#Autowired
PersionList personRepository;
public void addPerson(#UniqueField Person person) {
personRepository.add(person);
}
}

How do I autowire dependencies into Spring #Configuration instances?

I need to inject an object into my No XML Spring #Configuration object as follows:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "web.client")
public class WebApplicationConfiguration extends WebMvcConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(WebApplicationConfiguration.class);
#Inject
private MonitoringExceptionResolver resolver; // always null
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
log.debug("configuring exception resolvers");
super.configureHandlerExceptionResolvers(exceptionResolvers);
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
exceptionResolvers.add(new AnnotationMethodHandlerExceptionResolver());
exceptionResolvers.add(new ResponseStatusExceptionResolver());
exceptionResolvers.add(resolver); // passing null ref here
}
}
Where MonitoringExceptionResolver is defined as follows:
#Service
public class MonitoringExceptionResolver implements HandlerExceptionResolver {
private final Counters counters;
#Inject
public MonitoringExceptionResolver(Counters counters) {
super();
this.counters = counters;
}
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Counter counter = counters.getCounterFor(ex.getClass());
if(counter != null) {
counter.increment();
}
return null;
}
}
However, I get NPE later in the execution chain because the "resolver" field above is null, even if I use #Autowired.
Other classes are being successfully wired in elsewhere using component scanning. Why is it always null in the above? Am I doing something wrong?
#Inject and #Autowired should work very similar in Spring.
Make sure that *BeanPostProcessor in use is aware of MonitoringExceptionResolver: mark it as #Component and make is subject of some #ComponentScan or make a #Bean factory method is some #Configuration class in use.

Resources