天天看点

spring boot2 webflux风格开发示例1,配置mongodb

话不多说 开搞。

项目接上回搭建的webflux项目。传送门:IDEA 搭建一个spring boot2 webflux项目

项目为jdk10+ spring boot2 +mongodb

1,配置mongodb

     1,修改配置文件名

        springboot项目创建好之后默认的resources里面的文件名为:application.properties

        方便我们使用,我们改成application.yml        

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/webflux
           

当启动时,会自动在你安装的mongodb中建立一个webflux数据库,mongodb默认端口号27017。

    2,启动类注解

/**
 * 启动类
 */
@EnableReactiveMongoRepositories//开启流mongodb
@SpringBootApplication(scanBasePackages = {"com.example.demo"})
public class DemoApplication {

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

注意是@EnableReactiveMongoRepositories,Reactive mongodb

    3,编写domain,repository,handlers,routers

    1,实体entity user

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;



@Document(collection = "user")//指定在mongodb中的表名
@Data //lombok注解
public class User {

    @Id  //主键
    private String id;
    @NotBlank  //校验框架 不能为空
    private String name;
    @Max(150)@Min(1)  //最大值最小值
    private Integer age;

}
           

    2,mongodb的JPA

import com.example.demo.domain.User;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends ReactiveMongoRepository<User, String> {


}
           

还是注意reactivemongodb。

跟其他JPA差不多,可以使用如findById这类的取名直接生成方法方式也可以使用@Query来自己写查询。这里不多说了。

    3,handlers层(MVC中的service层)

import com.example.demo.domain.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

/**
 *user处理器
 */
@Component  //交给spring管理的
public class UserHandler {

    @Autowired //注入JPA层
    private UserRepository repository;


    public Mono<ServerResponse> findAll(ServerRequest request){
        return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                .body(repository.findAll(), User.class);
    }

    public Mono<ServerResponse> save(ServerRequest request){
        //这里MediaType.TEXT_EVENT_STREAM设置的是返回值的格式
        return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                .body(repository.saveAll(request.bodyToMono(User.class)),User.class);
    }

}
           

这里我们看一下ServerResponse,它里面提供了很多静态方法还有内部接口,很多操作他通过自己就可以来完成。

如果你想返回成功,那么直接调用他的静态方法,ok()

static BodyBuilder ok() {
		return status(HttpStatus.OK);
	}
           

方式一个http的状态码, HttpStatus是一个枚举,它里面提供了所有的httpcode,200,400,404,500......

OK(200, "OK"),
           

所以通过这种方式我们直接设置了response中的返回码为200。

其他方法也比较简单,就不多列举了,有的我也不会。。哈哈。。

但是注意一下,ok()方法的返回值是BodyBuilder ,他是ServerResponse中的一个接口,用来构建response的。

包括contentType()也是,他们都不能满足返回值要求。所以在构建最后要使用BodyBuilder接口的body、syncbody、render

方法来返回Mono<ServerResponse>。如果不需要返回数据,只需要返回状态码,如404,那么可以使用ServerResponse的build()方法。

还有值得注意的是ServerRequest和ServerResponse。他们是reactive中的非阻塞的req和resp。用法和之前大家熟悉的HttpServletRequest有区别。

而且在写代码的时候,要时刻关注着不能让程序阻塞。不然就失去了响应式编程的意义。

关于为什么参数列表是ServerRequest ,返回值是Mono<ServerResponse>,我们下面4路由器中说到。

至于Mono和Flux,我想有时间了写一个单独的博客。

目前简单说,Mono代表返回0或者1个元素;Flux表示返回0或者N个元素。替换原来MVC开发模式中的直接返回entity和list等。

    4,routers路由器,,MVC模式下的controller

我觉得最大的改变就是使用使用路由器替代原先的控制器。

import com.example.demo.handlers.UserHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;


/**
 * 路由器
 */
@Configuration
public class UserRouters {

    @Bean
    public RouterFunction<ServerResponse> router(UserHandler handler) {

        return RouterFunctions.nest(RequestPredicates.path("/u"),
                RouterFunctions
                        .route(RequestPredicates.GET("/"),handler::findAll)
                        .andRoute(RequestPredicates.POST("/").
                         and(RequestPredicates.accept(MediaType.APPLICATION_JSON_UTF8)), handler::save));
    }
}
           
上面例子来源于RouterFunctions.nest的注解。略加修改。
           

webflux注册了一个以RouterFunction<ServerResponse>为返回值的Bean。

当系统发现存在该返回值得Bean时,就会将这个Bean注册为一个路由用来处理请求。

RouterFunction是一个函数式接口,输入一个ServerRequest,返回一个Mono<HandlerFunction<T>>

RouterFunctions提供了实现RouterFunction的一些方法。所以这里我们使用RouterFunctions来进行操作。

public static <T extends ServerResponse> RouterFunction<T> nest(
			RequestPredicate predicate, RouterFunction<T> routerFunction) {

		return new DefaultNestedRouterFunction<>(predicate, routerFunction);
	}
           

nest方法输入一个RequestPredicate,一个RouterFunction。

RequestPredicate是去请求的地址,同样是一个函数式接口,并且也给他提供了一个工具类RequestPredicates。

RequestPredicates.path()  代表请求路径

RequestPredicates.GET() 代表请求是一个get请求

RequestPredicates.POST() 代表请求是一个POST请求

其实看一下源码,以GET为例

public static RequestPredicate GET(String pattern) {
		return method(HttpMethod.GET).and(path(pattern));
	}
           

path

public static RequestPredicate path(String pattern) {
		Assert.notNull(pattern, "'pattern' must not be null");
		return pathPredicates(DEFAULT_PATTERN_PARSER).apply(pattern);
}
           

GET内部还是调用的path方法,只不过在前面加上了get。

一般常用的put,get,post,delete,patch啥的都已经提供了,所以我们直接使用相应的请求即可。真贴心。

第二个参数还是RouterFunctions。

所以这个方法,可以视为将这个方法内的所有请求路径中一样的部分抽取出来,作为父路径。

再看一下RouterFunctions..route方法。

public static <T extends ServerResponse> RouterFunction<T> route(
			RequestPredicate predicate, HandlerFunction<T> handlerFunction) {

		return new DefaultRouterFunction<>(predicate, handlerFunction);
	}
           

输入一个RequestPredicate和一个HandlerFunction

再看一下HandlerFunction

@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {

	/**
	 * Handle the given request.
	 * @param request the request to handle
	 * @return the response
	 */
	Mono<T> handle(ServerRequest request);

}
           

这也是一个函数式接口,输入一个ServerRequest,输出一个Mono<T>.

这就又回到了刚才我们自己写的handler

public Mono<ServerResponse> findAll(ServerRequest request){
        return ServerResponse.ok().contentType(MediaType.TEXT_EVENT_STREAM)
                .body(repository.findAll(), User.class);
    }
           

我们写的handler正好就是handlerFunction的实现。’

到这这一切算是联系上了。。我写的有点乱,凑合着看吧。

现在项目写完了。启动验证一下。

spring boot2 webflux风格开发示例1,配置mongodb

好了,就到这吧。如果有什么写的不好的地方,请指教。