Spring4 How to use Service in Validator - java

public class BoardValidator implements Validator {
#Autowired
BoardService boardService;
#Override
public boolean supports(Class<?> clazz) {
return BoardVO.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
boardService.getAllList();
//TODO add
}
}
create Validator like this
Can use boardService in controller package
but in validator boardService is Null
how to use Service #Autowired in Validator class?

Related

Custom AsyncUncaughtExceptionHandler

Using the following configuration for #Async methods :
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
//Just to experiment
return new SimpleAsyncTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
Is there a way to "get" the ability to autowire (or similar) Services ?
I'd like to use such Services to record errors in database and use common services.
Non working sample :
#Component //seems pointless
public class CustomAsyncExceptionHandler extends ServiceCommons implements AsyncUncaughtExceptionHandler {
protected Logger LOG = LoggerFactory.getLogger(this.getClass());
#Autowired
private MyService myService; //always null
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
//null pointer !
myService.doSomething(throwable);
}
}
When using not in #Async methods, #ControllerAdvice global exception handler allows #Autowired fields. Why not in this case ? Is this because of async thread management ?
I just faced this problem and solved this way:
#Configuration
#EnableAsync
public class MyAsyncConfigurer implements AsyncConfigurer {
private CustomAsyncExceptionHandler customAsyncExceptionHandler;
//...
//other code here
//...
#Autowired
public void setCustomAsyncExceptionHandler(CustomAsyncExceptionHandler customAsyncExceptionHandler) {
this.customAsyncExceptionHandler = customAsyncExceptionHandler;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return this.customAsyncExceptionHandler;
}
}
Custom async exception handler annotated with #Component:
#Component
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private MyMailService myMailService;
#Autowired
public void setMyMailService(MyMailService myMailService) {
this.myMailService= myMailService;
}
#Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
myMailService.sendMailToAdmin(throwable, method.getName());
}
}
IoC injects both, myMailService and customAsyncExceptionHandler, correctly whith no errors.
I don't think my solution is the most elegant, but tell me what you think. the idea is to bypass the automatic injection mechanism by using ApplicationContextAware interface. My first attempt was to make my AsyncUncaughtExceptionHandler implementing class to also implement ACAware. But that didn't work. Somehow this class, even annotated as Component or Service seems to live a bit outside the Spring environment. So I did this:
#Configuration
#EnableAsync
public class DemoAsyncConfigurer implements AsyncConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
And in the same class:
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
DemoAsyncExceptionHandler demoHandler = new DemoAsyncExceptionHandler(); // you can't add the parameter in this constructor, for some reason...
demoHandler.setApplicationContext(applicationContext);
return demoHandler;
}
/**
*
* {#inheritDoc}
*/
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
My DemoAsyncExceptionHandler has the following:
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
UserService userService = this.applicationContext.getBean("userService", UserService.class);
// call userService method
That worked! Hope I have helped

How to disable Spring autowiring for a certain bean?

There are some class in jar (external library), that uses Spring internally.
So library class has structure like a:
#Component
public class TestBean {
#Autowired
private TestDependency dependency;
...
}
And library provides API for constructing objects:
public class Library {
public static TestBean createBean() {
ApplicationContext context = new AnnotationConfigApplicationContext(springConfigs);
return context.getBean(TestBean);
}
}
In my application, I have config:
#Configuration
public class TestConfig {
#Bean
public TestBean bean() {
return Library.createBean();
}
}
It's throw en exception: Field dependency in TestBean required a bean of type TestDependency that could not be found..
But Spring should not trying to inject something, because bean is already configured.
Can i disable Spring autowiring for a certain bean?
Based on #Juan's answer, created a helper to wrap a bean not to be autowired:
public static <T> FactoryBean<T> preventAutowire(T bean) {
return new FactoryBean<T>() {
public T getObject() throws Exception {
return bean;
}
public Class<?> getObjectType() {
return bean.getClass();
}
public boolean isSingleton() {
return true;
}
};
}
...
#Bean
static FactoryBean<MyBean> myBean() {
return preventAutowire(new MyBean());
}
This worked for me:
import org.springframework.beans.factory.FactoryBean;
...
#Configuration
public class TestConfig {
#Bean
public FactoryBean<TestBean> bean() {
TestBean bean = Library.createBean();
return new FactoryBean<TestBean>()
{
#Override
public TestBean getObject() throws Exception
{
return bean;
}
#Override
public Class<?> getObjectType()
{
return TestBean.class;
}
#Override
public boolean isSingleton()
{
return true;
}
};
}
}
It seems like it's impossible to disable autowiring for a specific bean.
So there is some workaround.
We can make wrapper for a target bean and use it instead of original bean:
public class TestBeanWrapper {
private final TestBean bean;
public TestBeanWrapper(TestBean bean) {
this.bean = bean;
}
public TestBean bean() {
return bean;
}
}
#Configuration
public class TestConfig {
#Bean
public TestBeanWrapper bean() {
return new TestBeanWrapper(Library.createBean());
}
}
#RestController
public class TestController {
#Autowired
private TestBeanWrapper bean;
...
}
Not exactly but you can add required=false (#Autowired(required=false)) in your autowired annotation. But be careful that might get you NullPointer exception

Spring REST validation on custom annotation

I'm trying to add some extra validation logic on my REST beans using annotations. This is just an example, but the point is that the annotation is to be used on multiple REST resource objects / DTO's.
I was hoping for a solution like this:
public class Entity {
#NotNull // JSR-303
private String name;
#Phone // Custom phonenumber that has to exist in a database
private String phoneNumber;
}
#Component
public class PhoneNumberValidator implements Validator { // Spring Validator
#Autowired
private PhoneRepository repository;
public boolean supports(Class<?> clazz) {
return true;
}
public void validate(Object target, Errors errors) {
Phone annotation = // find fields with annotations by iterating over target.getClass().getFields().getAnnotation
Object fieldValue = // how do i do this? I can easily get the annotation, but now I wish to do a call to repository checking if the field value exists.
}
}
Did you try JSR 303 bean validator implementations like hibernate validator
e.g. is available here http://www.codejava.net/frameworks/spring/spring-mvc-form-validation-example-with-bean-validation-api
Maven Module A:
public interface RestValidator<A extends Annotation, T> extends ConstraintValidator<A, T>
public interface PhoneValidator extends RestValidator<PhoneNumber, String>
#Target(FIELD)
#Retention(RUNTIME)
#Constraint(validatedBy = PhoneValidator.class) // This usually doesnt work since its a interface
public #interface PhoneNumber {
// JSR-303 required fields (payload, message, group)
}
public class Person {
#PhoneNumber
private String phoneNumber;
}
Maven Module B:
#Bean
LocalValidatorFactoryBean configurationPropertiesValidator(ApplicationContext context, AutowireCapableBeanFactory factory) {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setConstraintValidatorFactory(factory(context, factory));
return factoryBean;
}
private ConstraintValidatorFactory factory(final ApplicationContext context, final AutowireCapableBeanFactory factory) {
return new ConstraintValidatorFactory() {
#Override
public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
if (RestValidator.class.isAssignableFrom(key)) {
return context.getBean(key);
} else {
return factory.createBean(key);
}
}
#Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
if (!(instance instanceof RestValidator<?, ?>)) {
factory.destroyBean(instance);
}
}
};
}
#Bean
WebMvcConfigurerAdapter webMvcConfigurerAdapter(final LocalValidatorFactoryBean validatorFactoryBean) {
return new WebMvcConfigurerAdapter() { // Adds the validator to MVC
#Override
public Validator getValidator() {
return validatorFactoryBean;
}
};
}
Then I have a #Component implementation of PhoneValidator that has a Scope = Prototype.
I hate this solution, and I think Spring SHOULD look up on Interface implementations by default, but I'm sure some people that are a lot smarter than me made the decision not to.

Spring MVC - lookup validators automatically

Suppose I have a sample entity class like this:
public class Address {
...
}
and a corresponding validator:
#Component
public AddressValidator implements Validator {
#Override
public boolean supports(Class<?> entityClass) {
return entityClass.equals(Address.class);
}
#Override
public void validate(Object obj, Errors errors) {
...
}
}
When I use a controller like the following, everything works:
#RestController
#RequestMapping("/addresses")
public class AddressController {
#Autowired
private AddressValidator validator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
#RequestMapping(method=POST)
public Long addNewAddress(#Valid #RequestBody Address address) {
...
}
}
However, if I omit the validator registering part (i.e. the following), validation is not performed.
#Autowired
private AddressValidator validator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
Having to register validators manually seems pointless. Can I instruct Spring to look up validators automatically (similar to how controllers are looked up)?
It's a Spring Boot based application.
You can use my example from gist or below. The idea is to have a main CompositeValidator that will be a holder of all your Validator or SmartValidator instances.
It supports hints and can be also integrate with Hibernate Annotation Validator (LocalValidatorFactoryBean). And also it's possible to have more that one validator per specific Model.
CompositeValidator.java
#Component
public class CompositeValidator implements SmartValidator {
#Autowired
private List<Validator> validators = Collections.emptyList();
#PostConstruct
public void init() {
Collections.sort(validators, AnnotationAwareOrderComparator.INSTANCE);
}
#Override
public boolean supports(Class<?> clazz) {
for (Validator validator : validators) {
if (validator.supports(clazz)) {
return true;
}
}
return false;
}
#Override
public void validate(Object target, Errors errors) {
validate(target, errors, javax.validation.groups.Default.class);
}
#Override
public void validate(Object target, Errors errors, Object... validationHints) {
Class<?> clazz = target.getClass();
for (Validator validator : validators) {
if (validator.supports(clazz)) {
if (validator instanceof SmartValidator) {
((SmartValidator) validator).validate(target, errors, validationHints);
} else {
validator.validate(target, errors);
}
}
}
}
}
SomeController.java
#Controller
#RequestMapping("/my/resources")
public class SomeController {
#RequestMapping(method = RequestMethod.POST)
public Object save(
#Validated(javax.validation.groups.Default.class) // this interface descriptor (class) is used by default
#RequestBody MyResource myResource
) { return null; }
}
Java Config
#Configuration
public class WebConfig {
/** used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy */
// #Bean
// public Validator jsr303Validator() {
// LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
// // validator.setValidationMessageSource(...);
// return validator;
// }
#Bean
public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
return new WebMvcConfigurerAdapter() {
#Autowired
private CompositeValidator validator;
#Override
public Validator getValidator() {
return validator;
}
}
}
Or XML Config
<!-- used for Annotation based validation, it can be created by spring automaticaly and you don't do it manualy -->
<!--<bean id="jsr303Validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">-->
<!-- <property name="validationMessageSource" ref="messageSource"/>-->
<!--</bean>-->
<mvc:annotation-driven validator="compositeValidator">
//...
</mvc:annotation-driven>
You can configure global Validator.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#validation-mvc
If you are using Java based spring configuration with WebMvcConfigurationSupport, you can override getValidator()
/**
* Override this method to provide a custom {#link Validator}.
*/
protected Validator getValidator() {
return null;
}
You may call setValidator(Validator) on the global WebBindingInitializer. This allows you to configure a Validator instance across all #Controller classes. This can be achieved easily by using the Spring MVC namespace:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven validator="globalValidator"/>
</beans>
I have not found a build in Spring solution for this, but here is what I do.
I declare my validator beans in the spring java configuration like so:
#Bean
public Validator studentValidator(){
return new StudentValidator();
}
#Bean
public Validator carValidator(){
return new CarValidator();
}
Then I have all controllers extends a base controller like so:
public abstract class BaseController <T> {
public BaseController(List<Validator> validators) {
super();
this.validators = validators;
}
//Register all validators
#InitBinder
protected void initBinder(WebDataBinder binder) {
validators.stream().forEach(v->binder.addValidators(v));
}
}
The concrete class of this controller gets the List injected via dependency injection, like so:
#Controller
public class StudentController extends BaseController<Student>{
#Inject
public StudentController(List<Validator> validators) {
super(validators);
}
}
The base Controller uses the #InitBinder call-back method to register all Validators.
I am surprised that spring doesn't automatically register all objects in class path that implement the Validator interface.

Spring BeanFactory product autowiring

I want to have something like this:
#Servise
public class BeanAFactory implements FactoryBean<BeanA>{
#Autowired <…>;
#Override
public BeanA getObject() throws Exception {
return new BeanAImpl();
}
#Override
public Class<BeanA> getObjectType() {
return BeanA.class;
}
#Override
public boolean isSingleton() {
return false;
}
private class BeanAImpl implements BeanA {
<…>
}
}
and to have autowiring of result of BeanAFactory.getObject() on BeanA: ctx.getBean(BeanA.class) should return result of BeanAFactory.getObject(). Is it possible?
Yes, Declare object of BeanA in BeanAFactory class and initialize it in using default constructor so you will get that object while autowiring BeanAFactory class.

Categories

Resources