Spring MVC 函数式编程进阶
1. 前言
上一篇对 Spring MVC 的函数式接口编程进行了简单入门,让很多不知道的同学见识了这种新操作。也有反应这种看起来没有传统写法顺眼,其实大家都一样。但是我们还是要敢于尝试新事物。Java Lambada 刚出来也是被人各种吐槽,现在我在很多项目都见到了它的身影。好了转回正题,本文是对上一篇的延伸,我们继续对 Functional Endpoint 进行一些了解和运用。范式转换其实上一篇已经介绍差不多了,但是一旦你初次接触这种方式往往会面临新的问题。
2. 新的问题
在使用这种风格时我们也会遇到一些新的问题。接下来我们将通过举例来一步步解决这些问题。
2.1 如何异常处理
接口异常处理是必须的。改成函数式风格后异常可以这样处理:
/**
* 接口附带异常处理逻辑.
*
* @param userService the user service
* @return the user by name with error handle
*/
public RouterFunction<ServerResponse> withErrorHandle() {
return RouterFunctions.route()
.GET("/userwitherrorhandle/{username}",
request -> ServerResponse.ok()
.body(userService.getByName(request.pathVariable("username"))))
// 异常处理
.onError(RuntimeException.class,
(e, request) -> EntityResponse.fromObject(e.getMessage())
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.build())
.build();
}
你可以使用上面的 onError
方法及其重载方法进行接口的异常处理。但是传统方法有统一异常处理啊!不要捉急,后面我们也会进行统一异常的处理。
2.2 如何使用过滤器
我还有不少 Spring MVC 在使用过滤器呢,使用这种风格如何编写过滤器,上一篇漏掉了一个处理过滤器的函数式接口HandlerFilterFunction
。我们通过该接口来对请求进行过滤:
/**
* 对特定接口指定过滤器.
*
* @param userService the user service
* @return the router function
*/
public RouterFunction<ServerResponse> withFilter() {
return RouterFunctions.route().POST("/save",
request -> ServerResponse.ok()
.body(userService.saveUser(request.body(UserInfo.class))))
// 执行了一个过滤器逻辑 参数携带了 save 放行 否则返回 bad request 并附带消息
.filter((request, next) -> request.param("save").isPresent() ?
next.handle(request) :
ServerResponse.status(HttpStatus.BAD_REQUEST).body("no save"))
.build();
}
通过 filter
方法我们可以实现日志、安全策略、跨域等功能。
2.3 如何使用拦截器
使用函数式编程风格时并没有提供 Spring MVC 的拦截器 API,但是提供了类似过滤器前置/后置处理机制以达到同样的效果。
public RouterFunction<ServerResponse> getUserByName() {
return RouterFunctions.route()
.GET("/user/{username}",
request -> ServerResponse.ok()
.body(userService.getByName(request.pathVariable("username"))))
// 前置处理 打印 path
.before(serverRequest -> {
log.info(serverRequest.path());
return serverRequest;
})
// 后置处理 如果响应状态为200 则打印 response ok
.after(((serverRequest, serverResponse) -> {
if (serverResponse.statusCode() == HttpStatus.OK) {
log.info("response ok");
}
return serverResponse;
})).build();
}
当你请求/user/{username}
时, before
和 after
方法将会分别进行前置和后置处理。
2.4 如何进行统一处理
传统方式我们每个Controller 处理的都是特定单一领域的业务,UserController
处理 User
相关业务,我们会给它添加一个统一的前缀标识 /v1/user
;OrderController
处理 Order
相关业务,给它添加一个统一的前缀标识 /v1/order
。对相同得业务接口进行聚合更加有利于维护使用函数式编程我们可以通过以下方式实现:
@Bean
RouterFunction<ServerResponse> userEndpoints(UserController userController) {
return RouterFunctions.route()
.path("/v2/user", builder -> builder
// /get/{username} -> /v2/user//get/{username}
.add(userController.getUserByName()
// /del/{username} -> /v2/user//del/{username}
.and(userController.delUser()
// /save -> /v2/user/save
.and(userController.saveUser()
// /update -> /v2/user/update
.and(userController.updateUser())))))
.build();
}
你也可以使用 RouterFunctions.route().nest
相关的方法进行实现。而且对这些路由进行分组聚合之后就可以统一过滤器、拦截器、异常处理。例如上一篇提到的统一异常问题:
@Bean
RouterFunction<ServerResponse> nestEndpoints(UserController userController) {
return RouterFunctions.route().nest(RequestPredicates.path("/v1/user"),
builder -> builder
.add(userController.getUserByName())
.add(userController.delUser())
.add(userController.saveUser())
.add(userController.updateUser()))
// 对上述路由进行统一的异常处理
.onError(RuntimeException.class,
(throwable, serverRequest) -> ServerResponse
.status(HttpStatus.BAD_REQUEST)
.body("bad req"))
.build();
}
3. 总结
本文主要对 Spring MVC 函数式开发和传统开发中等效的特性(过滤器、拦截器、分组聚合等)进行了简单的说明,更加贴合于实际运用。函数式风格开发更加灵活,但是同样让习惯命令式编程的开发者有点不适应,但是目前越来越被普遍的应用。所以-/如果有志于长期从事编程开发的同学来说,还是需要掌握的。本文的 demo 可通过关注公众号:Felordcn回复 mvcfun 获取。
关注公众号:Felordcn 获取更多资讯
Spring MVC 函数式编程进阶的更多相关文章
- Scala函数式编程进阶
package com.dtspark.scala.basics /** * 函数式编程进阶: * 1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量: * 2, 函数更长用的方式 ...
- Spark函数式编程进阶
函数式编程进阶 1.函数和变量一样作为Scala语言的一等公民,函数可以直接复制给变量: 2.函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是匿名函数赋值给 ...
- Python函数式编程(进阶2)
转载请标明出处: http://www.cnblogs.com/why168888/p/6411915.html 本文出自:[Edwin博客园] Python函数式编程(进阶2) 1. python把 ...
- Scala实战高手****第12课:Scala函数式编程进阶(匿名函数、高阶函数、函数类型推断、Currying)与Spark源码鉴赏
/** * 函数式编程进阶: * 1.函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量 * 2.函数更常用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称 ...
- 使用Spring MVC创建 REST API
1.REST的基础知识 当谈论REST时,有一种常见的错误就是将其视为“基于URL的Web服务”——将REST作为另一种类型的远程过程调用(remote procedurecall,RPC)机制,就像 ...
- [读后感]spring Mvc 教程框架实例以及系统演示下载
[读后感]spring Mvc 教程框架实例以及系统演示下载 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&qu ...
- 编程体系结构(08):Spring.Mvc.Boot框架
本文源码:GitHub·点这里 || GitEE·点这里 一.Spring框架 1.框架概述 Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 ...
- Python进阶:函数式编程实例(附代码)
Python进阶:函数式编程实例(附代码) 上篇文章"几个小例子告诉你, 一行Python代码能干哪些事 -- 知乎专栏"中用到了一些列表解析.生成器.map.filter.lam ...
- J2EE进阶(十三)Spring MVC常用的那些注解
Spring MVC常用的那些注解 前言 Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam,@ModelAttribute等等这样 ...
随机推荐
- Ipython入门小教程
学习<利用python进行数据分析>第三章 IPython:一种交互式计算和开发环境的笔记,共享给大家,同时为自己作为备忘用. 安装ipython用pip即可.ps.博主用的是win7系统 ...
- MySQL之慢日志记录、分页
1.慢日志记录 slow_query_log = OFF #是否开启慢日志记录 long_query_time = 2 #时间限制,超过此时间,则记录 slow_query_log_file = C: ...
- 国外程序员整理的 PHP 资源大全
原文:http://blog.jobbole.com/82908/ ziadoz 在 Github 发起维护的一个 PHP 资源列表,内容包括:库.框架.模板.安全.代码分析.日志.第三方库.配置工具 ...
- application/x-www-form-urlencoded ,multipart/form-data, text/plain
APPLICATION/X-WWW-FORM-URLENCODED MULTIPART/FORM-DATA TEXT/PLAIN 后台返回的数据响应的格式类型 application/x-www-fo ...
- JVM相关参数设置
Java启动参数共分为三类: 其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容: 其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足, ...
- [Inno Setup] 字符串列表,当要处理一长串文件时很有用
https://wiki.freepascal.org/TStringList-TStrings_Tutorial TStringList-TStrings Tutorial │ Deutsch (d ...
- linux sort 命令实用手册
Linux 中的sort 命令是一个很实用的工具,用于对文本内容以行为单位进行ASCII 码排序,默认按照升序进行排序(当然也可以按照降序). sort 命令的格式如下: sort `参数` `文件名 ...
- qemu-img 整理
qemu-img命令语法: qemu-img command [command options] check命令: check [-f fmt < qcow2 | qed | vdi >] ...
- 国际站中国区,孟买上Redis 4.0 集群版
信息摘要: 国际站中国区,孟买上线Redis 4.0 集群版适用客户: 所有用户版本/规格功能: redis 4.0 集群版产品文档: https://www.alibabacloud.com/hel ...
- 安装opencv3.3.0方法
#系统环境:CentOS6.5 x64 #首先安装jdk7u80 mkdir /java tar -zxvf jdk-7u80-linux-x64.gz -C /java/ vim /etc ...