Spring

  • Spring概念
  • IOC容器
    • IOC底层原理
    • IOC接口
    • IOC操作Bean管理
      • 创建对象和注入属性(xml方式)
      • FactoryBean工厂bean
      • Bean的作用域
      • Bean的声明周期
      • Bean的自动装配
      • 引入外部属性文件
      • 创建对象和注入属性(注解实现)
  • AOP
    • AOP底层原理
    • AOP操作
  • JdbcTemplate
  • 事务管理
    • 环境搭建:实现转账的操作
    • Spring事务管理
  • Spring5新特性
    • 日志框架整合
    • @Nullable注解和函数式风格
    • JUnit5测试
    • WebFlux

Spring概念

Spring框架是一个轻量级的开源的JavaEE框架。
Spring可以解决企业应用开发的复杂性。

Spring有两个核心部分:IOC和AOP

  • IOC:控制反转,把创建对象的过程交给Spring
  • AOP:面向切面,不修改源代码的情况下进行功能增强

Spring特点

  1. 方便解耦,简化开发
  2. AOP编程支持
  3. 方便程序测试
  4. 方便和其他框架进行整合

入门案例
第一步,下载jar包(GA表示稳定版本)

下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/

第二步,创建普通的java项目

第三步、导入相关jar包


第四步,创建普通类

第五步,创建spring配置文件,在配置文件配置对象的创建

第六步,测试

@Test
public void testAdd() {//1 加载spring配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");//2 获取配置创建的对象User user = context.getBean("user", User.class); //参数"user"是配置文件中的idSystem.out.println(user);user.add();
}

IOC容器


IOC底层原理

控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理。

使用IOC目的,是为了降低耦合度。

工厂模式:解耦合

IOC过程解耦合,通过 xml解析 + 反射 实现

IOC接口

IOC思想基于IOC容器完成,IOC容器底层就是对象工厂。

Spring提供IOC容器两种实现方式:(两个接口)

  • BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用。加载配置文件的时候不会创建对象,在获取对象使用时才去创建
  • ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件的时候就会把在配置文件对象进行创建

ApplicationContext接口有两个实现类,区别是ClassPathXmlApplicationContext的参数写xml的文件名,而FileSystemXmlApplicationContext的参数是写磁盘路径。

IOC操作Bean管理

创建对象和注入属性(xml方式)

Bean管理指的是两个操作:

  • Spring创建对象
  • Spring注入属性

Bean管理操作有两种方式:

  • 基于xml配置文件方式实现
  • 基于注解方式实现

基于xml方式创建对象

在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建。

在bean标签有很多属性,常用属性:

  • id属性:唯一标识
  • class属性:类全路径(包类路径)

创建对象的时候,默认也是执行无参数构造方法完成对象创建。

基于xml方式注入属性

DI:依赖注入,就是注入属性
IOC和DI有什么区别:DI是IOC中的具体实现,DI表示依赖注入或注入属性,注入属性要在创建对象的基础之上完成。

  • 第一种注入方式:使用set方法进行注入

    public class Book {private String bookName;private String bAuthor;// set方法注入public void setBookName(String bookName) {this.bookName = bookName;}public void setbAuthor(String bAuthor) {this.bAuthor = bAuthor;}
    }
    
    <!--set方法注入属性-->
    <bean id="book" class="com.company.spring5.Book"><!--使用property完成属性注入name:类里面属性名称value:向属性注入的值--><property name="bookName" value="白鹿原"></property><property name="bAuthor" value="陈忠实"></property>
    </bean>
    
  • 第二种注入方式:使用有参构造进行注入
    public class Order {private String name;private String address;public Order(String name, String address) {this.name = name;this.address = address;}public void test() {System.out.println(name + "--" + address);}
    }
    
    <!--有参构造注入属性-->
    <bean id="order" class="com.company.spring5.Order"><constructor-arg name="name" value="电脑"></constructor-arg><constructor-arg name="address" value="中国"></constructor-arg>
    </bean>
    
  • 第三种注入方式:p名称空间注入,可以简化基于xml配置方式
    第一步,在配置文件中添加p名称空间

    第二步,进行属性注入

    <bean id="book" class="com.company.spring5.Book" p:bookName="平凡的世界" p:bAuthor="路遥">
    </bean>
    

1、xml注入其他类型属性
注入null值和特殊符号

<bean id="book" class="com.company.spring5.Book"><!--设置属性为空--><property name="bookName"><null/></property><!-- 属性值包含特殊符号1. 把<>进行转义 &lt; &gt;2. 把带特殊符号内容写到CDATA--><property name="bAuthor"><value><![CDATA[<<南京>>]]></value></property>
</bean>

2、注入属性-外部bean
(1)创建两个类service类和dao类
(2)在service调用dao里面的方法
(3)在spring配置文件中进行配置

public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void add() {System.out.println("add..............");userDao.update();}
}
public class UserDaoImpl implements UserDao {@Overridepublic void update() {System.out.println("update...........");}
}

3、注入属性-内部bean和级联赋值
(1)一对多关系:部门和员工。一个部门有多个员工。
(2)在实体类之间表示一对多关系。

//部门类
public class Dept {private String dname;public void setDname(String dname) {this.dname = dname;}
}
//员工类
public class Emp {private String ename;private String gender;//员工属于某一个部门,使用对象形式表示private Dept dept;public void setEname(String ename) {this.ename = ename;}public void setGender(String gender) {this.gender = gender;}public void setDept(Dept dept) {this.dept = dept;}public void test() {System.out.println(ename+"::"+gender+"::"+dept);}
}

内部bean注入

级联赋值注入

  • 第一种写法
  • 第二种写法
    前提是Emp的dept属性要加上get方法,不然dept.dname获取不到。

4、注入集合类型属性
(1)注入数组类型属性
(2)注入List集合类型属性
(3)注入Map集合类型属性

public class Student {// 数组类型private String[] courses;//list集合类型private List<String> list;//map集合类型private Map<String, String> maps;//编写set方法
}

list集合中存放对象类型

public class Student {private List<Course> courseList;public void setCourseList(List<Course> courseList) {this.courseList = courseList;}
}

把集合注入部分提取出来,让别人也能使用这个集合
(1)在spring配置文件中引入名称空间util

(2)使用util标签完成list集合注入提取

FactoryBean工厂bean

Spring有两种类型bean,一种普通bean,另一种工厂bean

  • 普通bean:在配置文件中定义bean类型就是返回类型

  • 工厂bean:在配置文件定义bean类型可以和返回类型不一样
    第一步,创建类,这个类作为工厂bean,实现接口FactoryBean

    第二步,实现接口里面的方法,在实现的方法中定义返回的bean类型

Bean的作用域

在Spring中,设置创建bean实例默认是单实例对象。

在spring配置文件bean标签里面有属性scope用于设置单实例还是多实例。

scope属性值
singleton,表示单实例对象。
prototype,表示多实例对象。

singleton和prototype的区别

  • 设置scope值是singleton的时候,加载spring配置文件就会创建单实例对象
  • 设置scope值是prototype的时候,在调用getBean方法创建多实例对象。

Bean的声明周期

生命周期:从对象创建到对象销毁的过程。

bean的生命周期:
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)调用bean的初始化方法(配置初始化方法)
(4)使用bean(对象获取到了)
(5)当容器关闭,调用bean的销毁方法(需要配置销毁的方法)

如果加上后置处理器,有七个步骤:
(1)通过构造器创建bean实例(无参构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)把bean实例传递bean后置处理器的方法
(4)调用bean的初始化方法(配置初始化方法)
(5)把bean实例传递bean后置处理器的方法
(6)使用bean(对象获取到了)
(7)当容器关闭,调用bean的销毁方法(需要配置销毁的方法)

实现:创建类实现接口BeanPostProcessor,创建后置处理器

public class MyBeanPost implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("在初始化之前执行的方法");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("在初始化之后执行的方法");return bean;}
}


配置好后,会为所有对象配置上后置处理器,分别在调用bean初始化之前和初始化之后执行。

Bean的自动装配

自动装配:指 Spring 容器在不使用 和 标签的情况下,可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。


注意,使用byType注入,相同类型的bean不能定义多个

引入外部属性文件

配置数据库信息
(1)配置德鲁伊连接池,引入德鲁伊依赖jar包
(2)引入外部属性文件配置数据库连接池

(3)把外部properties属性文件引入到Spring配置文件
引入名称空间

引入外部属性文件,用标签赋值

创建对象和注入属性(注解实现)

Spring针对Bean管理中创建对象提供注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四个注解功能是一样的,都可以用来创建bean实例。

基于注解方式实现对象创建

第一步,引入依赖

第二步,开启组件扫描,看哪个类上有注解,有注解就进行创建

第三步,创建类,在类上面添加创建对象注解
注解中的value属性可以省略不写,不写默认值是类名首字母小写(StudentService —— studentService)

@Test
public void test2() {ApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");StudentService studentService = context.getBean("studentService", StudentService.class);studentService.add();
}

组件扫描配置
扫描包下所有加了注解的类

use-default-filters:表示是否使用默认filter,false表示使用自己配置的filter
context:include-filter:设置扫描哪些内容

context:exclude-filter:表示设置哪些内容不进行扫描

基于注解方式实现属性注入
(1)@Autowired:根据属性类型进行自动装配
第一步,把service和dao对象创建,在service和dao类添加注解
第二步,在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解

(2)@Qualified:根据属性名称进行注入
@Qualified 注解要和 @Autowired 一起使用。
@Qualified表示以名称注入,@Autowired表示以类型注入,如果一个接口有多个实现类,那么要用@Qualified指定名称进行注入。

(3)@Resource:可以根据类型注入,可以根据名称注入
@Resource:根据类型进行注入
@Resource(name=“userDaoImpl1”):根据名称进行注入

(4)@Value:注入普通类型属性

@Value(value="abc")
private String name;

完全注解开发

AOP


面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
通俗描述:不通过修改源码方式,在主干功能里面添加新功能。

AOP底层原理

AOP底层使用动态代理。

  • 第一种,有接口的情况,使用JDK动态代理
  • 第二种,没有接口的情况,使用CGLIB动态代理

JDK动态代理的实现:使用Proxy类里面的方法创建代理对象
现在想要增强这个add方法

调用 newProxyInstance方法,方法有三个参数:

  • 第一个参数:类加载器
  • 第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口
  • 第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强方法

    执行

AOP操作

术语:

  1. 连接点:类中可以被增强的方法,称为连接点。
  2. 切入点:实际被真正增强的方法,称为切入点。
  3. 通知(增强):实际增强的逻辑部分,称为通知。
    通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知。
  4. 切面:是一个动作,把通知应用到切入点的过程。

Spring框架一般基于AspectJ实现AOP操作。AspectJ不是Spring组成部分,一般把AspectJ和Spring框架一起使用,进行AOP操作。

引入依赖

切入点表达式
利用切入点表达式,可以让Spring知道对哪个类里面的哪个方法进行增强。
语法:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])

举例1:对 com.atguigu.dao.BookDao类里面的add进行增强
execution(* com.atguigu.dao.BookDao.add(..))

举例2:对 com.atguigu.dao.BookDao类里面所有的方法进行增强
execution(* com.atguigu.dao.BookDao.*(..))

举例3:对 com.atguigu.dao包里面所有的类,类里面所有的方法进行增强
execution(* com.atguigu.dao.*.*(..))

进行通知的配置
(1)在Spring配置文件中,开启注解扫描

(2)使用注解创建User和UserProxy对象

(3)在增强类上面添加注解@Aspect

(4)在Spring配置文件中开启生成代理对象
如果找到类上有@Aspect注解,将这个类生成代理对象。

AspectJ注解:配置不同类型的通知
在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置。

测试:执行被增强的方法

结果

配置其他类型通知

测试,此时没有执行异常通知

我们给被增强的方法中加一个异常

结果

相同的切入点抽取

有多个增强类对同一个方法进行增强,设置增强类的优先级
在增强类上面添加注解@Order(数字类型值)

PersonProxy优先级高,先进行增强

完全注解开发

配置文件实现AOP

<bean id="user" class="com.company.spring5.aop.User"></bean>
<bean id="userProxy" class="com.company.spring5.aop.UserProxy"></bean><!--配置aop增强-->
<aop:config><!--切入点--><aop:pointcut id="p" expression="execution(* com.company.spring5.aop.User.add(..))"/><!--配置切面--><aop:aspect ref="userProxy"><!--增强作用在具体方法上--><aop:before method="before" pointcut-ref="p"/></aop:aspect>
</aop:config>

JdbcTemplate


JdbcTemplate:Spring框架对JDBC进行的封装,使用JdbcTemplate方便实现对数据库操作。

1. 引入依赖

2. 配置数据库连接池

3. 配置JdbcTemplate对象,注入DataSource

<!--JdbcTemplate对象-->
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--注入dataSource--><property name="dataSource" ref="dataSource"></property>
</bean>

4. 创建类和接口

5. 创建数据库表和对应实体类

用JdbcTemplate进行添加操作

用update方法进行添加操作,方法有两个参数:
第一个参数:sql语句
第二个参数:可变参数,sql语句的参数

dao层

@Override
public void add(Book book) {String sql = "insert into t_book values (?,?,?)";Object[] args = {book.getUserId(), book.getUsername(), book.getUstatus()};int update = jdbcTemplate.update(sql, args);System.out.println(update);
}

service层

//添加
public void addBook(Book book) {bookDao.add(book);
}

测试

@Test
public void test1() {ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");BookService bookService = context.getBean("bookService", BookService.class);Book book = new Book();book.setUserId("1");book.setUsername("java");book.setUstatus("有效");bookService.addBook(book);
}

用JdbcTemplate进行修改操作

@Override
public void updateBook(Book book) {String sql = "update t_book set username = ?, ustatus = ? where user_id = ?";Object[] args = {book.getUsername(), book.getUstatus(),book.getUserId()};int update = jdbcTemplate.update(sql, args);System.out.println(update);
}

用JdbcTemplate进行删除操作

@Override
public void deleteBook(String id) {String sql = "delete from t_book where user_id = ?";int update = jdbcTemplate.update(sql, id);System.out.println(update);
}

用JdbcTemplate进行查询操作
查询表里有多少条记录,返回某个值

第一个参数:sql语句
第二个参数:返回类型Class

@Override
public int selectCount() {String sql = "select count(*) from t_book";Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);return integer;
}

查询返回对象

有三个参数:
第一个参数:sql语句
第二个参数:RowMapper,接口,针对返回不同类型的数据,使用这个接口里面的实现类完成封装
第三个参数:sql语句的参数

@Override
public Book findBookInfo(String id) {String sql = "select * from t_book where user_id = ?";Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Book.class), id);return book;
}

查询返回集合

@Override
public List<Book> findAllBook() {String sql = "select * from t_book";List<Book> books = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Book.class));return books;
}

JdbcTemplate批量操作数据

//批量添加
@Override
public void batchAdd(List<Object[]> batchArgs) {String sql = "insert into t_book values (?,?,?)";int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);System.out.println(Arrays.toString(ints));
}
@Test
public void test2() {ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");BookService bookService = context.getBean("bookService", BookService.class);List<Object[]> batchArgs = new ArrayList<>();Object[] o1 = {"3", "java", "a"};Object[] o2 = {"4", "c++", "b"};Object[] o3 = {"5", "Mysql", "c"};batchArgs.add(o1);batchArgs.add(o2);batchArgs.add(o3);bookService.batchAdd(batchArgs);
}

事务管理


事务:数据库操作最基本单元,逻辑上是一组操作,要么同时成功,要么同时失败,如果有一个失败所有操作都失败。

环境搭建:实现转账的操作

1. 创建数据库表,添加记录

2. 创建service,搭建dao,完成对象创建和注入关系


3. 编写dao的两个方法,加钱和减钱

//减钱
@Override
public void addMoney() {String sql = "update t_account set money = money - ? where username = ?";jdbcTemplate.update(sql, 100, "lucy");
}
//加钱
@Override
public void reduceMoney() {String sql = "update t_account set money = money + ? where username = ?";jdbcTemplate.update(sql, 100, "mary");
}

4. 在service层调用这两个方法,实现转账

//转账的方法
public void accountMoney() {personDao.reduceMoney();personDao.addMoney();
}

上面代码,如果正常执行没有问题,但是如果代码执行过程中出现异常,则有问题。

Spring事务管理

事务应该添加到三层结构中的Service层(业务逻辑层)

Spring进行事务管理操作有两种方式

  • 编程式事务

    try {//第一步,开启事务//第二步,进行业务操作personDao.reduceMoney(); // lucy少100//可能出现异常int a = 1/0;personDao.addMoney(); // mary多100//第三步,没有发生异常,提交事务} catch (Exception e) {//第四步,出现异常,事务回滚
    }
    
  • 声明式事务管理
    • 基于注解方法
    • 基于xml配置文件方式

Spring的声明式事务管理,底层使用了AOP原理。

Spring事务管理提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类。

Spring声明式事务注解实现
1、在Spring配置文件配置事务管理器

<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
</bean>

2、在spring配置文件中,开启事务注解
(1)在Spring配置文件引入名称空间tx

(2)开启事务的注解

<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3、在service类上面添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加方法到方法上
(2)如果把这个注解添加在类上,这个类所有的方法都添加事务
(3)如果在方法上添加注解,只是给这个方法添加事务

声明式事务管理参数配置
1、propagation:事务传播行为
当一个事务方法被另外一个事务方法调用的时候,这个事务方法如何进行。

  • REQUIRED:如果当前(上层调用者方法)没有事务,则自己新建一个事务,如果当前(上层调用者方法)存在事务,则加入这个事务。spring默认使用该传播类型。
  • REQUIRES_NEW:无论当前有没有事务,都会新建事务
  • SUPPORTS:如果有事务在运行,当前方法就在这个事务内运行,否则它可以不运行在事务中。

2、isolation:事务隔离级别
有三个读问题:脏读、不可重复读、幻读。
解决:通过设置事务隔离级别,解决读问题。

3、其他参数

  • timeout:超时时间,事务需要在一定时间内进行提交,如果不提交进行回滚。默认值为-1,时间以秒单位进行计算。
  • readOnly:是否只读。默认值为false,表示可以查询,可以增删改。设置为true之后,只能查询。
  • rollbackFor:回滚。设置出现哪些异常进行事务回滚。
  • noRollbackFor:不回滚。设置出现哪些异常不进行事务回滚。

事务操作(XML声明式事务管理)
第一步,配置事务管理器

第二步,配置通知

第三步,配置切入点和切面

<!--1 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入数据源--><property name="dataSource" ref="dataSource"></property>
</bean><!--2 配置通知-->
<tx:advice id="txadvice"><!--配置事务参数--><tx:attributes><!--指定哪种规则的方法上面添加事务--><tx:method name="accountMoney" propagation="REQUIRED"/></tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config><!--配置切入点--><aop:pointcut id="pt" expression="execution(* com.company.spring5.service.PersonService.*(..))"/><!--配置切面--><aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

完全注解配置

@Configuration //配置类
@ComponentScan(basePackages = "com.company")  //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {//创建数据库连接池@Beanpublic DruidDataSource getDruidDataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/db4?useSSL=false&characterEncoding=utf-8");dataSource.setUsername("root");dataSource.setPassword("qwer`123");return dataSource;}//创建JdbcTemplate对象@Beanpublic JdbcTemplate getJdbcTemplate(DataSource dataSource) {//到ioc容器中根据类型找到dataSourceJdbcTemplate jdbcTemplate = new JdbcTemplate();//注入dataSourcejdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//创建事务管理器@Beanpublic DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
}

Spring5新特性


日志框架整合

Spring5框架自带了通用的日志封装。

Spring5移除了Log4jConfigListener,官方建议使用Log4j2.

第一步,引入依赖

第二步,创建log4j2.xml(文件名固定)

<?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>

@Nullable注解和函数式风格

@Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空。

(1)注解用在方法上面,方法返回值可以为空。

(2)注解用在方法参数里面,方法参数可以为空。

(3)注解使用在属性上面,属性值可以为空。

函数式风格GenericApplicationContext
Spring5核心容器支持函数式风格创建对象,交给Spring进行管理。

@Test
public void test5() {//1 创建GenericApplicationContext对象GenericApplicationContext context = new GenericApplicationContext();//2 调用context的方法对象注册context.refresh();context.registerBean("user1", User.class, ()->new User());//3 获取在spring注册的对象User user = (User) context.getBean("user1");System.out.println(user);
}

JUnit5测试

Spring5整合Junit5
第一步,添加依赖

第二步,测试

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean7.xml")
public class Test5 {@Autowiredprivate PersonService personService;@Testpublic void test() {personService.accountMoney();}
}

这样省去了之前创建ApplicationContext对象和getBean的操作。

简化,使用一个复合注解替代上面两个注解

WebFlux

1. SpringWebflux介绍
(1)WebFlux是Spring5添加的新模块,用于web开发的,功能和SpringMVC类似的,WebFlux使用当前一种比较流行的响应式编程出现的框架。
(2)使用传统web框架,比如SpringMVC,这些基于是Servlet容器的,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。

(3)Webflux的特点:异步非阻塞,在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程;函数式编程,Spring5框架基于Java8,Webflux使用java8函数式编程方式实现路由请求。
(4)比较SpringMVC:两个框架都可以使用注解方式,都运行在Tomcat等容器;SpringMVC采用命令式编程,Webflux采用异步响应式编程。

2. 响应式编程
什么是响应式编程:
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
例如,对于 a=b+c 这个表达式的处理,在命令式编程中,会先计算 b+c 的结果,再把此结果赋值给 变量a,因此 b,c 两值的变化不会对 变量a 产生影响。但在响应式编程中,变量a 的值会随时跟随 b,c 的变化而变化。

Java实现响应式编程(观察者模式)

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实现)
(1)响应式编程操作中,Reactor是满足Reactive规范框架
(2)Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富的操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或1个元素。
(3)Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者。

<dependency><groupId>io.projectreactor</groupId><artifactId>reactor-core</artifactId><version>3.2.3.RELEASE</version>
</dependency>
public class TestReactor {public static void main(String[] args) {//just方法直接声明Flux.just(1,2,3,4);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或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生。

调用subscribe进行订阅

操作符
对数据流进行一道道操作,称为操作符,比如工厂流水线。
1、map元素映射为新元素

2、flatMap元素映射为流。把每个元素转换为流,把转换后多个流合并成大的流。

3. WebFlux执行流程和核心API
SpringWebFlux是基于Reactor,默认使用容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架。

Netty
BIO

NIO

SpringWebflux执行过程和SpringMVC相似:
SpringWebflux核心控制器DispatcherHandler,实现接口WebHandler,接口WebHandler有一个方法。


SpringWebflux里面DispatcherHandler,负责请求的处理。
HandlerMapping:请求查询到处理的方法
HandlerAdapter:真正负责请求处理
HandlerResultHandler:响应结果处理

SpringWebflux实现函数式编程,两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)

4. SpringWebflux(基于注解编程)
第一步,引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

第二步,创建包和实体类

第三步,编写service接口和实现接口

//用户操作接口
public interface UserService {//根据id查询用户Mono<User> getUserById(int id);//查询所有用户Flux<User> getAllUser();//添加用户Mono<Void> saveUserInfo(Mono<User> user);
}
@Repository
public class UserServiceImpl implements UserService{//创建map集合存储数据private final Map<Integer,User> users = new HashMap<>();public UserServiceImpl() {this.users.put(1, new User("lucy", "nan", 20));this.users.put(2, new User("mary", "nv", 30));this.users.put(3, new User("jack", "nv", 50));}//根据id查询@Overridepublic Mono<User> getUserById(int id) {return Mono.justOrEmpty(this.users.get(id));}//查询多个用户@Overridepublic Flux<User> getAllUser() {return Flux.fromIterable(this.users.values());}//添加用户@Overridepublic Mono<Void> saveUserInfo(Mono<User> user) {return user.doOnNext(person -> {//向集合里面放值int id = users.size() + 1;users.put(id, person);}).thenEmpty(Mono.empty());}
}

第四步,编写controller

@RestController
public class UserController {@Autowiredprivate UserService userService;//id查询@GetMapping("/user/{id}")public Mono<User> getUserId(@PathVariable int id) {return userService.getUserById(id);}//查询所有@GetMapping("/user")public Flux<User> getUsers() {return userService.getAllUser();}//添加@PostMapping("/saveUser")public Mono<Void> saveUser(@RequestBody User user) {Mono<User> userMono = Mono.just(user);return userService.saveUserInfo(userMono);}
}

说明:
SpringMVC:同步阻塞的方式,基于SpringMVC+Servlet+Tomcat
SpringWebflux:异步非阻塞方式,基于SpringWebflux+Reactor+Netty

5. SpringWebflux(基于函数式编程)
(1)在使用函数式编程操作的时候,需要自己初始化服务器。
(2)有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。
(3)SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse。

第一步,创建Handler(具体实现方法)

public class UserHandler {private final UserService userService;public UserHandler(UserService userService) {this.userService = userService;}//根据id查询public Mono<ServerResponse> getUserById(ServerRequest request) {//获取id值int userId = Integer.valueOf(request.pathVariable("id"));//空值处理Mono<ServerResponse> notFound = ServerResponse.notFound().build();//调用service方法得到数据Mono<User> userMono = this.userService.getUserById(userId);//把userMono转换成流返回//使用Reactor操作符flatMapreturn userMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(person))).switchIfEmpty(notFound);}//查询所有public Mono<ServerResponse> getAllUsers() {//调用service得到结果Flux<User> users = this.userService.getAllUser();return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);}//添加public Mono<ServerResponse> saveUser(ServerRequest request) {//得到user对象Mono<User> userMono = request.bodyToMono(User.class);return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));}
}

第二步,初始化服务器,编写Router,创建适配完成服务,完成最终调用

public class Server {public static void main(String[] args) throws Exception {Server server = new Server();//调用server.createReactorServer();System.out.println("enter to exit");System.in.read();}//1 创建Router路由public RouterFunction<ServerResponse> routingFunction() {UserService userService = new UserServiceImpl();UserHandler handler = new UserHandler(userService);return RouterFunctions.route(GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserById).andRoute(GET("/users").and(accept(APPLICATION_JSON)), handler::getAllUsers);}//2 创建服务器完成适配public void createReactorServer() {//路由和handler适配RouterFunction<ServerResponse> route = routingFunction();HttpHandler httpHandler = toHttpHandler(route);ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);//创建服务器HttpServer httpServer = HttpServer.create();httpServer.handle(adapter).bindNow();}
}
public class Client {public static void main(String[] args) {//调用服务器地址WebClient webClient = WebClient.create("http://127.0.0.1:5794");//根据id查询String id = "1";User userResult = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();System.out.println(userResult.getName());//查询所有Flux<User> results = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);results.map(stu -> stu.getName()).buffer().doOnNext(System.out::println);}
}

Spring-尚硅谷-学习笔记相关推荐

  1. Java 基础 第3阶段:高级应用——尚硅谷学习笔记(含面试题) 2023年

    Java 基础 第 3 阶段:高级应用--尚硅谷学习笔记(含面试题) 2023 年 Java 基础 第 3 阶段:高级应用--尚硅谷学习笔记(含面试题) 2023 年 第 9 章 异常处理 9.1 异 ...

  2. B站MySQL(尚硅谷)学习笔记

    B站MySQL基础(尚硅谷)学习笔记 最近在学习数据库技术,并且把视频中的知识点进行了汇总,字数较多,仅供参考. 会持续更新 欢迎读者提出问题与错误,一起交流~ 视频前几集所讲述的基本知识: DB:数 ...

  3. SpringBoot(尚硅谷学习笔记)

    1.SpringBoot优点(官网 spring.io) Create stand-alone Spring applications 创建独立Spring应用 Embed Tomcat, Jetty ...

  4. 8-zookeeper算法基础(尚硅谷学习笔记)

    目录 拜占庭将军问题 paxos算法 paxos算法流程 情况一 情况二 情况1 情况2 ZAB协议 ZAB协议内容 消息广播 过程 可能出现的问题 崩溃恢复 异常假设 leader选举 数据恢复 C ...

  5. 尚硅谷学习笔记-节点的常用属性和方法

    节点的常用属性和方法[图片在末尾] 方法: 通过具体的元素节点调用 getElementsByTagName() 方法,获取当前节点的指定标签名孩子节点 appendChild( oChildNode ...

  6. [React] 尚硅谷 -- 学习笔记(七)

    第七章 react-ui 最流行的开源React UI组件库 material-ui(国外) 官网 GitHub ant-design(国内蚂蚁金服) PC官网 GitHub 移动官网 GitHub ...

  7. [React] 尚硅谷 -- 学习笔记(六)

    第六章 react-router4 理解 react-router react的一个插件库 专门用来实现一个SPA应用 基于react的项目基本都会用到此库 SPA 单页Web应用(single pa ...

  8. [React] 尚硅谷 -- 学习笔记(五)

    第五章 总结 组件间通信 通过props传递 共同的数据放在父组件上, 特有的数据放在自己组件内部(state) 通过props可以传递一般数据和函数数据, 只能一层一层传递 一般数据–>父组件 ...

  9. [React] 尚硅谷 -- 学习笔记(四)

    第四章 react ajax 理解 React本身只关注于界面, 并不包含发送ajax请求的代码 前端应用需要通过ajax请求与后台进行交互(json数据) react应用中需要集成第三方ajax库( ...

  10. [React] 尚硅谷 -- 学习笔记(三)

    第三章 react应用(基于react脚手架) 使用create-react-app创建react应用 react脚手架 xxx 脚手架:用来帮助程序员快速创建一个基于 xxx 库的模板项目 包含了所 ...

最新文章

  1. python实现简单的http服务器_Python实现简单HTTP服务器(二)
  2. 练习2-1 Programming in C is fun!
  3. java反码_Java:二进制(原码、反码、补码)与位运算
  4. NOI.AC #31. MST
  5. json在java中的使用_有效地使用JSON流(在Java中)
  6. 获取简单的输入和渲染窗口(Hello Window)
  7. cifs mount fail
  8. IBM 人工智能黑科技
  9. 什么是AOP切面编程
  10. 明御:APT攻击预警平台
  11. GitLab CI/CD 配置指南 .gitlab-ci.yml文件的配置
  12. springboot配置mybatis.generator
  13. Java实现蓝桥杯二项式的系数规律
  14. 面试心得与总结---BAT、网易、蘑菇街等
  15. 在V2EX的开发环境里尝试了一下OneAPM @livid
  16. (二十一)资产(组合)的预期收益率和风险
  17. Ajax请求URL的写法
  18. VScode 完整安装、配置及完全卸载
  19. java计算机毕业设计springboot+vue学生宿舍管理系统 elementui
  20. ESP32 之 ESP-IDF 教学(十八)—— 组件配置(KConfig)

热门文章

  1. 我的世界服务端开服基础大全
  2. 图论学习笔记(一) 图
  3. linux下那些服务可以禁用掉
  4. 无信号交叉口车辆通行控制研究
  5. 集装箱号识别API免费信息识别免费,中国人工智能企业中集飞瞳全球最大AI集装箱识别检测云服务,智慧港航智能化港航中国人工智能企业
  6. poi版本升级(3.13到4.0.1)的那些坑
  7. TypeError: super(type, obj): obj must be an instance or subtype of type
  8. Python tushare股票大数据分析与报告生成(优化版1)
  9. 台哥教你写计算机毕业设计论文
  10. 华硕幻X GZ301VV VU VF原厂系统恢复带ASUS Recovery