spring(三)AOP、spring声明式事务、Webflux的执行流程和核心API

尚硅谷-Spring5框架2020最新版教程笔记 尚硅谷-Spring5框架2020最新版教程 文章目录尚硅谷-Spring5框架2020最新版教程一、Spring框架概述1、Spring特点2、Spring版本选择3、入门案例二、IOC容器1、IOC底层原理2、IOC接口(BeanFactory)4、IOC操作 Bean 管理5、IOC操作 Bean 管理(基于xml)6、IOC操作Bean管理(xml注入其他类型属性)7、IOC操作Bean管理(xml注入集合属性)8、IOC操作Bean管理(FactotyBean)9、IOC操作Bean管理(bean 阅读详情

AOP切面编程

什么是AOP

  AOP是面向切面编程。全称:Aspect Oriented Programming

  面向切面编程指的是:程序是运行期间,动态地将某段代码插入到原来方法代码的某些位置中。这就叫面向切面编程。

一个简单计算数功能加日记

  准备计算器相关类
计算接口

public interface Calculate {
	public int add(int num1, int num2);

	public int mul(int num1, int num2);

	public int div(int num1, int num2);

	public int sub(int num1, int num2);
}

  计算机类

public class Calculator implements Calculate {
	public int add(int num1, int num2) {
		System.out.println("日记 :【add】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 + num2;
	}

	public int mul(int num1, int num2) {
		System.out.println("日记 :【mul】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 * num2;
	}

	public int div(int num1, int num2) {
		System.out.println("日记 :【div】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 / num2;
	}

	public int sub(int num1, int num2) {
		System.out.println("日记 :【sub】 方法调用前 。参数1是:" + num1 + " , 参数2是:" + num2);
		return num1 - num2;
	}
}

  测试的代码

public class CalculatorTest {
	public static void main(String[] args) {
		Calculate calculate = new Calculator();
		int result = calculate.add(12, 12);
		System.out.println("相加的结果:" + result);
		
		result = calculate.mul(12, 12);
		System.out.println("相乘的结果:" + result);
	}
}

在这里插入图片描述

  上面这种方法加日记处理操作。日记的代码就会耦合到业务代码中。而且后期如果需要修改日记就需要去指的修改所有方法中的日记操作。这个维护操作非常不方便。
可以说是一个很失败的例子。

原始方法统一日记处理。

  把日记的内容封装到一个类去中集中处理。

编写一个日记处理工具类

public class LogUtil {
	 /**
     * 记录前置的日志操作
     * @param method 当前运算操作
     * @param args 当前运算参数
     */
    public static void logBefore(String method, Object ... args){
        System.out.println("操作运算是 : " + method + " 参数是 : " + Arrays.asList(args));
    }

    /**
     * 返回日志操作
     * @param method 当前方法
     * @param result 当前操作返回值
     */
    public static void logAfterReturning(String method, Object result){
        System.out.println("当前操作运算时 : " + method + " 返回值是 : " + result);
    }

    /**
     * 当前操作产生的异常
     * @param method 当前操作
     * @param e 发生的异常
     */
    public static void logAfterThrowing(String method, Exception e){
        System.out.println("当前运算时 : " + method + " 发生的异常是 : " + e);
    }
}

  修改原来Calculator中的日记代码

@Override
	public int add(int num1, int num2) {
		LogUtil.log("add", num1, num2);
		return num1 + num2;
	}

	@Override
	public int mul(int num1, int num2) {
		LogUtil.log("mul", num1, num2);
		return num1 * num2;
	}

  但是这种方式的不足之处是,每有一个需要加日记的类,都需要到类的代码中去添加日记功能代码。
无法做到所有对象都统一处理。

使用代理实现日记

使用jdk动态代理实现日记

创建一个计算器代理工具类

public class CalculateProxyFactory {

    public static Object getProxy(final Calculate target) {

        /**
         * Proxy 是Jdk中自带的一个工具类(反射包下,属于反射的功能).
         * Proxy类的作用: 它可以帮我们创建代理类或实例
         * 方法newProxyInstance()说明: 创建代理对象实例
         * 第一个参数是: 目标对象的类加载器
         * 第二个参数是: 目标对象实现的所有接口
         * 第三个参数是: InvocationHandler 接口的实例
         * InvocationHandler 接口的实现类可以对代理的目标对象方法进行增强操作.
         * 代理的目标对象 ===>>> 需要额外增加功能的类(对象实例)
         * 增强操作 ===>>> 给原来功能添加的额外功能叫增强操作 ( 日记就是增强操作 )
         */
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() { // 匿名内部类
                    /**
                     * invoke 方法是 InvocationHandler 接口中唯一的方法
                     * 代理对象每次调用方法时,都会执行 invoke() 方法 , 所有的增强操作都需要在invoke()方法中完成
                     * @param proxy  代理对象实例
                     * @param method 代理调用的方法的反射 Method 对象实例
                     * @param args  调用代理方法时传递进来的参数
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代理调用了 invoke 方法 ");
                        System.out.println(method);  //打印方法信息
                        System.out.println(Arrays.asList(args)); //打印参数信息
                        // invoke() 方法执行代理对象的(加法 / 除法 / 增强日志)操作
                        Object result = null;
                        LogUtil.logBefore(method.getName(), args);
                        try {
                            // 1. 返回值是 method 方法调用时的返回值
                            result = method.invoke(target, args);
                            // 2. 增强操作
                            LogUtil.logAfterReturning(method.getName(), result);
                        }catch (Exception e){
                            LogUtil.logAfterThrowing(method.getName(), e);
                        }
                        // invoke() 返回代理方法的返回值
                        return result;
                    }
                });
    }
}

测试代码:

// 测试代码
 public static void main(String[] args) {
  // 目标对象
  Calculate target = new CalculateImpl();
  // 创建 Calculate 的代理对象实例
  Calculate calculateProxy = (Calculate) createJDKProxy(target );
  // jdk动态代理对象实例和目标对象实例 同宗同族 ( 他们都实现了相同的接口 )
  System.out.println(calculateProxy instanceof Calculate);
  System.out.println(target instanceof Calculate);
 
  System.out.println( "代理方法的结果是 : " + calculateProxy.div(100,20) );
 
  // jdk动态代理创建出来的代理对象实例 是 目标对象 接口的一个实现类
  // 这个代理对象 和 目标对象类没有父子关系 ( 只能用接口接收代理对象 )
 }

  优点:这种方式已经解决我们前面所有日记需要的问题。非常的灵活。而且可以方便的在后期进行维护和升级。
  缺点:当然使用jdk动态代理,需要有接口。如果没有接口。就无法使用jdk动态代理。

使用cglib代理

public class CGLibProxyFactory implements MethodInterceptor {

	public static Object getCGLibProxy(Object target, Callback callback) {
		// 创建一个CGLig生成器
		Enhancer enhancer = new Enhancer();
		// 设置父类。因为cglib是通过类,进行代码,不是通过接口
		enhancer.setSuperclass(target.getClass());
		// 设置拦截的代理方法
		enhancer.setCallback(callback);
		// create 方法创建一个代理对象并返回
		return enhancer.create();
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy)
			throws Throwable {
		LogUtil.log(method.getName(), (int) params[0], (int) params[1]);
		// 调用实际的对象的方法
		// 一定要使用methodProxy对象
		// 第一个参数是proxy代码对象的父类方法
		Object result = methodProxy.invokeSuper(proxy, params);
		System.out.println("这是后置代码");

		return result;
	}

	public static void main(String[] args) {
		Calculator calculator = (Calculator) CGLibProxyFactory.getCGLibProxy(new Calculator(),
				new CGLibProxyFactory());
		calculator.add(12, 13);
	}
}

优点:在没有接口的情况下,同样可以实现代理的效果。
缺点:同样需要自己编码实现代理全部过程。
但是为了更好的整合Spring框架使用。所以我们需要学习一下Spring 的AOP 功能。

AOP编程的专业术语

通知(Advice)
  通知就是增强的代码。比如前置增强的代码。后置增强的代码。异常增强代码。这些就叫通知
切面(Aspect)
切面就是包含有通知代码的类叫切面。

横切关注点
  横切关注点,就是我们可以添加增强代码的位置。比如前置位置,后置位置,异常位置。和返回值位置。这些都叫横切关注点。
目标(Target)
  目标对象就是被关注的对象。或者被代理的对象。

代理(Proxy)
  为了拦截目标对象方法,而被创建出来的那个对象,就叫做代理对象。

连接点(Joinpoint)
  连接点指的是横切关注点和程序代码的连接,叫连接点。

切入点(pointcut)
  切入点指的是用户真正处理的连接点,叫切入点。

  在Spring中切入点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
在这里插入图片描述

使用Spring实现AOP简单切面编程

使用ProxyFactoryBean进行AOP

目标类:
package com.zy.entity;


import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
 * @ClassName: Student
 * @Author: Tiger
 * @Title:
 * @Datetime: 2020/8/19   14:24
 * @Package: com.zy.entity
 */
@Component
public class Student implements Serializable ,StudentInterface{
    private Integer sid;
    private String name;
//getset、tostring略
    public void study(){
        System.out.println("我在认真学习");
    }
    public void smoke(){
        System.out.println("吸烟有害健康");
    }
    public void show(){
        System.out.println("介绍一下我自己!");
    }
}

创建通知类:(封装辅助功能的类)

  BeforeAdvice:目标类方法执行前要执行的代码 实现接口MethodBeforeAdivce

public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("目标方法"+method.getName()+"("+ Arrays.toString(objects) +")执行前要执行的代码");
    }
}

  AfterAdivce:目标类方法执行后要执行的代码: 实现接口AfterReturningAdvice

public class MyAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("目标方法"+method.getName()+"("+ Arrays.toString(objects) +")执行之后要执行的代码");
        System.out.println(o+":::"+o1);
    }
}

  AroundAdivce:目标类方法执行前后要执行的代码: 实现接口MethodIntercoptor

public class MyInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        Method method = arg0.getMethod();
        System.out.println(method+"方法的around通知1111");
        Object proceed = arg0.proceed();
        System.out.println(method+"方法的around通知2222");
        return proceed;
    }
}

  ThrowAdivce:目标类方法异常时要执行的代码:实现接口ThrowsAdivce
在这里插入图片描述

public class MyThrowAdvice implements ThrowsAdvice {
    public void afterThrowing(Method method, Object[] objects, Object o1,Exception e){
        System.out.println(method.getName()+"出现异常,异常是:"+e.getMessage());
    }
}

  配置问价中创建目标bean,通知bean创建bean

<bean id="s1" class="com.zy.entity.Student">
        <property name="sid" value="112"></property>
        <property name="name" value="小脑斧"></property>
    </bean>
    <bean id="before" class="com.zy.aop.MyBeforeAdvice"/>
    <bean id="after" class="com.zy.aop.MyAfterAdvice"/>
    <bean id="around" class="com.zy.aop.MyInterceptor"/>
    <bean id="throw" class="com.zy.aop.MyThrowAdvice"/>
<!--    使用proxyfactorybean吧bean组合成一个代理对象-->
    <bean id="studentProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="s1"/>
        <property name="interceptorNames">
            <list>
                <value>before</value>
                <value>after</value>
                <value>around</value>
                <value>throw</value>
            </list>
        </property>
    </bean>

  为目标类创建接口

public interface StudentInterface {
    void show();
    void smoke();
    void study();
}

    测试;

 @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        StudentInterface student = (StudentInterface) context.getBean("studentProxy");
        student.show();
        student.study();
        student.smoke();
    }

在这里插入图片描述
在这里插入图片描述

Spring中基于AOP的XML架构

1.1 ioc和aop jar包
1.2 创建目标bean对应的类 并提供接口
在这里插入图片描述1.3 对应核心配置文件:引入context和aop标签
在这里插入图片描述1.4 创建通知bean 其包含所有通知类型对应的方法

@Component
public class MyAdvice {
    public void afterMethod1() {
        System.out.println("public void afterMethod1()111");
    }

    public void afterMethod2() {
        System.out.println("public void afterMethods2()222");
    }

    public void beforeMethod() {
        System.out.println("public void beforeMethod()000");
    }

    public Object aroundMethod(ProceedingJoinPoint pjp) {
        System.out.println("方法运行前");
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        System.out.println("public void aroundMethod()后");
        return result;
    }
    public void throwsMethod(Exception e){
        System.out.println("public void throwsMethod()!!!!!!!  异常原因:"+e.getMessage());
    }

1.5 在核心装配文件中为目标类和通知类创建bean
和通过aop标签 设置切面(通知+切入点)

  <!-- 创建目标bean -->
    <bean id = "s2" class="com.zy.entity.Student">
        <property name="name" value="小脑斧"></property>
        <property name="sid" value="34"></property>
    </bean>
    <bean id = "s3" class="com.zy.entity.Student">
        <property name="name" value="大狮子"></property>
        <property name="sid" value="24"></property>
    </bean>
    <!--======================================================-->
<!--    为通知类创建bean-->
    <bean id="myAdvice" class="com.zy.advice.MyAdvice"/>
<!--    通过aop标签,声明要在哪些对象的哪些方法上添加哪些通知-->
    <aop:config>
        <aop:aspect ref="myAdvice"> <!-- 声明通知额bean-->
<!--            声明从切入点-->
            <aop:pointcut id="myPointCut" expression="execution(* com.zy.entity.*.*(..))"/>
            <aop:before  pointcut-ref="myPointCut" method="beforeMethod"/>
            <aop:after  pointcut-ref="myPointCut" method="afterMethod1"/><!-- 指定每种通知对应的方法 -->
            <aop:after-returning pointcut-ref="myPointCut"  method="afterMethod2"/>
            <aop:after-throwing  pointcut-ref="myPointCut" method="throwsMethod"  throwing="e"/>
            <aop:around  pointcut-ref="myPointCut" method="aroundMethod"/>
        </aop:aspect>
    </aop:config>

测试:
在这里插入图片描述
在这里插入图片描述

Spring中基于AOP的@AspectJ

  1.1 在核心配置文件中设置自动扫描包+自动代理
在这里插入图片描述

   1.2 给目标类添加compontent注解
在这里插入图片描述

   1.3 给通知类添加compontent注解和aspect注解(指定此类为通知类)
在这里插入图片描述

  1.4 给通知类中的方法添加注解 指定对应的通知类型和切入点

@Component("myAdvice2")
@Aspect//声明此类是通知类
public class MyAdvice2 {
    @After("execution(* com.zy.entity.*.*(..))")
    public void afterMethod1() {
        System.out.println("public void afterMethod1()1111");
    }

    @AfterReturning("execution(* com.zy.entity.*.*(..))")
    public void afterMethod2() {
        System.out.println("public void afterMethod2()22222222");
    }

    @Before("execution(* com.zy.entity.*.*(..))")
    public void beforeMethod() {
        System.out.println("public void beforeMethod()0000");
    }

    @Around("execution(* com.zy.entity.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        System.out.println("public void aroundMethod()前");
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        System.out.println("public void aroundMethod()后");
        return result;
    }

    //throwing  ::指定参数列表的变量名
    @AfterThrowing(value = "execution(* com.zy.entity.Student.show(..))", throwing = "e")
    public void throwsMethod(Exception e) {
        System.out.println("public void throwsMethod()!!!!!!!  异常原因:" + e.getMessage());
    }
}

  1.5 测试:
在这里插入图片描述

spring事务操作

什么是事务

1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账

  • lucy 转账 100 元 给 mary
  • lucy 少 100,mary 多 100
    2 、事务四个特性(ACID )
    (1)原子性
    (2)一致性
    (3)隔离性
    (4)持久性

事务操作( (Spring 事务管理介绍)

1 、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2 、在 Spring 进行事务管理操作
( (1)有两种方式: )有两种方式:编程式事务管理和声明式事务管理(使用) 和声明式事务管理(使用)
3 、声明式事务管理
( (1)基于注解方式 )基于注解方式(使用) (使用)
(2)基于 xml 配置文件方式
4 、在 Spring 进行声明式事务管理,底层使用 AOP 原理
5 、Spring 事务管理 API
(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
在这里插入图片描述

注解声明式事务操作

1、在spring配置文件中配置事务管理器

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

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

<?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:P="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

(2)开启事务注解

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

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

@Service
@Transactional
public class UserService {

xml声明事务管理

1 、在 spring 配置文件中进行配置
第一步 配置事务管理器
第二步 配置通知
第三步 配置切入点和切面

  <context:component-scan base-package="com.zy"/>
    <!--    创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--        注入数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--2 配置通知-->
    <tx:advice id="txAdvice">
        <!--配置事务参数-->
        <tx:attributes><!--指定哪种规则的方法上面添加事务-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--<tx:method name="account*"/>-->
        </tx:attributes>
    </ tx:advice>

    <!--3 配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pt" expression="execution(*
com.atguigu.spring5.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice- ref="txadvice" pointcut- ref="pt"/>
    </aop:config>

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

@Configuration
@EnableTransactionManagement//开启事务
@ComponentScan(basePackages = "com.zy")
public class TXConfig {

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return new JdbcTemplate();
    }

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

Webflux

1 、SpringWebflux 介绍

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

解释什么是异步非阻塞

  • 异步和同步
  • 非阻塞和阻塞
  • 上面都是针对对象不一样
  • 异步和同步针对调用者 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同
    步,如果发送请求之后不等着对方回应就去做其他事情就是异步
  • 阻塞和非阻塞针对被调用者 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻
    塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞

Webflux 特点:

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

2、响应式编程 、响应式编程( (Java 实现)

(1)什么是响应式编程

响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便
地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公
式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。

(2)Java8 及其之前版本

  • 提供的 观察者模式两个类 Observer 和 Observable、
public class ObserverDemo extends Observable {
    public static void main(String[] args) {
        ObserverDemo observer =new ObserverDemo();
        //添加观察者
        observer.addObserver((o,arg)->{
            System.out.println("发生了变化");
        });
        observer.addObserver((o,arg)->{
            System.out.println("手动被观察者的通知,准备发生变化");
        });

        observer.setChanged();//监控数据变化
        observer.notifyObservers();//通知
    }
}

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

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

(4 )代码演示 Flux 和 和 Mono
第一步 引入依赖

< dependency>
< groupId>io.projectreactor</ groupId>
< artifactId>reactor-core</ artifactId>
< version>3.1.5.RELEASE</ version>
</ dependency>

第二步 编程代码

public class ReactorTest {
    //just声明元素
    public static void main(String[] args) {
        Flux.just(1, 2, 3, 4, 5, 6);
        Mono.just(1);
        //其他方法
        Integer[] a = {1, 2, 3, 4, 5, 6};
        Flux.fromArray(a);
        List<Integer> list = Arrays.asList(a);
        Flux.fromIterable(list);
        Stream<Integer> stream = list.stream();
        Flux.fromStream(stream);

    }
}

(5)三种信号特点

  • 错误信号和完成信号都是终止信号,不能共存的
  • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
  • 如果没有错误信号,没有完成信号,表示是无限数据流
    (6)调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的
    在这里插入图片描述

(7 )操作符

  • 对数据流进行一道道操作,成为操作符,比如工厂流水线
    第一 map 元素映射为新元素
    在这里插入图片描述第二 flatMap 元素映射为流
    ⚫ 把每个元素转换流,把转换之后多个流合并大的流
    在这里插入图片描述

4 、SpringWebflux 执行流程和核心 API

SpringWebflux 基于 Reactor,默认使用容器是 Netty,Netty 是高性能的 NIO 框架,异步非阻塞的框架。
(1)Netty

  • BIO(Blocking IO阻塞io)
  • 在这里插入图片描述

NIO(non-blocking IO非阻塞io)
在这里插入图片描述

(2)SpringWebflux 执行过程和 SpringMVC 相似的

  • SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler
  • 接口 WebHandler 有一个方法
    在这里插入图片描述在这里插入图片描述(3)SpringWebflux 里面 DispatcherHandler,负责请求的处理
  • HandlerMapping:请求查询到处理的方法
  • HandlerAdapter:真正负责请求处理
  • HandlerResultHandler:响应结果处理
    (4)SpringWebflux 实现函数式编程,两个接口:RouterFunction(路由处理)
    和 HandlerFunction(处理函数)

5 、SpringWebflux (基于注解编程模型)

SpringWebflux 实现方式有两种:注解编程模型和函数式编程模型
使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,
SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器
第一步 创建 SpringBoot 工程,引入 Webflux 依赖
在这里插入图片描述在这里插入图片描述第二步配置端口号在这里插入图片描述第三步 创建包和相关类
⚫ 实体类
⚫ 创建接口定义操作的方
在这里插入图片描述⚫ 创建接口定义操作的方法

public interface UserService {
    //根据id查用户
    Mono<User> getUserById(Integer id);

    //查询所有
    Flux<User> getAllUser();

    //添加用户
    Mono<Void> insertUser(Mono<User> user);
}

⚫ 接口实现类

@Repository
public class UserServiceIml implements UserService {

    private final Map<Integer, User> users = new HashMap<>();

    public UserServiceIml() {
        this.users.put(1, new User("小脑斧", 21, "nan"));
        this.users.put(2, new User("大脑斧", 21, "nan"));
        this.users.put(3, new User("武松打虎", 21, "nan"));
    }

    @Override
    public Mono<User> getUserById(Integer id) {

        return Mono.just(this.users.get(id));
    }

    @Override
    public Flux<User> getAllUser() {
        return Flux.fromIterable(this.users.values());
    }

    @Override
    public Mono<Void> insertUser(Mono<User> userMono) {

        return userMono.doOnNext(person -> {
//向 map 集合里面放值
            int id = users.size() + 1;
            users.put(id, person);
        }).thenEmpty(Mono.empty());
    }
}

⚫ 创建 controller

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/{id}")
    public Mono<User> getUser(@PathVariable Integer id) {
        return userService.getUserById(id);
    }

    @GetMapping("/user/all")
    public Flux<User> findAll() {
        return userService.getAllUser();
    }

    @PostMapping("/saveUser")
    public Mono<Void> save(@RequestBody User userMono) {
        Mono<User> mono = Mono.just(userMono);
        return userService.insertUser(mono);

    }
}

在这里插入图片描述⚫ 说明
SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC+Servlet+Tomcat
SpringWebflux 方式实现,异步非阻塞 方式,基于 SpringWebflux+Reactor+Netty

6 、SpringWebflux (基于函数式编程模型)

(1)在使用函数式编程模型操作时候,需要自己初始化服务器
(2)基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发
给对应的 handler)和 HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数
式接口的实现并且启动需要的服务器。
( 3 ) SpringWebflux 请 求 和 响 应 不 再 是 ServletRequest 和 ServletResponse , 而 是
ServerRequest 和 ServerResponse
第一步 把注解编程模型工程复制一份 ,保留 entity 和 service 内容
第二步 创建 Handler(具体实现方法)

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
public class UserHandler {

    private final UserService userService;

    public UserHandler(UserService userService) {
        this.userService = userService;
    }

    //根据id查询
    public Mono<ServerResponse> getUserById(ServerRequest request) {
        //获取id
        Integer id = Integer.valueOf(request.pathVariable("id"));
        //空值处理
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        //查询数据
        Mono<User> user = this.userService.getUserById(id);

        return
                user
                        .flatMap(person -> ServerResponse.ok()
                                .contentType(MediaType.APPLICATION_JSON).
                                        body(fromObject(person))
                                .switchIfEmpty(notFound));


    }


    //添加操作
    public Mono<ServerResponse> saveUser(ServerRequest request) {
        //得到user对象
        Mono<User> userMono = request.bodyToMono(User.class);
        return ServerResponse.ok().build(this.userService.insertUser(userMono));
    }

    //查询所有
    public Mono<ServerResponse> getAllUsers(ServerRequest request) {
        Flux<User> user = this.userService.getAllUser();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(user, User.class);
    }
}

第三步 初始化服务器,编写 Router
⚫ 创建路由的方法

public class Server {
    //创建Router路由
    public RouterFunction<ServerResponse> routingFunction(){
        UserService userService = new UserServiceIml();
        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);
    }
}

⚫ 创建服务器完成适配

//创建服务器完成适配
    public void creatReactorServer(){
        //路由和适配器
        RouterFunction<ServerResponse> route = routingFunction();
        HttpHandler httpHandler = toHttpHandler (route);
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

        //创建服务器
        HttpServer httpServer = HttpServer. create ();
        httpServer.handle(adapter).bindNow();

    }

⚫ 最终调用

public static void main(String[] args) throws IOException {
        Server server = new Server();
        server.creatReactorServer();
        System.out.println("enter to continue");
        System.in.read();
    }

在这里插入图片描述
在这里插入图片描述使用webclient调用

public class MyClient {
    public static void main(String[] args) {
        //调用服务器地址
        WebClient webClient = WebClient.create("https//127.0.0.1:64803");
        //根据id查询
        String id = "1";
        User user = webClient.get().uri("/users/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).block();
        System.out.println(user);


        //查询所有
        Flux<User> userFlux = webClient.get().uri("/users").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);

        userFlux.map(stu -> stu.getName()).buffer().doOnNext(System.out::println).blockFirst();
    }
}
尚硅谷-Spring5框架2020笔记 尚硅谷-Spring5框架2020最新版教程 文章目录 尚硅谷-Spring5框架2020最新版教程 一、Spring框架概述 1、Spring特点 2、Spring版本选择 3、入门案例 二、IOC容器 1、IOC底层原理 2、IOC接口(BeanFactory) 4、IOC操作 Bean 管理 5、IOC操作 Bean 管理(基于xml) 6、IOC操作Bean管理(xml注入其他类型属性) 7、IOC操作Bean管理(xml注入集合属性) 8、IOC操作Bean管理(FactotyB 阅读详情

相关推荐

Spring Framework中文文档的价值与核心功能解析

依赖注入(DI)和控制反转(IoC)是Spring Framework的核心特性,通过@Autowired注解或XML配置,开发者可以高效管理对象依赖关系。Spring提供了Spring MVC和WebFlux两种Web开发框架,分别适用于传统同步模型和高并发场景的响应式编程。在数据访问方面,Spring的抽象层支持JDBC、ORM等多种持久化技术,并通过@Transactional注解实现声明式事务管理。中文文档的翻译与维护需要解决术语统一、版本同步等挑战,采用Git仓库管理和CI/CD自动化构建确保质量

weixin_34248705的博客 2089

webflux切面拦截权限,webflux整合aop,webflux获取request

背景 在springboot+tomcat应用中获取request可以使用RequestContextHolder.getRequestAttributes()的方式来获取,此种方式的核心在于request所在容器被放在threadlocal中,但是webflux结合netty项目却不能这么使用,因为webflux是异步响应式的。 ...

qq_41633199的博客 8288

Spring核心原理全方位梳理:IOC、AOP、事务与自动配置

在Java后端开发中,依赖管理和对象创建是绕不开的基础问题。IOC容器通过控制反转将对象生命周期和依赖关系交给容器统一管理,从而大幅降低模块耦合;AOP则借助动态代理机制把日志、事务等横切逻辑从业务代码中剥离,使系统更易维护。理解Bean的完整创建过程、循环依赖的三级缓存方案,以及事务在代理环境下失效的原因,是排查线上问题和应对技术面试的关键。Spring Boot在核心框架之上引入了条件化自动配置,将繁杂的装配工作简化为“约定优于配置”,极大提升了工程落地效率。从容器到代理,从事务到自动化装配,这篇文章系

weixin_30781433的博客 344

spring5学习(ioc、aop、事务,webflux)

spring原理级重新学习,ioc、aop、事务、webflux

M1275601161的博客 897

Spring Framework核心特性与实战指南

依赖注入(DI)和面向切面编程(AOP)是现代Java开发的核心技术范式。Spring Framework作为企业级Java开发的事实标准,通过IoC容器实现对象生命周期管理,利用代理模式实现横切关注点分离。这些技术显著提升了代码的可维护性和可测试性,广泛应用于Web服务开发、微服务架构等领域。Spring Boot进一步简化了配置流程,提供自动装配、内嵌服务器等特性,成为构建云原生应用的首选框架。本文通过典型代码示例,解析Spring MVC架构、JdbcTemplate数据访问等核心模块的实现原理,并给

weixin_30582943的博客 235

Spring框架核心原理与云原生实践深度解析

控制反转(IoC)和依赖注入(DI)是现代Java框架的核心设计思想,通过解耦组件依赖关系提升代码可维护性。Spring框架作为Java生态的基石,其IoC容器通过三级缓存机制巧妙解决循环依赖问题,同时基于AOP实现声明式事务管理等企业级功能。在云原生时代,Spring Boot通过自动配置简化了微服务开发,与Nacos、Seata等组件的深度集成支持服务注册发现和分布式事务。结合GraalVM原生镜像和Spring AI等前沿技术,开发者可以构建高性能的智能应用系统。本文重点剖析Spring核心机制在微服

weixin_30765505的博客 446

webflux在切面@Aspect中获取请求头

打断点可以看到args包含了ReactorServerHttpRequest对象

qq_42028447的博客 1942

ArcGIS Pro2.5深度学习使用注意事项

ArcGIS Pro2.5深度学习使用中的注意事项1、ArcGIS Pro2.5深度学习使用中的注意事项1.1、Label深度学习样本工具参数介绍1.2、准备数据参数介绍1.3、训练模型介绍1.4、ArcGIS Pro2.5深度学习推理工具和模型对应关系介绍1.4.1、Classify Objects Using Deep Learning1.4.2、Classify Pixels Using D...

GIS哼哈哈的博客 7202

SpringBoot WebFlux记录接口访问日志

SpringBoot WebFlux 记录接口访问日志

皓月当空 2530

AI 技术新手入门与实战指南

仅仅能对话还不够,实际应用中,我们往往需要让 AI 处理特定的业务数据。比如,从日志文件中提取错误码,或者将非结构化的用户反馈整理成 JSON 格式。这时候,自定义输入和严格的结果验证就显得尤为重要。假设我们需要让 AI 分析一段服务器日志,并只返回错误的级别和简述。请分析以下日志片段,提取所有 ERROR 级别的信息。要求输出格式为 JSON 列表,包含 'timestamp' 和 'message' 两个字段。不要输出任何多余的解释文字。日志内容: {

2600_94936330的博客 2681

SMILES:化学结构的线性表示方法

SMILES:化学结构的线性表示方法

jasminefeng的博客 1万+

(十一)springboot实战——springboot3下关于WebFlux项目的一些常用功能整合

本节内容主要是对webflux项目一些常用功能的介绍,例如系统集成swagger接口文档,方便接口测试以及前后端项目联调测试;使用actuator完成系统各种指标的监控功能;系统使用logback日志框架完成项目日志的收集;使用过滤器WebFilter完成一些初始化验证;使用CorsWebFilter解决系统的跨域问题;使用AOP切面实现功能方法操作日志的打印等。

厉害哥哥的博客 2231

FOC控制必看:单/双/三电阻电流采样方案全对比(附NXP实战避坑指南)

本文深入对比了FOC控制中单电阻、双电阻和三电阻电流采样方案的优劣与适用场景,重点剖析了采样时序、窗口与ADC转换的工程挑战。针对NXP平台,提供了解决采样窗口过窄的实战方案与配置指南,帮助工程师根据成本与性能需求做出最优选择,并规避常见设计陷阱。

n2o3p4的博客 416

Spring5 新特性 和 IOC ,AOP, WebFlux

这里写目录标题一 SpringIOC容器1. IOC底层原理2. IOC接口(BeanFactory)3.IOC操作Bean管理(基于XML)4.Bean管理(1) 普通的Bean(2) 工厂bean(FactoryBean)(3) bean的作用域(4) bean的生命周期通过构造器创建bean实例为bean的属性值或对其他bean的引用配置初始化方法并调用获取对象容器关闭时 配置销毁方法 并调用4.IOC操作Bean管理(基于注解) 一 SpringIOC容器 1. IOC底层原理 控制反转 把对

weixin_43691773的博客 2344

西门子 G120 变频器报文

G120报文

mmxxoo_xxmmp的博客 8090

大模型之Spring AI实战系列(二十六):Spring Boot + DeepSeek 实战指南:构建AI聊天应用

在人工智能技术飞速发展的今天,大型语言模型已经成为了推动技术创新的重要力量。DeepSeek作为国内领先的大型语言模型提供商,其推出的DeepSeek系列模型在多个领域都展现出了卓越的性能。本文将详细介绍如何使用Spring Boot框架集成DeepSeek API,构建一个简单的AI聊天应用。我们将实现两个核心功能:普通文本生成和流式文本生成。

寻道AI,探索AI无限可能! 2264

Spring框架核心机制:IoC、AOP与事务管理深度解析

IoC(控制反转)和AOP(面向切面编程)是Spring框架的两大核心技术支柱。IoC容器通过依赖注入管理对象生命周期,其核心BeanFactory采用三级缓存机制解决循环依赖问题。AOP基于动态代理实现,支持方法拦截和增强,在日志记录、事务管理等场景发挥关键作用。Spring的事务管理建立在AOP基础上,通过@Transactional注解实现声明式事务控制。这些机制共同构成了Java企业级开发的基础架构,广泛应用于微服务、分布式系统等场景。理解Spring 5的CGLIB代理优化和Bean作用域选择对性

weixin_30251829的博客 640

【AI大模型:架构实战】31、工业级AI大模型系统全景解析:从架构设计到提示语引擎实践

本文系统解析了工业级AI大模型的核心架构与技术体系。整体采用分层解耦设计,包含数据工程、模型训练、模型部署、应用集成和监控运维五大核心模块,以及分布式计算、高速网络、异构硬件三大支撑体系。数据工程构建高质量数据处理流水线;模型训练实现从预训练到业务适配的全流程;模型部署优化服务性能与可用性;应用集成连接业务场景;监控运维保障系统持续优化。三大支撑能力专注于千亿参数管理、高并发低延迟处理和持续学习进化。该架构设计既满足当前需求,又为技术演进预留空间,为企业在数字化转型中提供AI工业化落地的完整解决方案。

2025博客之星Top81。专注AI工程化与架构实战。从分布式思维到模型部署,用工程化视角为你厘清AI落地的真实路径。 1990

Java-24 深入浅出 Spring 全景:从起源到 Spring 6 一文打通 IoC / AOP / 发展史

Spring 是面向 Java 平台的分层 Full-Stack 轻量级开源框架,以控制反转(IoC)和面向切面编程(AOP)为核心理念,简化企业级应用开发。本文系统讲解 Spring 的发展历程:2003 年 Rod Johnson 推出雏形,2004 年 3 月发布 1.0 正式版,2006 年 Spring 2.0 引入 XML Schema 命名空间,2009 年 12 月 Spring 3.0 基于 Java 5 并引入 SpEL,2013 年 12 月 Spring 4.0 全面支持 Java

谢谢你的喜欢 我们一起无限进步 5188

Oracle EBS Interface/API(42) -AP发票客制化付款申请

背景 在OA系统上,选择Oracle EBS的某张AP发票做付款申请,需要类似于锁定该发票以便付款,其他用户或者下次用户不能再次选择该发票做付款申请。 快速参考 参考点 内容 功能导航 N: AP->Invoices->Entry>Invoices 并发请求 None 基表 AP.AP_INV_SELECTION_CRITERIA_ALLAP.AP_SELECTED_INVOICES_ALL API 参考下面介绍 错误信息表 None 接口Form

陈显平的专栏-Oracle EBS 1200

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Spring核心原理与面试指南:IoC、Bean生命周期、AOP、事务一网打尽

控制反转与依赖注入是Java后端架构解耦的核心思想,它通过容器统一管理对象的创建与依赖装配,从根本上降低了模块间的耦合度。理解Bean的完整生命周期与三级缓存机制,能帮助开发者应对循环依赖和代理生成等复杂场景。而面向切面编程则借助动态代理,将日志、事务等横切逻辑从业务代码中剥离,提升系统可维护性。从Spring Boot的自动配置到Spring Cloud的分布式治理,这套生态已成为企业级应用的事实标准。本文基于Spring框架二十余年的设计沉淀,系统梳理从IoC容器、AOP原理到事务失效排查、微服务落地的

weixin_30443075的博客 420

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

一次搞清Spring 、Spring Boot、Spring Web MVC、Spring WebFlux

介绍Spring生态系统,辨析Spring、Spring Boot、Spring Web MVC和Spring WebFlux。

u013643074的博客 2488

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Spring 框架核心功能全解

AOP 是用来处理横切关注点(如日志、事务、安全)的技术。典型应用:记录日志、统一异常处理、权限控制、方法拦截等。功能模块作用简介IoC 容器管理 Bean 生命周期与依赖注入AOP抽离横切逻辑,如日志/权限/监控数据访问简化 JDBC/JPA/MyBatis 使用事务管理控制数据一致性与事务边界构建同步/响应式 Web 应用安全模块实现登录、权限、OAuth2 认证等测试支持提供自动化测试工具集成与注解。

Harry的博客 1038

尚硅谷Spring5笔记--自用

Spring5

weixin_47770259的博客 3662

Spring5学习笔记2(更新webflux,完)

1.jdbcTemplate 什么是jdbcTemplate? spring框架对JDBC进行封装,使用jdbcTemplate方便实现对数据库的操作 使用步骤 (1)导入jar包 (2)在spring配置文件中配置数据库连接池 (3)配置jdbcTemplate对象,注入DataSource 这里源码使用的是set方式 创建service类和创建dao类,在dao中注入jdbcTemplate对象 jdbcTemplate操作数据库-添加操作 创...

小旻 692

spring5学习总结

文章目录Spring5框架Springde的基本概述Spring是什么Spring有核心的部分:IOC和AopSpring 特点入门案例下载spring百度搜索 Spring.io下载地址:点击你所要下载的版本下载第一个链接 (spring-5.3.9-dist.zip)Ioc容器IOC概念ioc底层原理原始方法用工厂模式进行优化了解工厂模式:**IOC方法**xml解析反射IOC(接口)Spring提供了IOC容器实现的两种方式:(两个接口)ApplicationContext接口中的实现类IOC操作Be

qq_47664976的博客 1295
上一篇: 设计模式之设计模式七大原则(详解)单一职责原则、开闭原则、接口隔离原则、依赖倒置原则、迪米特法则、里氏替换原则、组合优于继承原则
下一篇: 数据结构(二)
·小脑斧·
博客等级 码龄7年 10粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值