Preliminary Exploration of Responsive Spring demo Based on WebFlux

Posted by lunarul on Wed, 02 Oct 2019 08:02:16 +0200

1. What is responsive programming? Why do we now think this is the trend of micro service?

Let's dig a hole and give a simple formula.

Responsive programming = data stream + change transfer + declarative

 

2. Following nonsense, don't talk much about code directly

The Implementing Thought and Introduction of the Core Reactor of WebFlux

https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-reactive-spring-web

Because it is a preliminary study, so as far as possible to give the whole code, the lesson to understand in detail what kind of category or domain is quoted, because it is a simple experiment, so it is inevitable that there are mistakes.

 

First, the project startup class, similar to Spring Boot

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.reactive.config.EnableWebFlux;

@SpringBootApplication
@EnableWebFlux
public class WebfluxDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(WebfluxDemoApplication.class, args);
	}

}

Then I have some configuration for web interaction.

Mainly in Codec, the configuration of serialization and deserialization for LocalDateTime related classes and two kinds of enumeration interfaces I defined are added.

package com.example.demo.config;

import static com.example.demo.utils.LocalDateTimeUtils.DATE_FORMATTER;
import static com.example.demo.utils.LocalDateTimeUtils.DATE_TIME_FORMATTER;
import static com.example.demo.utils.LocalDateTimeUtils.TIME_FORMATTER;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.reactive.config.WebFluxConfigurer;

import com.example.demo.config.message.formatter.IntegerToIntegerBaseEnumFactory;
import com.example.demo.config.message.formatter.StringToIntegerBaseEnumFactory;
import com.example.demo.config.message.formatter.StringToLocalDate;
import com.example.demo.config.message.formatter.StringToLocalDateTime;
import com.example.demo.config.message.formatter.StringToLocalTime;
import com.example.demo.config.message.formatter.StringToStringBaseEnumFactory;
import com.example.demo.type.IntegerBaseEnum;
import com.example.demo.type.StringBaseEnum;
import com.example.demo.type.zt.TaskStatus;
import com.example.demo.utils.json.jackson.IntegerBaseEnumDeserializer;
import com.example.demo.utils.json.jackson.IntegerBaseEnumSerializer;
import com.example.demo.utils.json.jackson.StringBaseEnumDeserializer;
import com.example.demo.utils.json.jackson.StringBaseEnumSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

@Configuration
public class WebConfig implements WebFluxConfigurer {
	
	@Override
	public void addFormatters(FormatterRegistry registry) {
		
		registry.addConverter(new StringToLocalDateTime());
		registry.addConverter(new StringToLocalDate());
		registry.addConverter(new StringToLocalTime());
		
		registry.addConverterFactory(new IntegerToIntegerBaseEnumFactory());
		registry.addConverterFactory(new StringToIntegerBaseEnumFactory());
		registry.addConverterFactory(new StringToStringBaseEnumFactory());
		
//		registry.addFormatter(new LocalDatetimeFormatter());
		
	}
	
	@Override
	public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
		
		var objectMapper = initObjectMapper();
		configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper));
		configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
	}
	
	private ObjectMapper initObjectMapper() {
		
		var objectMapper = new ObjectMapper();
//		var objectMapper = Jackson2ObjectMapperBuilder.json().build();
		// Ignore empty properties
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
		// objectMapper.enable(SerializationConfig.Feature.INDENT_OUTPUT); //Formatting
		objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
		objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
		
		JavaTimeModule timeModule = new JavaTimeModule();
		timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
		timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER));
		timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(TIME_FORMATTER));
		
		timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER));
		timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER));
		timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));
		objectMapper.registerModule(timeModule);
		
		addStringBaseEnumSerializers(TaskStatus.class);
		addIntegerBaseEnumSerializers();
		
		objectMapper.registerModule(simpleModule);
		return objectMapper;
	}
	
	SimpleModule simpleModule = new SimpleModule();
	@SafeVarargs
	private <T extends StringBaseEnum> void addStringBaseEnumSerializers(Class<T>... clazzes) {
		
		for (Class<T> s : clazzes) {
			
			simpleModule.addSerializer(s, new StringBaseEnumSerializer<>());
			simpleModule.addDeserializer(s, new StringBaseEnumDeserializer<>());
		}
	}
	
	@SafeVarargs
	private <T extends IntegerBaseEnum> void addIntegerBaseEnumSerializers(Class<T>... clazzes) {
		
		for (Class<T> i : clazzes) {
			
			simpleModule.addSerializer(i, new IntegerBaseEnumSerializer<>());
			simpleModule.addDeserializer(i, new IntegerBaseEnumDeserializer<>());
		}
	}
	
}

Here are two enumeration interfaces I customized

For String type

(2) For int/number type

package com.example.demo.type;

import java.io.Serializable;

public interface IntegerBaseEnum extends Serializable {

	/**
	 * Get value
	 * @return
	 */
	int getValue();

	/**
	 * Getting tag information
	 * @return
	 */
	String getLabel();
	
	public static <T extends IntegerBaseEnum> T valEnum(Class<T> type, Integer value) {

		if (value == null) {
			throw new IllegalArgumentException("IntegerBaseEnum value should not be null");
		}
		if (type.isAssignableFrom(Enum.class)) {
			throw new IllegalArgumentException("illegal IntegerBaseEnum type");
		}

		T[] enums = type.getEnumConstants();
		for (T t : enums) {

			if (t.getValue() == value.intValue()) {
				return t;
			}
		}
		throw new IllegalArgumentException("cannot parse integer: " + value + " to " + type.getName());
	}

    
}
package com.example.demo.type;

import java.io.Serializable;

public interface StringBaseEnum extends Serializable {

	/**
	 * Get value
	 * @return
	 */
	String getCode();

	/**
	 * Getting tag information
	 * @return
	 */
	String getLabel();

	public static <T extends StringBaseEnum> T valEnum(Class<T> type, String code) {

		if (code == null) {
			
			throw new IllegalArgumentException("StringBaseEnum value should not be null");
		}
		if (type.isAssignableFrom(Enum.class)) {
			
			throw new IllegalArgumentException("illegal StringBaseEnum type");
		}

		T[] enums = type.getEnumConstants();
		for (T t : enums) {

			if (t.getCode().equals(code)) {
				return t;
			}
		}

		throw new IllegalArgumentException("cannot parse String: " + code + " to " + type.getName());
	}
}

Writing here, I think it will lead to too much code, no focus. Give me a github address.

https://github.com/coderhuang/webflux-demo

Writing articles while still in the company, first dig a pit here

Topics: JSON Spring Java codec