1

How can I swap RequestResponseBodyMethodProcessor with CustomRequestResponseBodyMethodProcessor in the BeanPostProcessor postProcessAfterInitialization() method?

I have copied entire code from RequestResponseBodyMethodProcessor and made some modification in my CustomRequestResponseBodyMethodProcessor.

Now I want Spring to use my CustomRequestResponseBodyMethodProcessor, not the inbuilt.

So tried overwriting in postProcessAfterInitialization() by implementing BeanPostProcessor.

In the below forum, where it says "create a new list of it, replace the normal RequestResponseBodyMethodProcessor with your custom implementation", how can I get handle to do this?

For Reference:

http://forum.spring.io/forum/spring-projects/web/130803-how-to-extend-requestresponsebodymethodprocessor-and-configure-it-in-webmvc-config-xm

Pseudo Code:

class BaseInsert {
    commonattribute1;
    commonattribute1;
}

class ChildInsert extends BaseInsert {
    childattribute1;
    childattribute2;
}

@PostMapping("/abc")
public Resource<?> insert(@RequestBody BaseInsert baseInsert){
...
}

I changed the code in CustomRequestResponseBodyMethodProcessor to assign ChildInsert in BaseInsert.

nickstack
  • 51
  • 4
  • in most OO programming languages, you don't always need to copy/paste code...have you tried extending the class you want to replace, instead? – Clint Eastwood Jul 29 '19 at 16:29

2 Answers2

2

Solution 1: I will recommend this solution the most

@Configuration
@EnableWebMvc
public class AdapterConfig extends WebMvcConfigurerAdapter {
    private final ApplicationContext applicationContext;
    @Autowired
    public TrackingAdapterConfig(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver>reso) {
        super.addArgumentResolvers(reso);
        reso.add( new CustomRequestBodyMethodProcessor(); }
}

public class CustomProcessor extends RequestResponseBodyMethodProcessor {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    return (parameter.getNestedGenericParameterType().getTypeName()
        .equalsIgnoreCase(BaseInsert.class.getName()));
    }

    @Override protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {                                               
        BaseInsert request = childInsert;
        return super.readWithMessageConverters(webRequest, parameter, request.getClass());
    }
}

Solution 2: This is also good solution but less performant because BeanPostProcessor interface has 2 methods 'postProcessBeforeInitialization()' and 'postProcessAfterInitialization()'.

So when you provide your implementation of this BeanPostProcessor interface with the class annotated as '@Configuration'.

postProcessBeforeInitialization() - This method is called every time before beans are created

postProcessAfterInitialization() - This method is called every time after beans are created.This is the place where CustomResolver can be added to list of resolvers

@Configuration
public class TestBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equalsIgnoreCase("requestMappingHandlerAdapter")) {
            RequestMappingHandlerAdapter requestMappingHandlerAdapter = (RequestMappingHandlerAdapter) bean;
            List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
            List<HandlerMethodArgumentResolver> modifiedArgumentResolvers = new ArrayList<>(argumentResolvers.size());                
            for(int i =1; i< argumentResolvers.size();i++){
                modifiedArgumentResolvers.add(argumentResolvers.get(i));
            }
            modifiedArgumentResolvers.add(new TestRequestBodyMethodProcessor(requestMappingHandlerAdapter.getMessageConverters(), new ArrayList<Object>()));
            ((RequestMappingHandlerAdapter) bean).setArgumentResolvers(null);
            ((RequestMappingHandlerAdapter) bean).setArgumentResolvers(modifiedArgumentResolvers);
        }
        return bean;
    }
}

public class TestRequestBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

    public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
        super(converters);
    }

    public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters, ContentNegotiationManager manager) {
        super(converters, manager);
    }

    public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice) {
        super(converters, null, requestResponseBodyAdvice);
    }

    public TestRequestBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
                                          ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
        super(converters, manager, requestResponseBodyAdvice);
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestBody.class);
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        parameter = parameter.nestedIfOptional();
        BaseInsert trans_type_code = ;
        Object arg = readWithMessageConverters(webRequest, parameter,
                Test.getModelClassObject().getClass());
        String name = Conventions.getVariableNameForParameter(parameter);

        WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
        if (arg != null) {
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
            }
        }
        mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());

        return adaptArgumentIfNecessary(arg, parameter);
    }

    @Override
    protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
                                                   Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
        if (arg == null) {
            if (checkRequired(parameter)) {
                throw new HttpMessageNotReadableException("Required request body is missing: " +
                        parameter.getMethod().toGenericString());
            }
        }
        return arg;
    }

    protected boolean checkRequired(MethodParameter parameter) {
        return (parameter.getParameterAnnotation(RequestBody.class).required() && !parameter.isOptional());
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

        mavContainer.setRequestHandled(true);
        ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
        // Try even with null return value. ResponseBodyAdvice could get involved.
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
}
nickstack
  • 51
  • 4
  • if you'd only had split both solutions, I would seriously have upvoted the first one and downvoted the second one – Clint Eastwood Jul 29 '19 at 15:30
  • For me, didn´t work the solution 1 (not tried solution 2). My problem was this https://stackoverflow.com/questions/67745858/override-default-message-when-responsebody-is-null/ I hope it can help – Miguel Cabanes May 29 '21 at 00:33
0

I tried the Solution 1 from previous post but also need this:

@Autowired
private RequestMappingHandlerAdapter adapter;

@PostConstruct
public void prioritizeCustomArgumentMethodHandlers () {
    List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(adapter.getArgumentResolvers ());
    List<HandlerMethodArgumentResolver> customResolvers = adapter.getCustomArgumentResolvers();
    argumentResolvers.removeAll(customResolvers);
    argumentResolvers.addAll (0, customResolvers);
    adapter.setArgumentResolvers (argumentResolvers);
}

Without this code, program doesn´t stop at my custom RequestResponseBodyMethodProcessor.

You can check my post : Override default message when @ResponseBody is null

Miguel Cabanes
  • 103
  • 2
  • 10