Spring5.2新特性

Spring5.2新特性

  • 函数式端点
    • 用法
    • 实现原理
    • spring-boot-web自动配置
    • 结论

函数式端点

在spring-webflux中已经实现了两条腿走路。采用了函数式和@Controller两种模式。在5.2版本中,spring-mvc也支持了函数式端点。

用法

@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
@Configuration
@Slf4j
public class FunctionalEndpointConfig {
    FunctionalEndpoint functionalEndpoint;

    @Bean
    public RouterFunction<ServerResponse> functionalEndpointRouterFunction() {
        return RouterFunctions.route()
                .GET("/", functionalEndpoint::index)
                .GET("/me", functionalEndpoint::me)
                .POST("/post-json", functionalEndpoint::postJson)
                .POST("/post-form", functionalEndpoint::postForm)
                .GET("/redirect", functionalEndpoint::redirect)
                .build();
    }

    @Bean
    public RouterFunction<ServerResponse> lambdaRouterFunction() {
        return RouterFunctions.route()
                .POST("/users", request -> {
                    return ServerResponse.ok().build();
                })
                .before(serverRequest -> {
                    // 最先执行,以便于包装 serverRequest
                    log.info("before");
                    return serverRequest;
                })
                .after((serverRequest, serverResponse) -> {
                    // 最后执行,以便于包装serverResponse
                    log.info("after");
                    return serverResponse;
                })
                .filter((request, next) -> {
                    // 中间执行,此处获取到的request是在before中增强过的
                    // 与servlet的filter类似,实现过滤的功能
                    log.info("filter");
                    return next.handle(request);
                })
                .build();
    }

    @Bean
    public RouterFunction<ServerResponse> errorRouterFunction() {
        return RouterFunctions.route()
                .POST("/errors", request -> {
                    throw new IllegalAccessException();
                })
                // 错误处理,处理所有异常
                // 如果此处不处理异常,异常处理会进入spring-mvc的标准处理流程
                .onError(Exception.class, (throwable, serverRequest) -> {
                    return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
                })
                .build();
    }

}
@Component
@Slf4j
public class FunctionalEndpoint {
    public ServerResponse index(ServerRequest serverRequest) {

        val headers = serverRequest.headers();
        val httpMethod = serverRequest.method();
        val methodName = serverRequest.methodName();
        val cookies = serverRequest.cookies();
        val params = serverRequest.params();
        val stringStringMap = serverRequest.pathVariables();
        val httpServletRequest = serverRequest.servletRequest();
        val uri = serverRequest.uri();
        val principal = serverRequest.principal();
        val inetSocketAddress = serverRequest.remoteAddress();

        log.info("headers:{}", headers);
        log.info("httpMethod:{}", httpMethod);
        log.info("methodName:{}", methodName);
        log.info("cookies:{}", cookies);
        log.info("params:{}", params);
        log.info("stringStringMap:{}", stringStringMap);
        log.info("httpServletRequest:{}", httpServletRequest);
        log.info("uri:{}", uri);
        log.info("principal:{}", principal);
        log.info("inetSocketAddress:{}", inetSocketAddress);

        return ServerResponse.ok().body(Map.of("name", "name"));
    }

    public ServerResponse me(ServerRequest serverRequest) {
        val stringObjectHashMap = new HashMap<String, Object>();
        stringObjectHashMap.put("id", 1);
        stringObjectHashMap.put("username", 123);

        return ServerResponse.ok().body(stringObjectHashMap);
    }

    public ServerResponse postJson(ServerRequest serverRequest) throws ServletException, IOException {
        // 接收json数据
        val body = serverRequest.body(new ParameterizedTypeReference<Map<String, Object>>() {
        });

        return ServerResponse.ok().body(body);
    }

    public ServerResponse postForm(ServerRequest serverRequest) throws ServletException, IOException {

        // 接收form表单数据
        val body = serverRequest.body(new ParameterizedTypeReference<MultiValueMap<String, String>>() {
        });
        val stringStringMap = body.toSingleValueMap();
        return ServerResponse.ok().body(stringStringMap);
    }

    /** 重定向 */
    public ServerResponse redirect(ServerRequest serverRequest) throws ServletException, IOException {
        // 重定向到 /
        val path = serverRequest.uriBuilder().replacePath("/");
        URI build = path.build();
        return ServerResponse.status(HttpStatus.FOUND).location(build).build();
    }

}

实现原理

RouterFunctionMapping 检索RouterHandler

public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {

	@Nullable
	private RouterFunction<?> routerFunction;

	private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();

	private boolean detectHandlerFunctionsInAncestorContexts = false;

	public RouterFunctionMapping() {
	}

	public RouterFunctionMapping(RouterFunction<?> routerFunction) {
		this.routerFunction = routerFunction;
	}

	public void setRouterFunction(@Nullable RouterFunction<?> routerFunction) {
		this.routerFunction = routerFunction;
	}
	@Nullable
	public RouterFunction<?> getRouterFunction() {
		return this.routerFunction;
	}

	public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
		this.messageConverters = messageConverters;
	}

	public void setDetectHandlerFunctionsInAncestorContexts(boolean detectHandlerFunctionsInAncestorContexts) {
		this.detectHandlerFunctionsInAncestorContexts = detectHandlerFunctionsInAncestorContexts;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if (this.routerFunction == null) {
			initRouterFunction();
		}
		if (CollectionUtils.isEmpty(this.messageConverters)) {
			initMessageConverters();
		}
	}
	/**
	 * 收集所有RouterFunction
	*/
	@SuppressWarnings({"rawtypes", "unchecked"})
	private void initRouterFunction() {
		ApplicationContext applicationContext = obtainApplicationContext();
		Map<String, RouterFunction> beans =
				(this.detectHandlerFunctionsInAncestorContexts ?
						BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RouterFunction.class) :
						applicationContext.getBeansOfType(RouterFunction.class));

		List<RouterFunction> routerFunctions = new ArrayList<>(beans.values());
		if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
			routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
		}
		this.routerFunction = routerFunctions.stream()
				.reduce(RouterFunction::andOther)
				.orElse(null);
	}

	private void initMessageConverters() {
		List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(4);
		messageConverters.add(new ByteArrayHttpMessageConverter());
		messageConverters.add(new StringHttpMessageConverter());

		try {
			messageConverters.add(new SourceHttpMessageConverter<>());
		}
		catch (Error err) {
			// Ignore when no TransformerFactory implementation is available
		}
		messageConverters.add(new AllEncompassingFormHttpMessageConverter());

		this.messageConverters = messageConverters;
	}
	/**
	 * 匹配RouterFunction
	 */
	@Nullable
	@Override
	protected Object getHandlerInternal(HttpServletRequest servletRequest) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(servletRequest);
		servletRequest.setAttribute(LOOKUP_PATH, lookupPath);
		if (this.routerFunction != null) {
			ServerRequest request = ServerRequest.create(servletRequest, this.messageConverters);
			servletRequest.setAttribute(RouterFunctions.REQUEST_ATTRIBUTE, request);
			return this.routerFunction.route(request).orElse(null);
		}
		else {
			return null;
		}
	}

}

HandlerFunctionAdapter 负责执行HandlerFunction

public class HandlerFunctionAdapter implements HandlerAdapter, Ordered {

	private int order = Ordered.LOWEST_PRECEDENCE;

	public void setOrder(int order) {
		this.order = order;
	}

	@Override
	public int getOrder() {
		return this.order;
	}

	@Override
	public boolean supports(Object handler) {
		return handler instanceof HandlerFunction;
	}

	@Nullable
	@Override
	public ModelAndView handle(HttpServletRequest servletRequest,
			HttpServletResponse servletResponse,
			Object handler) throws Exception {


		HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;

		ServerRequest serverRequest = getServerRequest(servletRequest);
		ServerResponse serverResponse = handlerFunction.handle(serverRequest);

		return serverResponse.writeTo(servletRequest, servletResponse,
				new ServerRequestContext(serverRequest));
	}

	private ServerRequest getServerRequest(HttpServletRequest servletRequest) {
		ServerRequest serverRequest =
				(ServerRequest) servletRequest.getAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
		Assert.state(serverRequest != null, () -> "Required attribute '" +
				RouterFunctions.REQUEST_ATTRIBUTE + "' is missing");
		return serverRequest;
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		return -1L;
	}


	private static class ServerRequestContext implements ServerResponse.Context {

		private final ServerRequest serverRequest;


		public ServerRequestContext(ServerRequest serverRequest) {
			this.serverRequest = serverRequest;
		}

		@Override
		public List<HttpMessageConverter<?>> messageConverters() {
			return this.serverRequest.messageConverters();
		}
	}
}

spring-boot-web自动配置

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
	@Bean
	public RouterFunctionMapping routerFunctionMapping(
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

		RouterFunctionMapping mapping = new RouterFunctionMapping();
		mapping.setOrder(3);
		mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
		mapping.setCorsConfigurations(getCorsConfigurations());
		mapping.setMessageConverters(getMessageConverters());
		return mapping;
	}
	/**
	 * Returns a {@link HandlerFunctionAdapter} for processing requests through
	 * {@linkplain org.springframework.web.servlet.function.HandlerFunction handler functions}.
	 * @since 5.2
	 */
	@Bean
	public HandlerFunctionAdapter handlerFunctionAdapter() {
		return new HandlerFunctionAdapter();
	}
	}

结论

基于@Controller模式的端点那么好用?为什么要用函数式端点呢?

你可能感兴趣的:(spring,springboot)