Using both JSR-303 and Traditional Bean Validation? - spring

Is it possible to use both JSR-303 bean validation and traditional validation (a single validator class for the type) in Spring? If so, what configuration is required to set this up?
I have tried the instructions on the reference.
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new DualEntryValidator());
}
#RequestMapping(value="/dualEntry.htm", method = RequestMethod.POST)
public ModelAndView handlePost(#Valid DualEntryForm form, BindingResult result) {
ModelAndView modelAndView = new ModelAndView("dualEntry", getCommonModel());
if (!result.hasErrors()){
//do logic
return modelAndView;
}else {
modelAndView.addObject("dualEntryForm", form);
return modelAndView;
}
}
I can get this to use my custom Validator or the JSR-303 validation, but not both. If I have the initBinder present as in the example it uses the custom Validator. If I remove it the JSR-303 bean validation is used. How can I use both?

I've done that following the instructions here:
http://blog.jteam.nl/2009/08/04/bean-validation-integrating-jsr-303-with-spring/
See the "Enjoy both worlds" section. Shortly, you explicitly run a JSR303 validation from a Spring validator, "joining" the results of JSR303 validations based on annotations and your custom validation logic.

I realise this is quite old, but I got this to work with minimal disturbance to my code
Change binder.setValidator(new DualEntryValidator());
to
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new DualEntryValidator());
}
With setValidator() you're replacing the JSR-303 validator with your one. With addValidator(), the JSR-303 validator is called and so is yours.
You need to make sure that your validator does not overlap with your JSR-303 #NotNull, #Min, #Max, etc. annotations otherwise you'll get duplicate error messages added.

Spring provides three handle for bean validation.
1.abstract class AbstractPropertyValidationAnnotationHandler
2.abstract class AbstractMethodValidationAnnotationHandler
3.abstract class ClassValidationAnnotationHandler
In this example i am implementing custom annotation CustomAnnotationHandle
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
Class CustomAnnotationHandle extends Annotation{
public abstract String value();
}
To implement custom annotation for property validation we need to extend AbstractPropertyValidationAnnotationHandler Class.
AbstractPropertyValidationAnnotationHandler provides createValidationRule abstract method
protected abstract AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s);
So,the extended class must provide implementation of
protected abstract AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s)
public class CustomPropertyAnnotationHandler extends AbstractPropertyValidationAnnotationHandler
{
public CustomPropertyAnnotationHandler()
{
super(new Class[] {
XXX.XXX.PackageLevle.CustomAnnotationHandle // as it takes array of custom annotation ,so we can pass more than one
// overwriting abstract method
protected AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s){
CustomAnnotationHandle value = (CustomAnnotationHandle)annotation;
return TestValidationRule(value.getValue());
// as you can see it return AbstractValidationRule.So, we need a class to give our bean specific validation rule.In our case it is
//TestValidationRule
}
}
}
public class TestValidationRule extends AbstractValidationRule
{
public TestValidationRule (String valuetest)
{
super();
this.valuetest = valuetest;
}
Private String valuetest;
}
Spring provides AnnotationBeanValidationConfigurationLoader Class.This class is used for spring own annotation for bean validation.
DefaultValidationAnnotationHandlerRegistry class is used as defaultHandlerRegistry.But if we need to provide our own annotaion then we
need to extend AnnotationBeanValidationConfigurationLoader and set our specific handleregistry via method
setHandlerRegistry(new CustomPropertyAnnotationHandler());
Class DefaultValidationAnnotationHandlerRegistry is used to register spring own annotation for bean validation.It register bean by
calling registerPropertyHandler method of SimpleValidationAnnotationHandlerRegistry class.So for our custom annotation we need to
register CustomPropertyAnnotationHandler by calling registerPropertyHandler method of SimpleValidationAnnotationHandlerRegistry class
public class OurBeanSpecificValidationLoader extends AnnotationBeanValidationConfigurationLoader
{
public OurBeanSpecificValidationLoader ()
{
super();
setHandlerRegistry(new OurSpecificAnnotationHandleRegistery ());
}
}
public class OurSpecificAnnotationHandleRegistery extends DefaultValidationAnnotationHandlerRegistry
{
public OurSpecificAnnotationHandleRegistery ()
{
registerPropertyHandler(new CustomPropertyAnnotationHandler() );
}
}
so you have your custom annotation for bean valiation.E.g
#CustomAnnotationHandle(value = "test")
private Object test;

Related

Is there a way for #SpyBean to create spies for all types based on the interface type in a Spring Boot test?

I have a Spring Boot application where I would like to ensure that a list of decorators are verified to be executed. These decorators all extend from the same Abstract class, which in turn extend from the same interface, and they are autowired into a service class as a list of decorators. I would have thought that providing the #SpyBean(MyDecorator.class) at the class level of the test would have done the trick, but I got the error specifying that the decorator is not a spy. It looks like the MockitoPostProcessor class expects that we provide the individual concrete classes in the annotation as so #SpyBean(classes = {decorator1.class,decorator2.class}). I tried the latter, and it worked.
However, the issue that I have with this is that we have to add to this list every time we create a new decorator, which is not ideal. This is why I thought it makes sense to have the interface type be checked as well. Please let me know if there is a better way of doing this, or if I missed something. A thought that crossed my mind was to define my own post processor to wrap any bean from a defined type in a mockito spy, but I would like to check here first. Here is a skeleton definition of the classes to help you understand my dilemma.
MyDecorator.java
public interface MyDecorator{
public void decorate(SomeObject obj);
}
AbstractDecorator.java
public class AbstractDecorator implements MyDecorator{
//common decorator logic
}
Decorator1.java
#Component
public class Decorator1 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
Decorator2.java
#Component
public class Decorator2 extends AbstractDecorator{
public void decorate(SomeObject obj){
//decoration logic
}
}
DecorationService.java
#Service
public class DecorationService implements Service{
#Autowired
private List<MyDecorator> decoratorList;
public void processDecorators(){
//go through list of decorators and process some object
}
}
DecoratorServiceTest.java
#Runwith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
//#SpyBean(MyDecorator.class) //<-- This doesn't wrap the classes in a spy and errors out
#SpyBean(classes = {Decorator1.class, Decorator2.class}) //<-- This works
public class DecoratorServiceTest{
#Autowired
private List<MyDecorator> decoratorList;
#Test
public void testProcessDecorator(){
//verify that each decorator was processed
}
}
I posted a spring boot github issue here. Hopefully we would either see an improvement on it or we get an explanation as to why it is designed in this way.
I have a workaround in place that I'm using which is I've created a class that implements Spring's BeanPostProcessor interface, and I override the postProcessAfterInitialization method, and I check if the class is what I'm expecting, then I would wrap it in a mockito spy. Also, you would need to define the spring bean.
Here is a snippet of the class that I created.
public class SpyBeanPostProcessor<T> implements BeanPostProcessor{
/**
* The class type to spy on.
*/
private Class<T> typeToSpy;
/**
* Construct a SpyBeanPostProcessor with a class type to wrap
* as a {#link org.mockito.Spy}
* #param typeToSpy The class type to spy on.
*/
public SpyBeanPostProcessor(Class<T> typeToSpy) {
this.typeToSpy = typeToSpy;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (typeToSpy.isAssignableFrom(bean.getClass())){
return Mockito.spy(bean);
}else{
return bean;
}
}
}
I also needed to create a new spring bean that loads the BeanPostProcessor as shown below.
#Bean
public static SpyBeanPostProcessor decoratorSpyBeanPostProcessor(){
return new SpyBeanPostProcessor(MyDecorator.class);
}

Is it possible to use custom Spring validator to validate method input just like input of controller method in MVC?

I have custom Spring validator (not constraint-validator!)
#Component
public class MyValidator implements org.springframework.validation.Validator{
//my fine implementation
}
I can use that validator in controller like
#RestController
public class MyController{
#Autowired private MyValidator myValidator;
#InitBnder
public void binder(WebDataBinder binder){
binder.addValidator(myValidator);
}
#PostMapping
public void whatever(#RequestBody #Valid MyValidatedObject){ // not sure is #Valid needed here
.....
}
}
This works fine on controller level due to manual registration of validator to web binder. Now, I want to use the same validator on service method input, not on contorller level. If I do
#Service
#Validated
class MyService{
public void someMethod(#Valid MyValidatedObject input);
}
Validator is not fired. Other annotatons like #Min or #Max works here (throwing an exception as MyValidatedObject is not valid subject for those - but it shows that validation is about to be triggered). So it seams that only ConstrainValidator alike validators works here out of the box. How can I make Spring to use my custom validator to validate method input??
PS obviously I can inject validator and invoke it directly, but that is not what I want.

How can #Validated work on common method?

In Springboot project, when I try to add #Validated on controller method, it worked.But now I want to add it on a common method, then failed.
Try to add #Validated on controller method, it worked
public class TaskValidator {
private static final Logger logger = LogManager.getLogger("TaskValidatorLogger");
public void validateTest(#Validated Test test) {
logger.info("Validate: {}", test.getName());
}
public static void main(String[] args) {
new TaskValidator().validateTest(new Test());
}
}
#Data
public class Test {
#NotNull(message = "name can not be null")
private String name;
}
It should throw an MethodArgumentNotValidException but not.
Spring MVC has the ability to automatically validate #Controller
inputs. In previous versions it was up to the developer to manually
invoke validation logic.
In the controller methods, springboot automatically binds any validators to the model and invoke it when the data is bound to the object.
But in your case , you are trying to validate an object in which case , springboot might not be automatically binding your validator to your model and call the validator.So, in that case, you will need to manually bind the object to the validator.
or you can manually invoke the validator on a bean like :
#AutoWired
Validator validator;
...
validator.validate(book);

Spring Security: get controller class in Preauthorize annotation

Many of the controllers I use in a Spring application extend a common abstract class. The methods are thus declared in the abstract class.
I would like to apply a 'PreAuthorize' condition in the abstract method, but I need to get the name of the actual controller class being invoked to be passed to the EL evaluator.
Is there a way to do so?
In abstract class implement BeanNameAware interface.
public abstract class MyController implements BeanNameAware{
String beanName;
#Override
public void setBeanName(final String beanName) {
this.beanName = beanName;
}
#Override
public String getBeanName() {
return beanName;
}
}
You will get a hold of actual bean name instead regular proxy. If you need further customization implement BeanFactoryAware. You can than use that getBeanName method in SpEl of your preauthorize

Spring - Specify Implementation class of Model within Controller methods

For the most part I'm trying to code to interfaces as much as possible. But I'm having problems with a Spring Controller method signature. If I actually write the signature using the Model's interface, I end up getting the following exception:
BeanInstantiationException: Could not instantiate bean class[PageModel]: Specified class is an interface
Granted, I know that it's an interface, and if I change it to the actual implementation class, it works just fine. But is there no way to code to the interface? An annotation or something to tell Spring which bean to instantiate? btw, I'm using annotation configuration.
#RequestMapping("SpecificPageController")
public interface PageController {
#RequestMapping({"", "/load"})
ModelAndView load(#ModelAttribute("model") PageModel model);
}
#Controller
public class SpecificPageController implements PageController {
#Override
public ModelAndView load(final PageModel model) {
}
}
public interface PageModel {
... getters and setters...
}
public class ModelImpl implements PageModel {
... variables, getters, setters...
}
You can use #ModelAttribute on a controller method to get the implementation:
#ModelAttribute
public PageModel getModel() {
return new SpecificPageModel();
}

Resources