Spring5框架学习笔记(三)
Spring5框架新功能
- 整个Spring5框架代码基于java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除
- Spring5框架自带了通用的日志封装
- Spring5已移除Log4jConfigListener,官方建议是用Log4j2
- Spring5框架核心容器支持@Nullable注解
- @Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回值可以为空,属性值可以为空,参数值可以为空
- Spring5核心容器支持函数式风格GenericApplicationContext
- Spring5支持整合Junit5
//整合Junit4
@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
@ContextConfiguration(classes = TxConfig.class) //加载配置文件
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test(){
userService.accountMoney();
}
}
Spring框架新功能(webflux)
- WebFlux介绍
- Spring5添加的新模块,用于web开发,功能与spirngMVC类似,Webflux使用当前一种比较流行响应式编程出现的框架
- Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的
- 什么是异步非阻塞?
异步和同步,阻塞和非阻塞
同步和异步针对的是调用者,调用者发起请求,如果等着对方回应之后才去做其他事情就是同步,如果发起请求之后不等着对方回应就去做其他事情就是异步
阻塞和非阻塞针对的是被调用者,被调用者收到请求之后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后再去做事情就是非阻塞。 - Webflux特点
- 非阻塞是式:在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
- 函数式编程:Spring5框架基于java8,webflux使用java8函数式编程方式实现路由请求
- webflux和springMVC的区别
- 两个框架都可以使用注解方式,都运行在Tomcat容器中
- springMVC采用命令式编程,webflux采用异步响应式编程
- 什么是响应式编程?
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。 - java8及之前的版本,提供的观察者模式两个类Observer和Observable
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer = new ObserverDemo();
observer.addObserver((o, arg)->{
System.out.println("发生变化");
});
observer.addObserver((o, arg)->{
System.out.println("手动被观察者通知,准备改变");
});
observer.setChanged();
observer.notifyObservers();
}
}
响应式编程(Reactor实现)
- 响应式编程操作中,Reactor是满足Reactive规范框架
- Reactor两个核心类,Mono和Flux,这两个类都实现Publisher,提供丰富操作符,Flux对象实现发布者,返回N个元素,Mono实现发布者,返回0或1个元素
- Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值,错误信号,完成信号。错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了。错误信号终止数据流同时把错误信息传递给订阅者。
public class TestReactor {
public static void main(String[] args) {
Flux.just(1,2,3,4);
Mono.just(1);
Integer[] array = {1,2,3,4};
Flux.fromArray(array);
List<Integer> list = Arrays.asList(1,2,3,4);
Flux.fromIterable(list);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);
}
}
- 三种信号特点
- 错误信号和完成信号都是终止信号,不能共存
- 如果没有发送任何元素值,而是直接发送错误或完成信号,表示是空
- 如果没有错误信号,没有完成信号,表示是无限数据流
- 调用just或者其他方法只是声明数据流,数据流并没有发出,只有订阅之后才会触发数据流。
Flux.just(1,2,3,4).subscribe(System.out::println);
Mono.just(1).subscribe(System.out::println);
- 操作符
- 对数据流进行一道道操作,成为操作符,比如工厂流水线
- map: 元素映射为新的元素
- flatMap:元素映射为流,把每个元素转换成流,把转换之后的多个流合并成一个大的流
SpringWebflux执行流程和核心API
- SpringWebflux基于Reactor,默认容器是Netty,Netty是高性能,NIO框架,异步非阻塞框架
- SpringWebflux执行过程与springMVC相似
- SpringWebflux核心控制器DispatchHandler,实现接口WebHandler
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return this.createNotFoundError();
} else {
return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? this.handlePreFlight(exchange) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
return mapping.getHandler(exchange);
}).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
return this.invokeHandler(exchange, handler);
}).flatMap((result) -> {
return this.handleResult(exchange, result);
});
}
}
- SpringWebflux里面DispatcherHandler负责请求的处理,HandlerMapping:请求查询到处理的方法,HandlerAdapter:真正负责请求处理,HandlerResultHandler:响应结果处理
- SpringWebflux实现函数式编程,两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)
- SpringWebflux(基于注解编程模型)
@Repository
public class UserServiceImpl implements UserService {
private final Map<Integer, User> users = new HashMap<>();
public UserServiceImpl(){
this.users.put(1, new User("张三", "nan", 20));
this.users.put(2, new User("李四", "nv", 25));
this.users.put(3, new User("王五", "nv", 22));
}
@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.users.get(id));
}
@Override
public Flux<User> getAllUser() {
return Flux.fromIterable(this.users.values());
}
@Override
public Mono<Void> saveUserInfo(Mono<User> userMono) {
return userMono.doOnNext(person -> {
int id = this.users.size();
users.put(id, person);
}).thenEmpty(Mono.empty());
}
}
- SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat;SpringWebflux方式实现,异步非阻塞方式,基于SpringWebflux+Reactor+Netty
- SpringWebflux(基于函数式编程模型)
- 在使用函数式编程模型操作时,需要自己初始化服务器
- 基于函数式编程模型,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。
- SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService){
this.userService = userService;
}
public Mono<ServerResponse> getUserById(ServerRequest request){
int id = Integer.parseInt(request.pathVariable("id"));
//空值处理
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
Mono<User> userMono = this.userService.getUserById(id);
//把userMono进行转换返回
//使用reactorc操作符flatMap
return userMono.
flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(person))
.switchIfEmpty(notFound);
}
public Mono<ServerResponse> getAllUsers(ServerRequest request){
Flux<User> users = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users, User.class);
}
public Mono<ServerResponse> saveUser(ServerRequest request){
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));
}
}
//最终调用
public class Server {
public static void main(String[] args) throws IOException {
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
//创建router路由
public RouterFunction<ServerResponse> routingFunction(){
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService);
return RouterFunctions.route().GET("/user/{id}", userHandler::getUserById)
.GET("/users", userHandler::getAllUsers)
.POST("saveuser", userHandler::saveUser)
.build();
}
//创建服务器完成适配
public void createReactorServer(){
RouterFunction<ServerResponse> router = routingFunction();
HttpHandler httpHandler = RouterFunctions.toHttpHandler(router);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
//webclient调用方式
public class Client {
public static void main(String[] args) {
WebClient client = WebClient.create("http://127.0.0.1:57815");
String id = "1";
User block = client.get().uri("/user/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
.block();
System.out.println(block.getName());
//查询所有
Flux<User> userFlux = client.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
userFlux.map(user -> user.getName()).buffer().doOnNext(System.out::println).blockFirst();
}
}
循环依赖问题
- spring只能解决scope为singleton,通过set注入的循环依赖
- 通过构造注入或者scope为prototype的循环依赖无法解决,抛出BeanCurrentlyInCreationException异常
- 只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓中
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
//第一级缓存(也叫单例池),singletonObjects: 存放已经经历了完整生命周期的bean对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//第三级缓存: singletonFactories,存放可以生成bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
//第二级缓存: earlySingletonObjects,存放早期暴露出来的bean对象,bean的生命周期未走完(属性还未填充),就把这个bean存入该缓存中,也就是实例化但未初始化的bean放入该缓存中
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
private final Set<String> registeredSingletons = new LinkedHashSet(256);
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap(16));
//三级缓存中的ObjectFactory
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}