Spring5框架学习记录
一、Spring概念
- Spring是一个轻量级的开源的JavaEE框架。
- Spring可以解决企业应用开发的复杂性。
- Spring有两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象过程交给Spring进行管理。
- Aop:面向切面,不修改源代码进行功能增强。
- spring特点:
- 方便解耦,简化开发。
- Aop编程支持。
- 方便程序测试。
- 方便和其他框架进行整合。
- 方便进行事务操作。
- 降低API开发难度。
二、IOC容器
IOC概念和原理
概念
- 控制反转:把对象创建和对象之间的调用的过程交给Spring进行管理,使用IOC的目的是为了降低耦合度。
原理
- xml解析、工厂模式,反射
第一步,xml配置文件,配置创建对象
<bean id="dao" class="com.it.dao.UserDao"></bean>
第二步 有service类和dao类,创建工场类
class UserFactory{
public static UserDao getDao(){
String classValue = class属性值;// xml解析
Class clazz = Class.forName(classValue);// 2 通过反射创建对象
return (UserDao)clazz.newInstance();
}
}
-
IOC接口
-
IOC思想基于IOC容器完成,IOC容器底层就是对象工场。
-
Sping提供IOC容器实现两种方式(两个接口):
一、BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员使用。(注意:加载配置文件时候不会创建对象,在获取/使用对象时才会区创建对象。)
二、ApplicationContext:BeanFactroy接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。(注意:加载配置文件时就会把在配置文件里面的对象进行创建)
-
ApplicationContext常用实现类:
- FileSystemXmlApplicationContext(指定文件实际路径)
- ClassPathXmlApplicationContext(指定相对的src目录下路径)
-
Bean容器管理
什么时Bean管理
Bean管理的两个操作:
1. Spring创建对象。
2. Spring注入属性。
Bean管理操作两种方式
-
基于xml配置文件方式实现。
一、基于xml的对象注入 <!--配置User对象创建--> <bean id="user" class="com.company.spring5.User"></bean> (1) 在spring配置文件中,使用bean标签,标签里面添加对应属性。就可以实现对象创建。 (2) 在bean标签由很多属性。常用属性有。 1. id属性:唯一标识。 2. class属性:类全路径(包路径) (3) 创建对象默认执行无参构造方法。 二、 基于xml的属性注入 (1) DI:依赖注入,就是属性注入。 传统依赖注入:1.使用set方法进行注入。2.使用有参数构造进行注入。 <bean id="book" class="com.company.spring5.entry.Book" > <!--在bena标签里面使用 property--> <property name="bname" value="hjy"></property> <property name="bauthor" value="ljf"/> <property name="user" ref="user"/> </bean> 三、xml注入其他类型属性 1. 字面量 (1)null值。 <--!配置null值--> <property name="age"><null/></property> (2)属性值包含特殊符号。 <!--1.使用转义字符--> <!-- <property name="age" value="<南京>"/>--> <!--2.使用value标签--> <property name="age"> <value><![CDATA[<南京>]]></value> </property> 2. 注入属性-外部bean <bean id="userService" class="com.company.spring5.service.UserService"> <property name="userDao" ref="userDao"/> </bean> <bean id="userDao" class="com.company.spring5.dao.UserDaoImpl"/> 3. 注入属性-内部bean和级联赋值 实体类表示一对多关系 <!--内部bean--> <bean id="emp" class="com.company.spring5.entry.Emp"> <property name="ename" value="11"/> <property name="age"value="22"/> <property name="dept"> <bean id="dept" class="com.company.spring5.entry.Dept"> <property name="dname" value="保安"/> </bean> </property> </bean> <!--级联赋值--> <bean id="emp" class="com.company.spring5.entry.Emp"> <property name="ename" value="11"/> <property name="age"value="22"/> <property name="dept" ref="dept"> </property> </bean> <bean id="dept" class="com.company.spring5.entry.Dept"> <property name="dname" value="保安"/> </bean> xml注入list数据 <bean id="dept" class="com.company.spring5.entry.Dept"> <property name="dname" value="保安"/> <property name="list"> <list> <value></value> <value></value> <value></value> </list> </property> </bean> xml注入数组数据 <bean id="dept" class="com.company.spring5.entry.Dept"> <property name="dname" value="保安"/> <property name="list"> <array> <value></value> <value></value> <value></value> </array> </property> </bean> xml注入map数据 <bean id="dept" class="com.company.spring5.entry.Dept"> <property name="dname" value="保安"/> <property name="list"> <map> <entry key="" value=""></entry> <entry key="" value=""></entry> </map> </property> </bean>
FactroyBean
Spring有两种bean,一种普通bean,另外一种工厂bean(FactoryBean)
普通bean:在配置文件中定义的bean类型就是返回类型。
工厂bean:在配置文件定义bean类型可以和返回类型不一样。
Spring的作用域:单实例,多实例。
spring在默认情况下为单实例对象。
在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例对象 scope属性值: 1. 默认值,singleton 单实例 2. prototype 多实例 3. request 为每一个请求创建一个实例。 4. session 为每一个会话创建一个实例。 singleton和prototype区别 1. singleton为单实例,prototype为多实例。 2. singleton在加载配置文件时就会创建单实例对象。而prototype只有在调用getBean方法时才会创建实例,每次创建实例不同。
bean的生命周期
概念:从对象创建到对象销毁的过程。
bean生命周期 1. 通过构造器创建bean实例。(无参数构造器) 2. 为bean的属性设置值和对其他bean引用(设置set方法) 3. 调用bean的初始化的方法(需要进行配置初始化的方法) 4. bean可以使用了(对象获取到了) 5. 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
xml自动装配
<!-- bean标签属性autowire配置自动装配 autowire属性常用两个值: byName根据属性名称注入,注入值bean的id1 byType根据属性类型注入 -->
xml外部装配
通过导入context标签,实现读写.properties文件里面的配置信息。
-
基于注解方式实现。
<!--开启组件扫瞄--> <context:component-scan base-package="com.company.spring5"/> <!-- use-default-filters="false" 表示现在不使用默认filter,自己配置filter // 设置扫描哪些内容 <context:include-filter type="annotation" expression="com.company.spring5.entry.Book"/> // 设置不扫描哪些内容 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> --> Spring针对Bean管理中创建对象提供注解 @Component、@Service、@Controller、@Repository(四个注解功能一致) 基于注解方式实现属性注入 @Autowired(根据属性类型进行自动装配)、@Qualifier(根据属性名称进行注入)、@Resource(可以根据类型注入,也可以根据名称注入)、@Value(注入普通类型属性) 通过注解替代xml文件 @ComponentScan(组件扫描注解)
三、AOP
含义:面向切面,不修改远点进行功能增强。
什么是AOP
- 面向切面编程,利用AOP可以对业务逻辑的各个不发进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。
AOP底层原理
-
AOP底层使用动态代理。
第一种 有接口情况,使用JDK动态代理
创建借口实现类代理对象。
第二种 没有接口的情况,使用CGLIB动态代理
创建当前类子类的代理对象。
AOP(JDK动态代理)
-
使用jdk动态代理,使用Proxy类里面的方法创建代理对象。
-
调用newProxyInstance方法
方法有三个参数:
第一个参数,类加载器。
第二个参数,增强方法所在类,这类实习的接口,支持多个接口。
第三个参数,实习这个接口InvocationHandler,创建代理对象,写增强方法。
-
AOP术语
1. 连接点 (所有可增强的方法都可以称为连接点) 2. 切入点 (实际被增强的方法被称为切入点) 3. 通知(增强) (实际增强的逻辑就被称之为通知,通知有5种通知,前置通知,后置通知,环绕通知,异常通知,异常通知,最终通知) 4. 切面 (是一个动作,将通知应用到切入点的操作就叫做切面)
-
AOP操作
-
Spring框架一般都是基于AspectJ实现AOP操作
(注意:AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作。)
-
基于AspectJ实现AOP操作
(一)、基于xml配置文件实现。
(二)、基于注解方式实现(主要使用方式)
-
切入点表达式
一、 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
二、语法结构:execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
四、JdbcTemplate
五、事务管理
什么是事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败则全失败。
事务的四大特性
- 原子性。
- 一致性。
- 隔离性。
- 持久性。
事务操作
在spring进行事务管理的两种方式
- 编程式事务管理。
- 声明式事务管理。
- 基于注解方式
- 基于xml配置文件方式
在spring进行声明式事务管理,底层原理使用AOP原理。
声明式事务管理参数配置
1. 在service类上添加注解@Transactional,在这个注解里面可以配置事务相关参数。
propagetion:事务传播行为
1. PROPAGATION_REQUIRED 表示当前方法必须在一个具有事务的 上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新 开启一个事务。( 如果被调用端发生异常,那么调用端和被调用端事务都将回滚)
2. PROPAGATION_SUPPORTS 表示当前方法不必需要具有一个事务 上下文,但是如果有一个事务的话,它也可以在这个事务中运行
3. PROPAGATION_MANDATORY 表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常
4. PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
5. PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
6. PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
7. PROPAGATION_NESTED表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中 ,被嵌套的事务可以独立于被封装的事务中进行提交或 者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,
则propagation. required的一样
ioslation:事务隔离级别
ISOLATION_READ_UNCOMMITTED:读未提交
ISOLATION_READ_COMMITTED:读已提交
ISOLATION_REPEATABLE_READ:可重复读
ISOLATION_SERIALIZABLE:串行化
timeout:超时时间
1. 事务需要在一定时间内进行提交,如果不提交进行回滚。
2. 默认值是-1,设置时间以秒单位进行计算。
readOnly:是否只读
1. 读:查询操作,写:添加/修改/删除操作。
2. readOnley默认值为false,表示可以查询也可以添加修改删除操作。
3. 设置readOnly值是true,设置成true之后,只能查询。
rollbackFor:回滚
noRollbackFor:不回滚
六、Spring5新特性
1.@Nullable注解
Spring5框架核心容器支持@Nullable注解
- @Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法可以为null,属性值可以为null,参数值可以为null。
2.支持候选组件索引
3.函数式风格GenericApplicationContext AnnotationConfigApplicationContext
4.基本支持beanAPI注册。
5.在接口层面使用CGLIB动态代理的时候,提供事务,缓存,异步注解检测。
6.XML配置作用域流式
7.Spring WebMVC
8.全部都Servlet3.1签名支持在Spring-provied Filter实现。
9.在Srping MVC Controller方法里支持Servlet4.0 PushBuilder参数。
10.多个不可变对象的数据绑定(Kotkin/Lombok/@ConstructorPorties)
11.支持jackson2.9
12.支持JSON绑定API
13.支持protobuf3
14.支持Reactor3.1 Flux和Mono
七、Spring5整合日志
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
八、Spring中Webflux
1. SpringWebflux介绍
(1)是Spring5添加新的模块,用于web开发的,功能和SpringMVC类似的,Webflux使用当前一种比较流程响应式编程出现的框架。
(2)使用传统web框架,比如SpringMVC,这些基于Servlet容器,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。
异步非阻塞:
异步与同步。
阻塞和非阻塞。
Webflux特点:
第一 非阻塞: 在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程。
第二 函数式编程:Spring5框架基于JDK8,即Webflux可以体验Java8新特性。
SpringMVC和Webflux比较
- 两个框架都可以使用注解方式,都可以允许在tomcat等容器中。
- SpringMVC使用的是命令式编程,Webflux采用异步响应式编程。
- SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat。SpringWebflux方式实现,异步非阻塞方式,基于SpringWebflux+Reactor+Netty。
2. 响应式编程
(1)什么是响应式编程
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
Java8及之前版本提供的观察者模式两个类Observer和Observable。
public class ObserverDome extends Observable {
public static void main(String[] args) {
ObserverDome observerDome = new ObserverDome();
// 创建观察者
observerDome.addObserver((o,arg)->{ System.out.println("发生了变化"); });
observerDome.addObserver((o,arg)->{ System.out.println("手动被观察者通知,准备改变"); });
// 数据变化
observerDome.setChanged();
// 通知
observerDome.notifyObservers();
}
}
Java8之后
public class Main {
public static void main(String[] args) {
Flow.Publisher<String> publisher = subscriber -> {
subscriber.onNext("1");
subscriber.onNext("2");
subscriber.onError(new RuntimeException("出错了!!!"));
};
publisher.subscribe(new Flow.Subscriber<String>() {
@Override
public void onSubscribe(Flow.Subscription subscription) {
subscription.cancel();
}
@Override
public void onNext(String item) {
System.out.println(item);
}
@Override
public void onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
@Override
public void onComplete() {
System.out.println("publish complete");
}
});
}
}
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) { // just方法直接声明 Flux.just(1,3,4,5,6); Mono.just(1); // 其他方法 Integer[] array = {1,2,3,4}; Flux.fromArray(array); List<Integer> list = Arrays.asList(array); Flux.fromIterable(list); Stream<Integer> stream = list.stream(); Flux.fromStream(stream); } }
三种信号特点:
错误信号和完成信号都是终止信号,不能共存的,如果没有发送任何元素,而是直接发送错误或者完成,表示是一个空数据流,如果没有错误信号,没有完成信号,表示是无限数据流。
-
调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的。
-
操作符
对数据流进行一道道操作,称为操作符,比如工厂流水线。
第一 map 元素映射为新元素
第二 flatMap元素映射为流
3. SpringWebflux执行流程和核心API
SpringWebflux基于Reactot,默认容器是Netty,Netty是高性能的NIO框架,异步非阻塞框架。
SpringWebflux执行过程和SpringMVC相似的。SpringWebflux核心控制器DispatchHandler,实现接口是WebHandler。
代码原理:
在WebHandler类中有一个handle方法返回值是Mono<void> 参数为ServerWebExchange
//public interface WebHandler {
// Mono<Void> handle(ServerWebExchange exchange);
//}
public Mono<Void> handle(ServerWebExchange exchange) {
// ServerWebExchange 里面存放的是http里面的请求响应信息和相关参数
// 通过判断this.handlerMapping 判断接口映射是否为null 为null创建一个NotFound的错误
if (this.handlerMappings == null) {
return this.createNotFoundError();
} else {
// 判断参数是否为是飞行前请求。如果为则返回相关参数,否则根据请求地址,得到mapping值,调用具体的业务方法,最后返回处理结果。
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实现方式
SpringWebflux实现方式有两种:注解编程模型和函数编程模型,使用注解编程模型方式,和之前的SpringMVC使用类似,只需要把相关依赖配置到项目中,Springboot自动配置相关运行容器,默认情况下使用Netty服务器。
SpringWebflux(基于注解编程模型)
controller类
@RestController
@RequestMapping("/user")
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@GetMapping("/getId/{id}")
public Mono<User> getUserId(@PathVariable("id") int id){
return userService.getUserById(id);
}
@GetMapping("/list")
public Flux<User> getUserList(){
return userService.getAllUser();
}
@PostMapping("/add")
public Mono<Void> saveUserInfo(@RequestBody User user){
return userService.saveUserInfo(Mono.just(user));
}
}
service类
public interface UserService {
/**
* 根据id查询用户
* @param id 用户id
* @return 返回值
*/
Mono<User> getUserById(int id);
/**
* 查询所有用户
* @return 返回所有用户信息
*/
Flux<User> getAllUser();
/**
* 添加用户信息
* @param user 用户信息
*/
Mono<Void> saveUserInfo(Mono<User> user);
}
service实现类
@Service
public class UserServiceImpl implements UserService {
// 创建一个map集合,存储数据
private final Map<Integer, User> userMap = new HashMap<>();
public UserServiceImpl() {
this.userMap.put(1, new User("lucy", "男", 20));
this.userMap.put(2, new User("mary", "女", 18));
this.userMap.put(3, new User("tom", "女", 30));
this.userMap.put(4, new User("jack", "男", 24));
this.userMap.put(5, new User("hjy", "女", 16));
}
@Override
public Mono<User> getUserById(int id) {
return Mono.justOrEmpty(this.userMap.get(id));
}
@Override
public Flux<User> getAllUser() {
return Flux.fromIterable(this.userMap.values());
}
@Override
public Mono<Void> saveUserInfo(Mono<User> user) {
return user.doOnNext(person -> {
// 向map中添加值
int i = userMap.size() + 1;
userMap.put(i,person);
}).thenEmpty(Mono.empty());
}
}
User类
public class User {
private String name;
private String gender;
private Integer age;
public User() {
}
public User(String name, String gender, Integer age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
}
SpringWebflux(基于函数式编程模型)
- 在使用函数式编程模型操作时候,需要自己初始化服务器。
- 基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生产响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。
- SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse。
实现,其中service和实体类一样
Handler类
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService){
this.userService = userService;
}
// 根据id查询
public Mono<ServerResponse> getUserById(ServerRequest request){
int id = Integer.valueOf(request.pathVariable("id"));
// null值处理
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
// 调用service方法
Mono<User> userMono = this.userService.getUserById(id);
// 把userMono转换 使用Reactor操作符flatMap
return userMono.flatMap(person ->
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(person)))
.switchIfEmpty(notFound);
}
public Flux<ServerResponse> getUserList(){
Flux<User> allUser = this.userService.getAllUser();
return allUser.flatMap(person ->
ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(person)));
}
public Mono<ServerResponse> getAllUser(){
Flux<User> allUser = this.userService.getAllUser();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(allUser,User.class);
}
public Mono<ServerResponse> saveUser(ServerRequest request){
// 得到user对象
Mono<User> userMono = request.bodyToMono(User.class);
Mono<Void> voidMono = this.userService.saveUserInfo(userMono);
return ServerResponse.ok().build(voidMono);
}
}
Server类
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();
}
// 1. 创建Router路由
public RouterFunction<ServerResponse> routerFunction(){
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService);
return RouterFunction.route(GET("/user/getId/{id}").and(accept(APPLICATION_JSON)),userHandler::getUserById)
.andRoute(GET("/user/list").and(accept(APPLICATION_JSON)),userHandler::getAllUser)
.andRoute(GET("/user/list1").and(accept(APPLICATION_JSON)),userHandler::getUserList);
}
// 创建服务器完成适配
public void createReactorServer(){
// 路由和handler适配
RouterFunction<ServerResponse> serverResponseRouterFunction = routerFunction();
HttpHandler httpHandler = toHttpHandler(serverResponseRouterFunction);
ReactorHttpHandlerAdapter reactorHttpHandlerAdapter = new ReactorHttpHandlerAdapter(httpHandler);
// 创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(reactorHttpHandlerAdapter).bindNow();
}
}
Client类
public class Client {
public static void main(String[] args) {
WebClient webClient = WebClient.create("http://localhost:5794");
// 根据id查询
User block = webClient.get().uri("/user/getId/{id}", 1).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
System.out.println(block);
}
}