A very simple Demo of Springboot. When we write a SpringBoot demo, we usually return simple types such as String, Long, etc. We want to write a slightly more complex demo, so the return value defines a generic BaseResponse, like this:

public class BaseResponse<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    private String code;
    private String messageInternal;
    private String message;
    private T data;

    public BaseResponse(a) {
        this.code = "200";
        this.messageInternal = null;
    }

    public BaseResponse(T data) {
        this.code = "200";
        this.messageInternal = null;
        this.data = data; }}Copy the code

On return, set the generated data into the BaseResponse object:

AddressResp resp = getCodeByName(req, response);
BaseResponse baseResponse = new BaseResponse(resp);
return baseResponse;
Copy the code

Expected return results should be reported in fields such as code, messageInternal, etc. But the code returns the following:

Unable to find Converter.

To solve

  1. If converter cannot be found, it means that there is no Converter configured in the project, so configure one in the project. Springboot uses Jackson as a JSON format converter by default, or fastJSON can be used. Fastjson configuration is as follows:

    @Bean
    	public HttpMessageConverters fastJsonHttpMessageConverters(a) {
    		//1, define a convert object to convert messages
    		FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
    		//2. Add fastjson configuration information
    		FastJsonConfig fastJsonConfig = new FastJsonConfig();
    		fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
    		//3. Add configuration information to convert
    		fastConverter.setFastJsonConfig(fastJsonConfig);
    		// add convert to convertersHttpMessageConverter<? > converter = fastConverter; List<MediaType> supportedMediaTypes =new ArrayList<>();
    		supportedMediaTypes.add(MediaType.APPLICATION_JSON);
    		fastConverter.setSupportedMediaTypes(supportedMediaTypes);  // The supported type must be configured. Otherwise, an error will be reported
    		return new HttpMessageConverters(converter);
    	}
    Copy the code
  2. When the configuration is complete and the code continues, the body of the result is returned with a null value.

This result is very strange. After following the code, it is found that the value of the object set to baseresponse exists, but the result is' {} '. Seems to have to follow the source code to find the answer.Copy the code

The source code to track

  1. Trace the source code into the doInvoke method of the InvocableHandlerMethod class, which basically calls the method of the class’s object based on the parameters passed in, and can be considered a proxy class.

  2. Continue to follow the code until HandlerMethodReturnValueHandlerComposite handleReturnValue method of a class, . This method has a call handler handleReturnValue, see the name is know this process the return value.

  3. Continue tracing the code until you reach the following code segment:

    if(selectedMediaType ! =null) {
    			selectedMediaType = selectedMediaType.removeQualityValue();
    			for(HttpMessageConverter<? > converter :this.messageConverters) {
    				GenericHttpMessageConverter genericConverter = (converter instanceofGenericHttpMessageConverter ? (GenericHttpMessageConverter<? >) converter :null);  // Get the message converter
    				if(genericConverter ! =null? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<? >>) converter.getClass(), inputMessage, outputMessage);if(body ! =null) {
    						Object theBody = body;
    						LogFormatUtils.traceDebug(logger, traceOn ->
    								"Writing ["+ LogFormatUtils.formatValue(theBody, ! traceOn) +"]");
    						addContentDispositionHeader(inputMessage, outputMessage);
    						if(genericConverter ! =null) {
    							genericConverter.write(body, targetType, selectedMediaType, outputMessage);   // Where the message is actually processed
    						}
    						else{ ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); }}else {
    						if (logger.isDebugEnabled()) {
    							logger.debug("Nothing to write: null body"); }}return; }}}Copy the code

    Tracking genericConverter. Write method, the genericConverter here is actually FastJsonHttpMessageConverter, the real message processing is done in this class.

  4. FastJsonHttpMessageConverter message processing in the write method,

    public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
    			throws IOException, HttpMessageNotWritableException {
    
    		final HttpHeaders headers = outputMessage.getHeaders();
    		addDefaultHeaders(headers, t, contentType);
    
    		if (outputMessage instanceof StreamingHttpOutputMessage) {
    			StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
    			streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
    				@Override
    				public OutputStream getBody(a) {
    					return outputStream;
    				}
    				@Override
    				public HttpHeaders getHeaders(a) {
    					returnheaders; }})); }else {
    			writeInternal(t, outputMessage);  // Process the messageoutputMessage.getBody().flush(); }}Copy the code
  5. The last call is to the Write method of JSONSerializer

    public final void write(Object object) {
            if (object == null) {
                out.writeNull();
                return; } Class<? > clazz = object.getClass(); ObjectSerializer writer = getObjectWriter(clazz);try {
                writer.write(this, object, null.null.0);
            } catch (IOException e) {
                throw newJSONException(e.getMessage(), e); }}Copy the code

    The writer.write method here converts a Java object to a JSON string. During debugging, it is found that the writer is constructed by ASM (ASM is a bytecode enhancement technology). It’s not clear how FastJson uses ASM technology to convert objects.

  6. At this point, the code seems to be unable to continue, but suddenly it turns back to BaseResponse and discovers that the FastJson conversion failed due to the set and GET methods with no attributes. I guess fastjson needs to call the set/get method of the object to do the conversion. The specific conversion needs to be further studied.

summary

Seemingly very simple problems, it is not easy to analyze, Springboot on the one hand to our Web development has brought great convenience, on the other hand too much encapsulation, to our analysis and positioning problems have also brought great suffering. If you want to have a deeper understanding of web development, or need to analyze their own reading source code, only the internal implementation mechanism of Springboot has a deeper understanding, in order to know how to troubleshoot some difficult diseases!