Autowired gives Null value in Custom Constraint validator - java

I am totally new to Spring and I have looked in to a few answers on SO for the asked problem. Here are the links:
Spring 3.1 Autowiring does not work inside custom constraint validator
Autowiring a service into a validator
Autowired Repository is Null in Custom Constraint Validator
I have a Spring project in which I want to use Hibernate Validator for an object validation. Based on what I read online and a few forums I tried to inject validator as follows:
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean().getValidator();
}
But wherever I was using
#Autowired
Validator validator;
It was taking Spring's validator implementation instead of the Hibernate's validator. I couldn't figure out how to exactly inject Hibernate validator and simply Autowire it across other classes so I used a cheap trick, now my Java Config looks like this
#Bean
public Validator validator() {
// ValidatorImpl is Hibernate's implementation of the Validator
return new ValidatorImpl();
}
(I would really appreciate if someone can actually point me into the right direction on how to avoid getting Hibernate Validator in this Hacky way)
But lets come to the main issue here:
Here is custom validation definition
#Target( { METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER } )
#Retention(RUNTIME)
#Constraint(validatedBy = EmployeeValidator.class)
#Documented
public #interface EmployeeValidation {
String message() default "{constraints.employeeConstraints}";
public abstract Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
My Custom Validator
public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {
#Autowired
private EmployeeService employeeService;
#Override
public void initialize(EmployeeValidation constraintAnnotation) {
//do Something
}
#Override
public boolean isValid(String type, ConstraintValidatorContext context) {
return false;
}
}
In the above Custom Constraint Validator I get the employeeService null. I know that any implementations of ConstraintValidator are not instantiated when Spring is starting up but I thought adding the ValidatorImpl() will actually fix that. But it didn't.
Now I am stuck with a really hacky workaround and I do not want to continue with a code like that. Can someone please help me with my situation.
P.S. These are my imports in the Java Config file:
import org.hibernate.validator.HibernateValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

I hope the solution will help someone:
#Bean
public Validator validator () {
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure().constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
Initializing the validator with SpringConstraintValidatorFactory so that injection works and providing the validator implementation to be Hibernate.class works in the following manner:
Your objects will be validated by the library of your choice
Your custom validators will be able to use Spring's functionality while having validation to be executed by Hibernate.
How it works:
Hibernate's ConstraintValidatorFactory does not initialize any ConstraintValidators unless they are called but SpringConstraintValidatorFactory does by giving AutowireCapableBeanFactory to it.
EDIT
As mentioned in one of the comments by #shabyasaschi To inject autowireCapableBeanFactory you can change the method signature as:
Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {
or add getter and setter for it in the config file as follows:
public AutowireCapableBeanFactory getAutowireCapableBeanFactory() {
return autowireCapableBeanFactory;
}
public void setAutowireCapableBeanFactory(AutowireCapableBeanFactory autowireCapableBeanFactory) {
this.autowireCapableBeanFactory = autowireCapableBeanFactory;
}

You can fix this with two aproaches:
Try to inject Services on your validator using Spring.
Initialize it manually overriding Validator's initialize method.
I had the same problem time ago and finally i decided to use second option avoiding tons of problems.
As you point you must define one initialize method on your validator and there you can use a ServiceUtils to get the service bean you need:
#Autowired
private EmployeeService employeeService;
#Override
public void initialize(EmployeeValidation constraintAnnotation) {
//Use an utility service to get Spring beans
employeeService = ServiceUtils.getEmployeeService();
}
And ServiceUtils is a normal Spring bean with a static reference to itself used in the static methods.
#Component
public class ServiceUtils {
private static ServiceUtils instance;
#Autowired
private EmployeeService employeeService;
/* Post constructor */
#PostConstruct
public void fillInstance() {
instance = this;
}
/*static methods */
public static EmployeeService getEmployeeService) {
return instance.employeeService;
}
}
So you are using Spring to inject the services you need but not in the usual way.
Hope this helps.

In your bean definition
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean().getValidator();
}
What's the type of Validator in the method definition? You should make sure it returns javax.validation.Validator, not Validator from Spring.
Letting Spring bootstrap the validator will it also cause to pass a SpringConstraintValidatorFactory to Hibernate Validator which will enable dependency injection within constraint validators.

There is nothing wrong with your code It depends how are you creating your ValidatorFactory.
Create a bean and let Spring handle it.
#Bean
public ValidatorFactory validatorFactory(){
return Validation.buildDefaultValidatorFactory();
}

Think about it. There should have been no issue with using the #Autowired inside a Constraint validator class. This means that something is wrong.
This issue has been reported on various platform and I have not seen a good solution. I have seen some workaround though.
Here is what I found.
You may notice that the validation is happening twice. the first time it should work but the second time you got a null related error message. The problem should be that the entity or the class that you is being validated is being used twice in your controller. For example, you may want validate the entity class and try to save it at the same time in the same method in the controller method. when you try to save the entity, it will try to validate the object again and this time the #Autowired object will be null.
Here is what you can do for this scenario
You can use dto to carry the validation annotation and copy the property of the dto class to your entity class before you save it into the database. your scenario may be different but the solution approach should be the same.
Below is an illustration of code that works
public ResponseEntity<InstitutionModel> create(#Valid #RequestBody InstitutionDto institutiondto) {
Institution institution = new Institution();
BeanUtils.copyProperties(institutiondto, institution);
return Optional.of(this.institutionService.save(institution)).map(institutionModelAssembler::toModel)
.map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}

private XXXService xxxService = SpringContextHolder.getBean(XXXService.class);

Thats worked for me. For guys who search at now
public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {
private EmployeeService employeeService;
public EmployeeValidator(EmployeeService employeeService){
this.employeeService = employeeService;
}
...
}

Related

Autowire a Spring bean in a Singleton class

I am trying to autowire a bean inside of a Singleton class, I know that it always a best idea to avoid manual autowiring, but this class is being used in so many places so I do not want to change the caller of this class.
Runner.java
#Component
public class RunnerClass {
#Autowired
public ConfigService configService;
}
ConfigService.java
#Service
public class ConfigService {
private ConfigServiceDAO = ConfigServiceDAO.getInstance();
}
ConfigServiceDAO.java
public class ConfigServiceDAO {
//Bean I want to autowire here....
#Autowired
ConfigServiceDAOBuilder DAOBuilder
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE = new ConfigServiceDAO();
private SingletonHolder() {}
}
}
DAOBuilder inside ConfigServiceDAO is always null, which makes sense because my understanding is when the class is instantiated manually, spring injection doesn't happen.
What could be the solution here if I want to keep ConfigServiceDAO as non spring component?
====EDIT====
I know it is possible to make ConfigServiceDAO as a spring component and autowire all dependencies.
But a lot of classes from different packages already call
ConfigServiceDAO.getInstance().someMethod()
So I guess the the right question is, what would be the best way to autowire a spring component to the class that is instantiated manually.
I don't know your use case but you cannot use #Autowired annotation outside a Spring bean.
However if you really need to access a Spring bean from a non Spring piece of code you can do it like below. However this is a very non Spring way of designing your dependencies.
import org.springframework.context.ApplicationContext;
public enum ApplicationContextHolder {
INSTANCE;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
Then you have a configuration class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
#Configuration
public class SomeConfig {
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void init() {
ApplicationContextHolder.INSTANCE.setApplicationContext(applicationContext);
}
}
Then in your DAO class you get a reference to the builder bean you are interested. Something like this:
public class ConfigServiceDAO {
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE =
ApplicationContextHolder.INSTANCE.getApplicationContext().getBean(ConfigServiceDAOBuilder.class).buildConfigServiceDAO()
private SingletonHolder() {}
}
}
Again this is a very non Spring way of doing things.
Spring processed #Autowired only in beans that it manages by itself.
So you have two choices:
Get Rid Of singleton - if you're using spring, its already a singleton in the application context. This is by far the best approach in general (assuming other parts of application that call your singleton are also spring driven). I don't think you should fear to change ConfigServiceDAO.getInstance.method() - refactoring tools in IDE will do the job.
If you can't do 1, Don't use autowired annotation in the singleton - its useless anyway, instead, when you have an application context configured (in listener that spring emits when the application started for example), get the access to the ConfigServiceDAOBuilder bean by calling appCtx.getBean(ConfigServiceDAOBuilder.class) and "inject it" manually by reflection, this is what Spring does with spring managed beans anyway:
#EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
ConfigServiceDAOBuilder builder =
event.getApplicationContext().getBean(ConfigServiceDAOBuilder.class);
ConfigServiceDao dao = ConfigServiceDAO.getInstance();
dao.setDaoBuilder(builder); // or alternatively by reflection
}
As a side note, consider using method setDaoBuilder to be a package private to protect the singleton from some accidentally calling a setter
As far as I understand what you want: Create by Spring ConfigServiceDAOBuilder. After that inject it into non-managed object of class ConfigServiceDAO. You can do it after the Spring application context is instantiated. For example with CommanLineRunner:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
#Autowired
ConfigServiceDAOBuilder DAOBuilder
#Override
public void run(String...args) throws Exception {
ConfigServiceDAO.getInstance().init(DAOBuilder);
}
}
In ConfigServiceDAO has to be method init that helps to register all needed beans.
I'm confused after reading your comments, hence let me put in this way. What you are referring to manual autowiring is the Spring dependency injection way.
Whenever you are using any of the Spring Stereotype annotations with default scope instance is always Singleton.
Your ConfigService class has the problem.
Your are mixing up things, you should create a separate config class with #configuration and create Bean for the class ConfigServiceDAO, something like below
#Configuration
Class Config{
#Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}
then autowire the ConfigServiceDAO in the ConfigService class. With this Spring will resolve all of the dependency in correct order and DAOBuilder shouldn't be null.

javax.validator with spring component

I use javax.validation with Spring. In my test (groovy) I explicitly create validator.
import javax.validation.Validation
import javax.validation.Validator
import javax.validation.ValidatorFactory
ValidatorFactory factory = Validation.buildDefaultValidatorFactory()
Validator validator = factory.getValidator()
when:
Set<ConstraintViolation<User>> constraints = validator.validate(entity)
My validator in java
public class EntityDynamicValidator implements ConstraintValidator<SomeConstraint, Entity> {
private GroupService groupService;
// This constructor is required, see the link bellow.
public UserDynamicEnumValidator() {
}
public UserDynamicEnumValidator(final GroupService groupService) {
this.groupService = groupService;
}
#Override
public boolean isValid(final Entity entity, final ConstraintValidatorContext context) {
Something something = groupService.findByValue(entity.getValue());
// Validate all this stuff
}
}
I need to pass a Spring service to the validator.
I have a default constructor because there is an issue
Spring unit test issue with Validator
I also tried to solve this, however the tests are fully low level unit tests on my side and I wanted to avoid too much of a context. I couldn't even use your approach. The solution my side was to add a custom ConstraintValidatorBean into the mockmvc. That custom implementation could get a list of validator class Class objects and so if the factory tried to create a validator, I returned a Mockito.mock version of it instead of the real one and I could also look that up for expectation settings.
Feel free to share your knowledge. This is how I solved this task.
I created a Spring component and it has a static field (GroupService which is initialized in constructor).
#Component // Spring component.
class ServiceHolderComponent {
private static GroupService GROUP_SERVICE;
#Autowired
public ServiceHolderComponent(final GroupService groupService) {
GROUP_SERVICE = Validate.notNull(groupService); //apache lib
}
public static GroupService getGroupService() {
return GROUP_SERVICE;
}
}
And now validator with default constructor.
public class EntityDynamicValidator implements ConstraintValidator<SomeConstraint, Entity> {
private GroupService groupService;
public UserDynamicEnumValidator() {
this(ServiceHolderComponent.getGroupService());
}
public UserDynamicEnumValidator(final GroupService groupService) {
this.groupService = groupService;
}
#Override
public boolean isValid(final Entity entity, final ConstraintValidatorContext context) {
Something something = groupService.findByValue(entity.getValue());
// Validate all this stuff
}
}
Apache library https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/Validate.html
import org.apache.commons.lang3.Validate;
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>

Spring MVC & custom JsonSerializer

I'm using Spring 5.0 with MVC and have a custom (de)serializer for an entity, e.g.
#JsonDeSerialize(using = RoleDeserializer.class)
public class Role implements Serializable {
....
and for deserializing I have (StdDesializer won't change anything)
public class RoleDeserializer extends JsonDeserializer<Role> {
EntityManager em;
public RoleDeserializer(EntityManager em) {
this.em = em;
}
....
which gives me always an Exception
MappingJackson2HttpMessageConverter:205 Failed to evaluate Jackson deserialization for type [[simple type, class test.Role]]: c
om.fasterxml.jackson.databind.JsonMappingException: Class test.RoleDeserializer has no default (no arg) constructor
but somehow I need to have that constructor since if I do it like
public class RoleDeserializer extends JsonDeserializer<Role> {
#PersitenceContext
EntityManager em;
....
The automatic annotation on em with #PersitenceContext does not work because it is not injected with Spring, i.e. not initialized.
Remark: Following the suggestions I could not resolve the issue. The reason of the behavior is explained in link - but this does not get rid of the Exceptions :-/
Help is greatly appreciated.
After the hint to my question I figured out how to resolve this issue in Spring 5.0 (most likely as well in 4.1.1) - without XML:
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
final HashMap<Class<?>, JsonDeserializer<?>> map = new HashMap<>();
map.put(Role.class, new RoleDeserializer());
// more classes could be easily attached the same way...
builder.deserializersByType(map);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
after this the annotations #Autowired or #PersistenceContext work as expected in the classes :-)
Thx for the support!

NullPointerException when autowiring into non-spring managed class (POJO) - Spring Boot

I'm relatively new to Spring Boot and dependency injection overall, so please forgive any noob things going on here. I'm building an API and am having trouble when injecting dependencies into a POJO resource (DTO).
When I call the method in the POJO this.numComments = commentSvc.getAllForPhoto(this.getId()); I am getting a NullPointerException. However, when I do this from another spring-managed bean and pass the values into the constructor, it works fine.
After reading around, it looks like I need to do something with aspectJ and Load Time Weaving, but I'm not sure what that would look like in my code.
In essence, my approach looks something like this:
PhotoResource.java (POJO)
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
private Long id;
private Integer numComments;
PhotoResource(PhotoEntity entity){
super(entity);
this.setId(entity.getId);
this.numComments = commentSvc.getAllForPhoto(this.getId());
}
}
CommentService.java
#Service
public class CommentService{
public List<CommentResource> getAllForPhoto(Long photoId) {
// code to get all comments for photo
}
}
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Spring won't inject the dependency unless you ask the Spring container to manage the bean. In order for commentSvc to be injected into PhotoResource class, you need to annotate it with #Component or #Bean or #Service, e.g.:
#Component
public class PhotoResource extends BaseRepresentable {
#Autowired
CommentService commentSvc;
}
And make sure the package of this class is included into #ComponentScan packages.
Also, the following won't compile:
#Service
public class CommentService(){
You don't need paranthesis to declare a class, it should be:
#Service
public class CommentService{

Spring Dependency injection for interfaces

Well I've been watching some tutorials about Spring dependency injection as well as MVC, but I still seem to not understand how we can instantiate classes specifically?
I mean if for instance I have a variable
#Autowired
ClassA someObject;
How can I make spring create someObject as an Instance of ClassB which would extend ClassA? like someObject = new ClassB();
I don't really understand how it works in spring, does the ContextLoaderListener do it automatically or do we have to create some kind of configuration class where we specify exactly what spring should instantiate those classes to? (In this case I haven't seen that anywhere in the tutorials) If yes, then how do we specify and how does it look like? And how do we configure it to work in web.xml, etc?
You can do it like this:
Interface:
package org.better.place
public interface SuperDuperInterface{
public void saveWorld();
}
Implementation:
package org.better.place
import org.springframework.stereotype
#Component
public class SuperDuperClass implements SuperDuperInterface{
public void saveWorld(){
System.out.println("Done");
}
}
Client:
package org.better.place
import org.springframework.beans.factory.annotation.Autowire;
public class SuperDuperService{
#Autowire
private SuperDuperInterface superDuper;
public void doIt(){
superDuper.saveWorld();
}
}
Now you have your interface defined, written an implementation and marked it as a component - docs here. Now only thing left is to tell spring where to find components so they can be used for autowiring.
<beans ...>
<context:component-scan base-package="org.better.place"/>
</beans>
You have to specify the type of the class that you want to create object of in your applicationContext.xml file or you can directly annotate that class with any of #Component , #Service or #Repository if you are using latest version of Spring. In web.xml, you have to specify path of xml files as a context-param to servlet, if you are using xml-based configuration.
Yes, you have to provide a context.xml file in which you specify the instances. Give it to the ApplicationContext and it will autowire all fields for you.
http://alvinalexander.com/blog/post/java/load-spring-application-context-file-java-swing-application
Best Practices
#RestController
#RequestMapping("/order")
public class OrderController {
private final IOrderProducer _IOrderProducer;
public OrderController(IOrderProducer iorderProducer) {
this._IOrderProducer = iorderProducer;
}
#GetMapping("/OrderService")
void get() {
_IOrderProducer.CreateOrderProducer("This is a Producer");
}
}
Interface
#Service
public interface IOrderProducer {
void CreateOrderProducer(String message);
}
Implementation
public class OrderProducer implements IOrderProducer{
private KafkaTemplate<String, String> _template;
public OrderProducer(KafkaTemplate<String, String> template) {
this._template = template;
}
public void CreateOrderProducer(String message){
this._template.send("Topic1", message);
}
}
You need to include Project Lombok dependency in spring boot
Gradle implementation 'org.projectlombok:lombok'

Categories

Resources