文章目录
Spring基本使用
版本:spring 5.2.4
1 IOC容器
xml解析,工厂模式,反射
1.1 BeanFactory和ApplicationContext
BeanFactory
- 比较底层,不是给开发人员用的。
- 初始化过程并不加载bean,而是用到的时候才加载。
ApplicationContext
- 功能梗多更全面,适合开发人员用。
- 在初始化的过程中一次性将注册的所有bean加载。
2 Bean管理
bean管理:1.Spring创建对象 2.Spring注入属性 也就是实例化和初始化。
2.1 XMl方式
注册对象:
<bean id="user" class="com.bean.User"></bean>
id:唯一标识符
class:全限定类名
XMl创建对象默认执行无参构造方法——–因为newInstance()中是没有参数的。
注入属性:
1.set方法注入
Book类
public class Book {
private String bookName;
private String bookAuthor;
public void setBookName(String bookName) {
this.bookName = bookName;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
}
需要有属性的set方法:
<bean id="book" class="com.bean.Book">
<property name="bookName" value="java"></property>
<property name="bookAuthor" value="dogLi"></property>
</bean>
2.有参构造方法注入
要显示声明无参构造函数和有参构造函数。
<bean id="book1" class="com.bean.Book">
<constructor-arg name="bookName" value="java"></constructor-arg>
<constructor-arg name="bookAuthor" value="dogLi"></constructor-arg>
<!--<constructor-arg index="0" value="java"></constructor-arg> 索引值注入-->
</bean>
字面量:
- null
<property name="bookAuthor"><null></null></property>
- 特殊符号
- 将特殊符号进行转义,比如<
- CDATA写法
<property name="bookName">
<value><![CDATA[<<JAVA>>]]></value>
</property>
外部bean
类:
public class UserDao {
public void add(){
System.out.println("add user...");
}
}
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void addUser(){
userDao.add();
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
<bean id="userService" class="com.service.UserService">
<!--<property name="userDao" ref="userDao"></property>-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
内部bean-级联赋值
实例:一对多关系
public class Department {
private String name;
private List<Clerk> clerkList;
public void setName(String name) {
this.name = name;
}
public void setClerkList(List<Clerk> clerkList) {
this.clerkList = clerkList;
}
}
public class Clerk {
private String name;
private String gender;
private Department department;
public void setGender(String gender) {
this.gender = gender;
}
public void setDepartment(Department department) {
this.department = department;
}
public void setName(String name) {
this.name = name;
}
}
<bean id="clerk" class="com.bean.Clerk">
<property name="name" value="Ming"></property>
<property name="gender" value="male"></property>
<property name="department">
<bean id="department" class="com.bean.Department">
<property name="name" value="Security"></property>
</bean>
</property>
</bean>
<!-- 外部bean 级联写法-->
<bean id="clerk" class="com.bean.Clerk">
<property name="name" value="Ming"></property>
<property name="gender" value="male"></property>
<property name="department" ref="dept"></property>
</bean>
<bean id="dept" class="com.bean.Department">
<property name="name" value="money"></property>
</bean>
<!-- 另一种神奇写法,好麻烦-->
<bean id="clerk" class="com.bean.Clerk">
<property name="name" value="Ming"></property>
<property name="gender" value="male"></property>
<property name="department" ref="dept"></property>
<!-- Clerk类中需要有department的getter -->
<property name="department.name" value="ceo"></property>
</bean>
<bean id="dept" class="com.bean.Department"></bean>
注入集合属性:
数组、List、Map、Set、对象类型的集合
Student类
public class Student {
private String[] courses;
private List<String> interest;
private List<Course> courseList;
private Map<String, Integer> score;
private Set<String> set;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setInterest(List<String> interest) {
this.interest = interest;
}
public void setScore(Map<String, Integer> score) {
this.score = score;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
}
<bean id="student" class="com.bean.Student">
<property name="courses">
<array>
<value>Chinese</value>
<value>Math</value>
<value>English</value>
</array>
</property>
<property name="interest">
<list>
<value>PingPong</value>
<value>Football</value>
</list>
</property>
<property name="score">
<map>
<entry key="Chinese" value="80"></entry>
<entry key="Math" value="150"></entry>
<entry key="English" value="100"></entry>
</map>
</property>
<property name="set">
<set>
<value>Mysql</value>
<value>Mybatis</value>
</set>
</property>
<property name="courseList" >
<list>
<ref bean="spring"></ref>
<ref bean="java"></ref>
</list>
</property>
</bean>
<bean id="spring" class="com.bean.Course">
<property name="name" value="spring"></property>
</bean>
<bean id="java" class="com.bean.Course">
<property name="name" value="java"></property>
</bean>
提取集合注入部分:
名称空间引入:
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
使用util标签实现集合部分注入
<util:list id="courses">
<value>Chinese</value>
<value>Math</value>
</util:list>
<util:list id="courseList">
<ref bean="spring"></ref>
<ref bean="java"></ref>
</util:list>
<!-- 引入 -->
<property name="courses" ref="courses"></property>
自动装配:
根据指定的装配规则(属性名称or属性类型),系统自动将匹配的属性值注入。
引入外部属性文件:演示-配置连接池
1.添加命名空间
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
2.创建外部文件
jdbc.properties 属性文件格式 key=value 不用加双引号
3.引入外部文件
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
4.用占位符表示属性
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClassName}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.username}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
2.1 FactoryBean
Spring有两种bean,普通bean和工厂bean
1.普通bean:在配置文件中定义的bean类型就是返回类型。
2.工厂bean:在配置文件中定义的bean类型可以和返回类型不一样。
public class MyBean implements FactoryBean<Course> {
//指定返回的类型
@Override
public Course getObject() throws Exception {
Course course = Course.class.getDeclaredConstructor().newInstance();
course.setName("java");
return course;
}
@Override
public Class<?> getObjectType() {
return Course.class;
}
@Override
public boolean isSingleton() {
return false;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Course course = (Course) context.getBean("myBean", Course.class);
System.out.println(course);
}
}
<bean id="myBean" class="com.factorybean.MyBean"></bean>
//course::java
2.3 bean的作用域
在Spring里面可以设置创建的实例是单实例还是多实例的,在默认情况下是单实例对象。(工厂bean的对象不是单实例的)
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Student s1 = (Student) context.getBean("student");
Student s2 = (Student) context.getBean("student");
Course course1 = (Course) context.getBean("myBean", Course.class);
Course course2 = (Course) context.getBean("myBean", Course.class);
System.out.println(course1 == course2);
System.out.println(s1 == s2);
}
//false
//true
设置为多实例:
设置bean标签里的scope属性
- 默认值:singleton 单实例
- prototype 多实例 原型模式 【在getBean的时候才创建对象,并且是深拷贝】
2.4 bean的生命周期
从对象创建到销毁的过程。
1.通过构造器创建bean实例(无参构造)
2.为bean设置属性值或引用其它属性(调用set方法)
3.把bean传给后置处理器:postProcessBeforeInitialization
4.调用bean的初始化方法(需要进行配置)
5.把bean传给后置处理器:postProcessAfterInitialization
6.使用bean
7.容器关闭的时候,销毁bean(需要配置销毁的方法)
演示
配置文件:
<bean id="user" class="com.bean.User" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="liang"></property>
</bean>
<bean id="myBeanPost" class="com.factorybean.MyBeanPost"></bean>
代码
public class User {
private String name;
private int age;
public User() {
System.out.println("第一步 调用无参构造方法");
}
public void setName(String name) {
this.name = name;
System.out.println("第二部 调用setter");
}
public void initMethod(){
System.out.println("第三步 调用初始化方法");
}
public void useMethod(){
System.out.println("第四步 使用bean");
}
public void destroyMethod(){
System.out.println("第五步 调用销毁方法");
}
}
后置处理器:
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization");
return bean;
}
}
测试代码
//测试代码
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
User user = (User)context.getBean("user");
user.useMethod();
context.close();//手动关闭容器
}
//结果
第一步 调用无参构造方法
第二步 调用setter
postProcessBeforeInitialization
第三步 调用初始化方法
postProcessAfterInitialization
第四步 使用bean
第五步 调用销毁方法
2.5 注解方式
@Component(value=“beanName”) 通用
@Service service层
@Controller controller层
@Repository dao层
上面四个注解的效果是一样的,都可以创建bean。value如果不写,默认值是类的名称首字母小写。
使用:
1.引入包:aop
2.开启组件扫描
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="com"></context:component-scan>
如果要扫描多个包,包之间用逗号隔开,或者直接写上层目录。
一些细节:
<!-- 只扫描component注解-->
<context:component-scan base-package="com" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
<!-- 不扫描component注解-->
<context:component-scan base-package="com">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
自动注入:
@AutoWired:根据属性类型进行注入
@Qualifiers(value=“beanName”):根据属性名称进行注入。必须和@AutoWired一起使用。
@Resources:可以根据类型注入,也可以根据属性注入
@Value:注入普通类型属性。可以用来读取配置文件等。https://blog.csdn.net/chuang504321176/article/details/80672740
2.5.1 纯注解开发
1.创建一个配置类:@Configuration
2.开启组件扫描:@ComponentScan(basePackages = {" “,” "})
3.编写测试类:
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
3 AOP
3.1 概念
面向切面编程:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。通俗解释,在不修改源代码的基础上,在主干上增加新的模块。
底层原理
使用动态代理实现。有两种情况的动态代理:
(1)有接口:使用JDK动态代理
创建接口实现类代理对象,增强实现类中的方法。
(2)没有接口:使用CGlib动态代理
创建子类的代理对象,完成增强。
一些术语
- 连接点:类里面哪些方法可以被增强,这些方法称为连接点。
- 切入点:实际被增强的方法。
- 通知(增强):实际增强的逻辑部分,叫通知。
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
- 切面:是动作,把通知应用到切入点的过程。
3.2 JDK动态代理
使用proxy类中的方法实现创建代理对象。
//java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数解析:1.类加载器 2.增强方法所在的类实现的接口,可以有多个接口 3.实现InvocationHandler,编写增强的逻辑。
代码实例
1.创建接口和实现类
public interface UserDao {
public int add(int a, int b);
public void update(String id);
}
//
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public void update(String id) {
System.out.println("updated " + id);
}
}
2.创建代理类
class MyProxy implements InvocationHandler {
//要把被代理的对象传进来,一般利用有参构造方法
private Object obj;
public MyProxy(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法执行前
System.out.println("方法执行前::方法名= " + method.getName() + ", 参数= "+ Arrays.toString(args));
Object result = method.invoke(obj,args);
//方法执行后
System.out.println("方法执行后::返回值= " + result);
//还可以对返回值进行修改
return result;
}
}
3.调用
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
//获得增强后的类
UserDao dao =(UserDao) Proxy.newProxyInstance(TestDemo.class.getClassLoader(),
new Class[]{UserDao.class},
new MyProxy(userDao)
);
System.out.println(userDao.add(1,1));
dao.add(1,1);
}
//result
2
方法执行前::方法名= add, 参数= [1, 1]
方法执行后::返回值= 2
3.3 AOP操作
spring的AOP是基于AspectJ实现的,AspectJ是一个AOP框架。
1.引入依赖
2.切入点表达式作用:知道对哪个类里面的那个方法进行增强
execution([权限修饰符][返回值类型][类全路径].[方法名]([参数列表]))
修饰符可以省略,(…)表示任意参数。
代码示例
1.进行配置,这里用的是配置类
@Configuration //声明配置类
@ComponentScan(basePackages = {"com"}) //包扫描
@EnableAspectJAutoProxy //开启自动代理
public class MyConfig {}
2.编写被增强类和增强类,注意用到的注解
在增强类的通知方法上加上通知类型注解,用切入点表达式。
//被增强的类
@Component
public class User {
public int add(int a, int b){
return a+b;
}
}
//增强类
@Component
@Aspect
public class UserProxy {
//前置通知
@Before(value = "execution(* com.bean.User.add(..))")
public void Before(){
System.out.println("Before...");
}
@After(value = "execution(* com.bean.User.add(..))")
public void After(){
System.out.println("After...");
}
@AfterReturning(value = "execution(* com.bean.User.add(..))")
public void AfterReturning(){
System.out.println("AfterReturning...");
}
@AfterThrowing(value = "execution(* com.bean.User.add(..))")
public void AfterThrowing(){
System.out.println("AfterThrowing...");
}
@Around(value = "execution(* com.bean.User.add(..))")
public Object Around(ProceedingJoinPoint point) throws Throwable {
System.out.println("Around::Before...");
Object result = point.proceed();
System.out.println("Around::After...");
return result;
}
}
//结果
Around::Before...
Before...
user::add
AfterReturning...
After...
Around::After...
//在有异常的情况下
Around::Before...
Before...
AfterThrowing...
After...
注意异常的顺序:
重用切入点定义:
@Pointcut("execution(* com.bean.User.add(..))")
public void UserProxy(){}
//前置通知
@Before("UserProxy()")
public void Before(){
System.out.println("Before...");
}
多个增强类指定优先级顺序:
1.在增强类上加注解@Order(数值类型),数值越小优先级越高。
增加一个增强类,设置优先级比原来的增强类高,观察输出:
Person::Around::Before...
Person::Before...
User::Around::Before...
User::Before...
user::add
User::AfterReturning...
User::After...
User::Around::After...
Person::AfterReturning...
Person::After...
Person::Around::After...
注意到是类似包裹式的执行。
4 jdbcTemplate
4.1 介绍
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。JdbcTemplate处理了资源的建立和释放。他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果。
准备工作:
1.引入依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.24</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
2.配置dataSource
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/user?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false"></property>
<property name="username" value="root"></property>
<property name="password" value="123123"></property>
</bean>
这部分就看看,不做笔记了,估计将来用的都是mybatis了
5 事务
数据库操作的最基本单元,指的是一组操作,这一组操作要么都成功要么都失败。
事务一般添加到三层中的service层。
两种方式:编程式和声明式,一般使用声明式。
在Spring使用声明式事务管理,底层是用AOP实现的。
事务管理API:
1.提供一个接口,代表事务管理器,针对不同的框架提供了不同的实现类。
配置管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2.开启事务注解
引入tx命名空间
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
3.在service类上或方法上添加事务注解@Transactional
- 在类上表示类中所有方法添加上事务
propagation:事务的传播行为。多事务方法直接进行调用,这个过程中事务是如何管理的。
事务方法:对数据库表数据进行变更的操作。
PROPAGATION_REQUIRED 支持当前事务,假设当前没有事务。就新建一个事务
PROPAGATION_SUPPORTS 支持当前事务,假设当前没有事务,就以非事务方式运行
PROPAGATION_MANDATORY 支持当前事务,假设当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW 新建事务,假设当前存在事务。把当前事务挂起
PROPAGATION_NOT_SUPPORTED 以非事务方式运行操作。假设当前存在事务,就把当前事务挂起
PROPAGATION_NEVER 以非事务方式运行,假设当前存在事务,则抛出异常
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
isolation:事务的隔离级别:保证多事务操作之间不会产生影响。
https://www.cnblogs.com/xrq730/p/5087378.html
脏读:一个未提交事务读到了另一个未提交事务修改的数据。
不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(一个未提交事务读到了另一个已提交事务修改的数据。)
幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。(一个未提交事务读到另一个提交事务添加的数据)
timeout:超时时间:事务需要在一定时间内提交,如果不提交就回滚。默认为-1;设置时间以秒为单位。
readyOnly:是否只读:只允许查询不可以更新。默认为false;
rollbackFor:回滚:设置出现哪些异常进行回滚。
noRollbackFor:不回滚:设置出现哪些异常不进行回滚。
完全注解声明:
配置类:注解EnableTransactionManagement,以及通过@Bean
@Configuration
@ComponentScan(basePackages = {"com"})
@EnableTransactionManagement
public class MyConfig {
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriver();
dataSource.setUrl();
dataSource.setDriver();
return dataSource;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
Spring5新特性
5.1 日志
整个框架基于java8;自带通用的日志封装;移除了Log4jConfigListener,官方建议使用Log4j2;
如何整合Log4j2?
1.引入依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.0-beta9</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.0-beta9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
2.写配置文件
3.手动添加log
private static final Logger log = LoggerFactory.getLogger(TestDemo.class);
log.info();
log.debug();
5.2 @Nullable和函数式注册
@Nullable可以用在方法,属性,参数上,表示方法返回值,属性,参数可以为空。用处:不需要重载多个方法,可以将多种用法封装在一个方法里。
5.3 Spring Webflux
介绍:Spring5添加的新模块,用于web开发的,与Springmvc类似的,使用当前比较流行的响应式编程。
使用传统的web框架比如Springmvc,是基于servlet容器的,而Webflux是一种异步非阻塞的框架,是在servle3.1之后才支持的。Webflux核心是基于Reactor相关的API实现的。
什么是异步非阻塞?
同步与异步:调用者发送请求后,必须等待对方回应才进行下一步就是同步。
阻塞与非阻塞:收到请求后立马进行反馈,然后才去做任务就是非阻塞。做完请求任务之后才进行反馈就是阻塞。
特点:
- 非阻塞式:在有限的资源下提高系统的吞吐量和伸缩性。
- 函数式编程:更简洁
- 和SpringMVC比较
- 都采用注解,运行在tomcat等容器中。
- SpringMVC采用命令式编程,Webflux采用响应式编程。
响应式编程:响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
响应式编程(Reactor实现)
满足一种规则:reactive规范。Reactor就是满足这种规则的框架;
Reactor两个核心类:Mono和Flux,这两个类都实现了Publisher接口,提供了丰富的操作符;
- Flux实现发布者,返回N个元素。
- Mono也可以实现发布者,返回0或者一个元素。
Flux和Mono都是数据流的发布者,使用它们可以发出三种数据信号:元素值,错误信号,完成信号。其中错误信号,完成信号都是终止信号,告诉订阅者数据流已经结束了。错误信号可以终止数据流同时把错误信息传给订阅者。
-
错误信号的完成信号不能共存。
-
如果没有发送任何元素值而是直接发送错误/完成信号,表面是空的数据流;如果没有终止信号,说明是无限数据流;
-
调用just或者其他方法只是声明数据流,数据流并没有发出,只有订阅之后才会触发数据流。
-
操作符:对数据流进行一道道操作,成为操作符。
- map:元素映射成新元素
- flatmap:数据映射成流
SpringWebFlux执行流程和API:
默认容器是netty,netty是一个高性能的NIO框架。
SpringWebFlux执行流程和SpringMVC类似,核心控制器是dispatchHandler,实现接口WebHandler。