尚硅谷-Spring5(二)

   @Test
    public void TestJDBC(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        Book book = bookService.findBookInfo("1001");
        System.out.println(book);
    }

四、JdbcTemplate

1、什么是JdbcTemplate

Spring框架对JDBC进行封装,使用jdbcTemplate方便对数据库操作

(1)准备工作(引入相关jar包)

image-20201014175953750

(2)spring配置文件中配置数据库连接池

    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring5" />
        <property name="username" value="root" />
        <property name="password" value="root" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </bean>
 

(3)配置JdbcTemplate对象,注入DataSource

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

(4)创建service类,创建dao类。在dao里注入jdbcTemplate

  • 配置文件
<!--组件扫描-->
<context:component-scan base-package="cn.spring5.Aop"></context:component-scan>
  • service
@Service
public class BookService {
    //注入Dao
    @Autowired
    private BookDao bookDao;
}
  • dao
@Repository
public class BookDaoImpl {
    // 注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}
2、jdbcTemplate 操作数据库(添加)

(1)对应数据库的实体类

public class User {
    private String UserID;
    private String UserName;
    private String Password;

    public String getUserID() {
        return UserID;
    }

    public void setUserID(String userID) {
        UserID = userID;
    }

    public String getUserName() {
        return UserName;
    }

    public void setUserName(String userName) {
        UserName = userName;
    }

    public String getPassword() {
        return Password;
    }

    public void setPassword(String password) {
        Password = password;
    }
}

(2)编写service和dao

@Service
public class BookService {
    //注入Dao
    @Autowired
    private BookDao bookDao;

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

public interface BookDao {

    //添加方法
    void add(Book book);

}
  • 在dao进行数据库添加操作

  • 调用JdbcTemplate对象里的update方法实现添加操作

    update(String sql, Object args)

    有两个参数,一个是sql语句,第二个参数是可变参数,设置sql的值

@Repository
public class BookDaoImpl implements BookDao{
    // 注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 添加方法
    @Override
    public void add(Book book) {
        //创建sql语句
        String sql = "insert into book values(?,?,?)";
        //创建sql语句参数
        Object[] args = {book.getUserID(),book.getUserName(),book.getPassword()};
        int update = jdbcTemplate.update(sql,args);
        System.out.println(update);

    }
}

(4)测试

    @Test
    public void TestJDBC(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        Book book = new Book("1001","Spring5","100.00");
        bookService.addBook(book);
    }

image-20201014200319139

3、jdbcTemplate 操作数据库(修改和删除)
  • BookDao

    public interface BookDao {
        //添加方法
        void add(Book book);
    
        //修改方法
        void updataBook(Book book);
    
        //删除方法
        void delete(String id);
    }

  • BookDaoImpl

    @Repository
    public class BookDaoImpl implements BookDao{
        // 注入JdbcTemplate
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        // 修改方法
        @Override
        public void updataBook(Book book) {
            String sql = "update book set UserName=?,Password=? where UserID=?";
            Object[] args = {book.getUserName(),book.getPassword(),book.getUserID(),};
            int update = jdbcTemplate.update(sql,args);
            System.out.println(update);
        }
    
        // 删除方法
        @Override
        public void delete(String id) {
            String sql = "delete from book where UserID=?";
            int update = jdbcTemplate.update(sql,id);
            System.out.println(update);
        }
    }

  • BookService

    @Service
    public class BookService {
        //注入Dao
        @Autowired
        private BookDao bookDao;
    
        public void updateBook(Book book){
            bookDao.updataBook(book);
        }
    
        public void deleteBook(String id){
            bookDao.delete(id);
        }
    }

  • 测试类

        @Test
        public void TestJDBC(){
            ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
            BookService bookService = context.getBean("bookService", BookService.class);
    //        Book book = new Book("1001","Spring5","100.00");
    //        bookService.addBook(book);
    
            Book book = new Book("1","Spring5","100.00");
            bookService.updateBook(book);
    
            bookService.deleteBook("1");
        }

4、jdbcTemplate 操作数据库(查询返回某个值)

(1)查询表里有多少条记录,返回某个值

(2)使用iJdbcTemplate实现查询返回某个值的代码

queryForObject(String sql,Class requiredType )
两个参数,第一个参数,sql语句,第二个参数,返回类型Class

 // 查询表记录数
    @Override
    public int selectCount() {
        String sql = "select count(*) from book";
        int Count = jdbcTemplate.queryForObject(sql,Integer.class);
        return Count;
    }
5、jdbcTemplate 操作数据库(查询返回对象)

(1)场景:查询图书详情

(2)JdbcTemplate实现查询返回对象

queryForObject(String sql, RowMapper rowMapper, Object… args)

第一个参数,sql语句,

第二个参数,RowMapper,是接口,返回不同类型数据,使用这个接口里面实现完成数据封装

第三个参数,sql语句值

  // 查询返回对象
    @Override
    public Book findBookInfo(String id) {
        String sql = "select * from book where UserId=?";
        Book book = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Book>(Book.class),id);
        return book;
    }

(3)测试类

   @Test
    public void TestJDBC(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        Book book = bookService.findBookInfo("1001");
        System.out.println(book);
    }
 

6、jdbcTemplate 操作数据库(查询返回集合)

(1) 场景:查询图书列表分页

(2) JdbcTemplate实现查询返回集合

query(String sql, RowMapper rowMapper, Object… args )

第一个参数:sql语句

第二个参数:RowMapper,是接口,返回不同类型数据,使用这个接口里面实现完成数据封装

第三个参数:sql语句值

 // 查询返回集合
    @Override
    public List<Book> findAllBook() {
        String sql = "select * from book";
        List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
    }

(3) 测试类

    @Test
    public void TestJDBC(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        List<Book> all = bookService.findAll();
        System.out.println(all.toString());
    }
7、JdbcTemplate操作数据库(批量操作)

(1)批量操作:操作表里的多项记录

(2)JdbcTemplate实现批量操作

batchUpdate(String sql,List<Object[]> batchArgs)

第一个参数:sql语句,

第二个参数:List集合,添加多条记录数据

    // 批量添加
    @Override
    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into book values(?,?,?)";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }

(3)测试类

    @Test
    public void TestJDBC(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.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);
    }
8、JdbcTemplate操作数据库(批量删除、修改)
    // 批量修改
    @Override
    public void batchUpdateBook(List<Object[]> batchArgs) {
        String sql = "update book set UserName=?,Password=? where UserID=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }
	// 批量删除
    @Override
    public void batchDeleteeBook(List<Object[]> batchArgs) {
        String sql = "delete from book where UserID=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }

测试类

    @Test
    public void TestJDBC(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        // 批量修改
        List<Object[]> batchArgs = new ArrayList<>();
        Object[] o1 = {"WeiSanJin","java1","3"};
        Object[] o2 = {"WeiSanJin","c++1","4"};
        Object[] o3 = {"WeiSanJin","mysql1","5"};
        batchArgs.add(o1);
        batchArgs.add(o2);
        batchArgs.add(o3);
        bookService.batchUpdate(batchArgs);
        
        // 批量修改
        List<Object[]> batchArgs = new ArrayList<>();
        Object[] o1 = {"3"};
        Object[] o2 = {"4"};
        batchArgs.add(o1);
        batchArgs.add(o2);
        bookService.batchDelete(batchArgs);
    }

五、事务概念

1、什么是事务
  • 事务是数据库操作最近本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
  • 典型场景:银行转账
2、事物的四个特性(ACID)

(1) 原子性;要么都成功。要么都失败

(2) 一致性:操作之前和操作之后总量不变

(3) 隔离性:多事务操作之间不会相互影响

(4) 持久性:事务提交之后,表中数据发生变化保存起来

3、事务操作(搭建事务操作环境)

(1)创建数据表,添加记录

image-20201014214050651

image-20201014214129997

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

service注入dao,在到注入JdbcTemplate,在JdbcTemplate注入DataSource

@Service
public class UserService {
    // 注入dao
    @Autowired
    private UserDao userDao;
}

@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;
}

(3)在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账方法)

@Repository
public class UserDaoImpl implements UserDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addMoney() {
        String sql = "update account set money=money+? wherer username=?";
        jdbcTemplate.update(sql, 100, "mary");
    }

    @Override
    public void reduceMoney() {
        String sql = "update account set money=money-? wherer username=?";
        jdbcTemplate.update(sql, 100, "lucy");
    }
}

@Service
public class UserService {
    // 注入dao
    @Autowired
    private UserDao userDao;

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

(4)测试类

@Test
public void testAccount(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.accountMoney();
}

(5)上面代码,如果正常秩序没有问题的,但是如果代码执行过程中出现异常,有问题

public void accountMoney(){
    userDao.reduceMoney();

    // 模拟异常
    int i = 10/0;

    userDao.addMoney();
}

(6)解决

  • 第一步 开启事务
  • 第二步 进行业务操作
  • 第三步 没有发生异常,事务提交
  • 第四步 出现异常,事件回滚
4、事务操作(Spring事务管理介绍)

(1)事务添加到JavaEE三层结构里面Service层(业务逻辑层)

(2)在Spring进行事务管理操作

  • 编程式事务管理
  • 声明式事务管理(使用)

(3)声明式事务管理

  • 基于注解方式(使用)
  • 基于xml配置文件方式

(4)在Spring进行声明式事务管理,底层使用AOP原理

(5)Spring事务管理API

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

    image-20201015120052635

5、事务操作(注解声明式事务管理)

(1)在Spring配置文件配置事务管理器

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

(2)在Spring配置文件,开启事务注解

  • 在Spring配置文件引入名称空间tx

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    ">

  • 开启事务注解

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

(3)在Service类上面(获取service类里面方法上面)添加事务注解

  • @Transactional 这个注解添加到类上面,也可以添加方法上面

  • 如果把这个注解添加到类上面,这个类里面所有的方法都添加事务

  • 如果把这个注解添加到方法上面,为这个方法添加事务

    @Service
    @Transactional
    public class UserService {
    }

6、事务操作(声明式事务管理参数配置)

(1)在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数

  • propagation:事务传播行为

    image-20201015183416966

    image-20201015183454089

    • 多事务方法直接进行调用,这个过程中事务是如何进行管理的
  • ioslation:事务隔离级别

    • 事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
    • 有三个读问题:脏读、不可重复读、虚(幻)读

    (1)脏读

    image-20201015223040626

    (2)不可以重复读:一个未提交事务读取到另一提交事务修改数据

    image-20201015223223967

    (3)虚读:一个未提交事务读取到另一个提交事务添加数据

    🚑解决:通过设置事务隔离级别,解决读问题

    image-20201015223909643

    // 添加事务注解
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
    12
    
  • timeout:超时时间

    • 事务需要在一定时间内进行提交,如果不提交进行回滚
    • 默认值:-1,设置时间以秒单位进行计算
    @Transactional(timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

  • readOnly:是否只读

    • 读:查询操作,写:添加修改删除操作
    • readOnly默认值false,表示可以查询,可以添加修改删除操作
    • 设置readOnly值为true后,只能查询
    @Transactional(readOnly = true,timeout = -1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)

  • rollbackFor:不回滚

    • 设置出现哪些异常进行事务回滚
  • noRollbackFor:不回滚

    • 设置出现哪些异常不进行事务回滚
7、事务操作(XML声明式事务管理)

(1)在Spring配置文件中进行配置

  • 第一步 配置事务管理器

        <!--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(* cn.spring5.Aop.Service.UserService.*(..))"/>
            <!--配置切面-->
            <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>
        </aop:config>

8、事务操作(完全注解声明式事务管理)

(1)创建配置类,使用配置类替代xml配置文件

@Configuration // 配置类
@ComponentScan(basePackages = "cn.spring5.Aop") // 组件扫描
@EnableTransactionManagement // 开启事务
public class TxConfig {
    // 创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring5");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 注入DataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    // 创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

(2)测试类

    @Test
    public void testAccountConfig(){
        ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

六、Spring5框架新功能

1、整合JUnit5单元测试框架

(1)整个Spring5框架的代码基于JDK8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除

(2)Spring5.0框架自带了通用的日志封装

  • Spring5已经移除Log4jConfigListener,官方建议使用Log4j2

  • Spring5框架整合Log4j2

    • 第一步 引入jar包

      image-20201015232434908

    • 第二步 创建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>

(3)Spring5框架核心容器支持@Nullable注释

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

    // 注解用在方法上面,方法返回值可以为空
    @Nullable
    String getId();
    
    // 注解使用在方法参数里面,方法参数可以为空
    public <T. void registerBean(@Nullable String beanName){
        
    }
    
    // 注解使用在属性上面,属性值可以为空
    @Nullable
    private String bookName;

(4)Spring5核心容器支持函数式风格GenericApplicationContext

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

(5)Spring5支持整合JUnit5

  • 整合JUnit4

    • 第一步 引入Spring相关针对测试依赖

    • 第二步 创建测试类,使用注解方法完成

    // 单元测试框架
    @RunWith(SpringJUnit4ClassRunner.class)
    // 加载配置文件
    @ContextConfiguration("classpath:bean2.xml")
    public class JTest4 {
        @Autowired
        private UserService userService;
    
        @Test
        public void test(){
            userService.accountMoney();
        }
    }

  • 整合JUnit5

    • 第一步 引入JUnit5的jar包

    image-20201017154243100

    • 第二步 创建测试类,使用注解完成
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration("classpath:bean2.xml")
    public class JTest4 {
        @Autowired
        private UserService userService;
    
        @Test
        public void test1(){
            userService.accountMoney();
        }
    }

  • 使用复合注解替代上面两个注解完成整合

    @SpringJUnitConfig(locations = "classpath:bean2.xml")

2、Webflux-基本概念

(1)SpringWebflux介绍

image-20201017160951785

  • 是Spring5添加的新的模块,用于web开发的,功能和SpringMVC类似的,Webflux使用当前一种比较流行程响应式编程出现的框架
  • 使用传统web框架,比如SpringMAC,这些机遇Servlet容器,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的
  • 什么是异步非阻塞
    • 异步和同步:异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
    • 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后再去做事情就是非阻塞
  • Webflux特点
    • 第一 非阻塞式:在有限资源下,提供系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
    • 第二函数式编程:Spring5框架基于java8,Webflux使用Java8函数式编程方式实现路由请求
  • 比较SpringMVC
    • 两个框架都可以使用注解方式,都运用在Tomet等容器中
    • SpringMAC采用命令式编程,Webflux采用异步响应式编程

image-20201017162720090

(2)响应式编程

响应式编程式一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流二相关的计算模型会自动将变化的值通过数据流进行传播

  • Java8及其之前版本

    提供的观察这模式两个类Observer和Observable

    public class ObserverDemo extends Observable {
        public static void main(String[] args) {
            ObserverDemo observerDemo = new ObserverDemo();
            // 添加观察者
            observerDemo.addObserver((o,arg)->{
                System.out.println("发生变化"+arg);
            });
            observerDemo.addObserver((o,arg)->{
                System.out.println("收到被观察者通知,准备改变"+arg);
            });
            observerDemo.setChanged();// 数据变化
            observerDemo.notifyObservers();//通知
        }
    }

(3)响应式编程(Reactor实现)

  • 响应式编程操作中,Reactor是满足Reactive规范框架

  • Reactor有两个核心类,Mono和Flux,这两个实现接口Publisher,提供丰富操作符。flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素

    image-20201017180140176

(4)代码演示Flux和Mono

// 第一步 引入依赖

<dependencies>
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-core</artifactId>
        <version>3.1.5.RELEASE</version>
    </dependency>
</dependencies>
    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或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅声明都不会发生的

    public static void main(String[] args) {
        // just方法直接声明
        Flux.just(1,2,3,4).subscribe(System.out::print);
        Mono.just(1).subscribe(System.out::println);
    }

操作符

  • map元素映射为新元素

    image-20201017203421929

  • flatMap元素映射为流

    image-20201017203436522

3、SpringWebflux执行流程和核心API

SpringWebflux就Reactor,默认使用容器Netty,Netty是高性能的NIO框架,异步非阻塞的框架

  • Netty

    BIO

    image-20201017211010599

    NIO

    img

  • SpringWebflux 执行过程和 SpringMVC 相似的

    • SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler

    • 接口 WebHandler 有一个方法

      public interface WebHandler {
          Mono<Void> handle(ServerWebExchang var1)
      }

      image-20201017214718714

  • SpringWebflux 里面 DispatcherHandler,负责请求的处理

    • HandlerMapping:请求查询到处理的方法
    • HandlerAdapter:真正负责请求处理
    • HandlerResultHandler:响应结果处理
  • SpringWebflux 实现函数式编程,两个接口:RouterFunction(路由处理)和 HandlerFunction(处理函数)

4、SpringWebflux(基于注解编程模型)
  • SpringWebflux 实现方式有两种:

    • 注解编程模型
      • 使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器
    • 函数式编程模型
  • 演示

    第一步 创建 SpringBoot 工程,引入 Webflux 依赖

    <!--pom.xml-->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>

    第二步 配置启动端口号

    第三步 创建包和相关类

    // 实体类
    public class User {
        private String name;
        private String gender;
        private Integer age;
    
        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;
        }
    }

    创建接口定义操作的方法

    public interface UserService {
        /** 根据id查询用户*/
        Mono<User> getUserById(int id);
    
        /** 查询所有用户*/
        Flux<User> getAllUser();
    
        /** 添加用户*/
        Mono<Void> saveUserInfo(Mono<User> user);
    }

    接口实现类

    public class UserServiceImpl implements UserService {
        /** 创建map集合存储数据*/
        private final Map<Integer,User> userMap = new HashMap<>();
    
        public UserServiceImpl() {
            this.userMap.put(1, new User("lucy","nan",20));
            this.userMap.put(2, new User("mary","nv",30));
            this.userMap.put(3, new User("jack","nan",40));
            this.userMap.put(4, new User("WeiSanJin","nan",50));
        }
    
        /** 根据id查询*/
        @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> userMono) {
            return userMono.doOnNext(person -> {
                /* 向map集合里面放值*/
                int id = userMap.size()+1;
                userMap.put(id,person);
            }).thenEmpty(Mono.empty());
        }
    }

    创建 controller

    @RestController
    public class UserController {
        /** 注入service*/
        @Autowired
        private UserService userService;
    
        /** id查询*/
        @GetMapping("/user/{id}")
        public Mono<User> getUserId(@PathVariable int id){
            return userService.getUserById(id);
        }
    
        /** 查询所有*/
        @GetMapping("/user")
        public Flux<User> getUser(){
            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

  • 第一步 把注解编程模型工程复制一份 ,保留 entity 和 service 内容

  • 第二步 创建 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操作符fluxMap*/
            return userMono
                        .flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                            .body(BodyInserters.fromObject(person)))
                            .switchIfEmpty(notFound);
        }
    
        /** 查询所有*/
        public Mono<ServerResponse> getAllUser(){
            /* 调用service得到结果*/
            Flux<User> userFlux = this.userService.getAllUser();
            return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(userFlux,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 {
        /** 创建Router路由*/
        public RouterFunction<ServerResponse> routerFunction(){
            /* 创建hanler对象*/
            UserService userService = new UserServiceImpl();
            UserHandler handler = new UserHandler(userService);
            /* 设置路由*/
            return RouterFunctions.route(
                    GET("/user/{id}").and(accept(APPLICATION_JSON)),handler::getUserById)
                    .andRoute(GET("/user").and(accept(APPLICATION_JSON)),handler::getAllUser);
    
        }
    }

  • 创建服务器完成适配

    public class Server {
        /** 创建服务器完成配置*/
        public void createReactorServer(){
            /*路由和handler适配*/
            RouterFunction<ServerResponse> route = routerFunction();
            HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
            ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
    
            /* 创建服务器*/
            HttpServer httpServer = HttpServer.create();
            httpServer.handle(adapter).bindNow();
        }
    }

  • 最终调用

    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();
        }
    }

(4)使用 WebClient 调用

public class Client {
    public static void main(String[] args) {
        /* 调用服务器地址*/
        WebClient webClient = WebClient.create("http://127.0.0.1:8080");

        /* 根据ID查询*/
        String id = "1";
        User userResult = webClient.get().uri("/user/{id}",id)
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
                .block();
        System.out.println(userResult.getName());

        /* 查询所有*/
        Flux<User> results = webClient.get().uri("/user").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);
        results.map(stu ->stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
    }
}

课程总结

1 、Spring 框架概述
(1)轻量级开源 JavaEE 框架,为了解决企业复杂性,两个核心组成:IOC 和 AOP
(2)Spring5.2.6 版本
2 、IOC 容器
(1)IOC 底层原理(工厂、反射等)
(2)IOC 接口(BeanFactory)
(3)IOC 操作 Bean 管理(基于 xml)
(4)IOC 操作 Bean 管理(基于注解)
3 、Aop
(1)AOP 底层原理:动态代理,有接口(JDK 动态代理),没有接口(CGLIB 动态代理)
(2)术语:切入点、增强(通知)、切面
(3)基于 AspectJ 实现 AOP 操作
4 、JdbcTemplate
(1)使用 JdbcTemplate 实现数据库 curd 操作
(2)使用 JdbcTemplate 实现数据库批量操作
5 、事务管理
(1)事务概念
(2)重要概念(传播行为和隔离级别)
(3)基于注解实现声明式事务管理
(4)完全注解方式实现声明式事务管理
6 、Spring5 新功能
(1)整合日志框架
(2)@Nullable 注解
(3)函数式注册对象
(4)整合 JUnit5 单元测试框架
(5)SpringWebflux 使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值