title: Spring MVC custom validation annotation with service @Autowired
body: <p>I use Spring MVC with Hibernate to develop a Web Application. At first I used only standard JSR303 annotation. Then I decided to use some custom annotation to verify the uniqueness of the username and email of a new User, who would want to create an account.</p>
  
  <p>So I created two annotation like this one (one fr the email and the other for the username) :</p>
  
  <pre><code>@Documented
  @Constraint(validatedBy = UniqueUsernameConstraintValidator.class)
  @Target( { ElementType.METHOD, ElementType.FIELD })
  @Retention(RetentionPolicy.RUNTIME)
  public @interface UniqueUsername {
  
  String message() default "{UniqueUsername}";
  
  Class&lt;?&gt;[] groups() default {};
  
  Class&lt;? extends Payload&gt;[] payload() default {};
  
  }
  </code></pre>
  
  <p>And two Validator like this one :</p>
  
  <pre><code>public class UniqueUsernameConstraintValidator implements ConstraintValidator&lt;UniqueUsername, String&gt; {
  
  @SuppressWarnings("unused")
  private static final Logger logger = LoggerFactory.getLogger(UniqueUsernameConstraintValidator.class);
  
  @Autowired
  private UserService userService;
  
  @Override
  public void initialize(UniqueUsername uu) { }
  
  @Override
  public boolean isValid(String username, ConstraintValidatorContext cxt) {
      if(username == null) {
          return false;
      }
      return userService.findByUsername(username) == null;
  }
  </code></pre>
  
  <p>}</p>
  
  <p>I posted only one because those two are really similar.</p>
  
  <p>The annotation @UniqueUsername and @UniqueEmail work when the email or the username is not unique and send the right message of validation error. But when I give a username and an email not already used, then I have an null pointer error on the userService.</p>
  
  <p>I don't understand why I would have some null problem with the @Autowired annotation. But what is really intriguing me is why the IsValid() method (which give me the null exception) would be called a second time , or at least for m it seems that it' what happen, because the validation step seem to work.</p>
  
  <p>Here is the error :</p>
  
  <pre><code>java.lang.NullPointerException
  com.sam.website.custom.validators.UniqueUsernameConstraintValidator.isValid(UniqueUsernameConstraintValidator.java:30)
  com.sam.website.custom.validators.UniqueUsernameConstraintValidator.isValid(UniqueUsernameConstraintValidator.java:1)
  org.hibernate.validator.engine.ConstraintTree.validateSingleConstraint(ConstraintTree.java:153)
  org.hibernate.validator.engine.ConstraintTree.validateConstraints(ConstraintTree.java:140)
  org.hibernate.validator.metadata.MetaConstraint.validateConstraint(MetaConstraint.java:121)
  org.hibernate.validator.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:327)
  org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForRedefinedDefaultGroup(ValidatorImpl.java:273)
  org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:256)
  org.hibernate.validator.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:210)
  org.hibernate.validator.engine.ValidatorImpl.validate(ValidatorImpl.java:119)
  org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:136)
  org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:94)
  org.hibernate.action.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:160)
  org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:65)
  org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
  org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:320)
  org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203)
  org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129)
  org.hibernate.ejb.event.EJB3MergeEventListener.saveWithGeneratedId(EJB3MergeEventListener.java:62)
  org.hibernate.event.def.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:415)
  org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:341)
  org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:303)
  org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:258)
  org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
  org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867)
  org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851)
  org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855)
  org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:686)
  sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  java.lang.reflect.Method.invoke(Method.java:601)
  org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
  $Proxy39.merge(Unknown Source)
  org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:360)
  sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  java.lang.reflect.Method.invoke(Method.java:601)
  org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:333)
  org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:318)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
  $Proxy41.save(Unknown Source)
  com.sam.website.services.jpa.UserServiceJpa.save(UserServiceJpa.java:53)
  sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  java.lang.reflect.Method.invoke(Method.java:601)
  org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
  org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
  org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
  org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
  org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
  $Proxy46.save(Unknown Source)
  com.sam.website.controllers.SecurityController.register(SecurityController.java:63)
  sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  java.lang.reflect.Method.invoke(Method.java:601)
  org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
  org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
  org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
  org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
  org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
  org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
  org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
  org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
  javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
  javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
  org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
  org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:183)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
  org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
  org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
  org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
  org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
  org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
  org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
  </code></pre>
  
  <p>Here is my usage of the custom annotation</p>
  
  <pre><code>@UniqueUsername
  @NotEmpty
  @Size(min=3)
  @Column(name="username",unique=true)
  public String getUsername() {
      return username;
  }
  </code></pre>
  
  <p>And here my configuration</p>
  
  <pre><code>    &lt;!-- Validator settings --&gt;
  &lt;annotation-driven validator="validator"/&gt;
  
  &lt;beans:bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"&gt;
      &lt;beans:property name="validationMessageSource" ref="messageSource"/&gt;
  &lt;/beans:bean&gt;
  </code></pre>