Spring5的全细节回顾总结

概述:

https://cntofu.com/book/95/33-what-new-in-the-spring-framework.md
这个不错。
轻量级javaee框架。

针对于bean的生命周期进行管理。

解决企业应用开发的复杂性。

核心:

​ IOC:控制反转,把创建对象的过程交给spring管理。

​ AOP:面向切面,不修改源代码进行功能增强。

特点:

​ 1、方便解耦,简化开发;

​ 2、AOP编程的支持;

​ 3、方便程序的测试;

​ 4、方便整合其他框架;

​ 5、对JDBC的API进行了封装,方便事务的操作;

​ 6、源码经典;

https://repo.spring.io/ui/native/libs-release/org/springframework/spring/5.2.6.RELEASE/

bean的初使用:

1、导入jar包

在这里插入图片描述

这里边所有的包都是必须导入的。common-logging不能落下;导入包之后,才能配置下边的spring的xml配置文件,否则是没有的。

image-20230109094601361

2、配置jar包

在这里插入图片描述

3、代码:

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


    <bean id="user" class="com.pshdhx.spring5.User" ></bean>
</beans>
package com.pshdhx.spring5;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Auther: pshdhx
 * @Date: 2023/01/09/9:43
 * @Description:
 */
public class TestSpring5 {

    @Test
    public void testSpring5(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user);
        user.add();
    }
}

IOC容器

容器:说白了就是创建对象的工厂。

1、IOC底层原理

概念:控制反转,把对象的创建以及对象之间的调用过程,交给spring容器进行管理,降低了耦合度;

底层原理:

1、xml解析(IOC过程)

1、配置xml文件文件,配置要创建的对象

<bean id="dao" class="com.pshdhx.spring5.UserDao" ></bean>

2、第二步,有Service类和dao类,创建工厂类

class UserFactory{
	public static UserDao getDao(){
        String classValue = class属性值; //xml解析
        Class clazz = Class.forName(classValue);//通过反射创建对象
        return (UserDao)clazz.newInstance();
    }
}

我们只需要修改配置文件,耦合度进一步降低。

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

2、Spring提供IOC容器实现两种方式:两个接口。

​ BeanFactory:IOC容器的基本实现接口,是Spring内部的使用接口,不提供开发人员使用。

加载配置文件的时候看,不会创建对象,只有在获取对象或者是使用对象的时候,才会去创建对象。所以不应该在web项目中使用。因为我们希望在启动web项目的时候,就自动创建配置里边的对象,这些耗时、耗资源的过程在启动时就应该完成。

​ ApplicationContext接口:BeanFactory接口的子接口,提供更过更强大的功能,一般由开发人员进行使用。

加载配置文件时,就会把配置文件中的对象进行创建。
在这里插入图片描述

FileSystem:是盘符路径。

ClassPath:是src下的项目路径。

2、Bean管理

1、由Spring进行创建对象;

基于xml方式创建对象

public class User {
    private String userName;

    public User(String userName) {
        this.userName = userName;
    }

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

//org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in class path resource [bean1.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.pshdhx.spring5.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.pshdhx.spring5.User.<init>()

创建对象时,默认是执行的无参构造,如果没有无参构造,报错。此时此刻,无参构造被有参构造代替了,所以报错。

基于xml方式注入属性

DI:依赖注入,就是注入属性;

set方法注入
<bean id="user" class="com.pshdhx.spring5.User" >
    <property name="userName" value="pshdhx" />
</bean>
public class User {
    private String userName;
    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void add(){
        System.out.println("add..");
        System.out.println(this.userName);
    }
}
有参构造方法注入
    <bean id="user" class="com.pshdhx.spring5.User" >
<!--        <property name="userName" value="pshdhx" /> 无参构造 注入 -->
        <constructor-arg name="userName" value="pshdhx" />
    </bean>

    <bean id="user" class="com.pshdhx.spring5.User" >
<!--        <property name="userName" value="pshdhx" /> 无参构造 注入 -->
<!--        <constructor-arg name="userName" value="pshdhx" />-->
        <constructor-arg index="0" value="pshdhx2" />
    </bean>
public class User {
    private String userName;
    public User(String userName) {
        this.userName = userName;
    }

    public void add(){
        System.out.println("add..");
        System.out.println(this.userName);
    }
}
p名称空间方式注入
<bean id="user" class="com.pshdhx.spring5.User" p:userName="pshdhx"></bean>
public class User {
    private String userName;

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void add(){
        System.out.println("add..");
        System.out.println(this.userName);
    }
}
xml方式注入其他类型
设置字面量,可以null
<bean id="user" class="com.pshdhx.spring5.User">
    <property name="userName">
        <null></null>
    </property>
</bean>
属性值包含特殊符号
 <bean id="user" class="com.pshdhx.spring5.User">
        <property name="userName">
<!--            <null></null>-->
            <value><![CDATA[<<pshdhx>>]]></value>
        </property>
    </bean>
注入外部bean

1、注册两个bean,service和dao;

2、service层要有dao的属性,然后设置set方法;

3、一个bean的属性是另外一个bean,ref指向其id;

<property name="userName" ref="serviceImpl" />
注入内部bean和级联赋值

1、内部bean

例如员工和部门,在员工实体类中添加部门对象;

<bean id="Emp" class="com.pshdhx.spring5.emp">
    <property name="dept">
        <bean id="dept" class="com.pshdhx.spring5.Dept">
            <property name="dname" value="部门名称1"></property>
        </bean>
    </property>
</bean>
注入属性级联赋值
<bean id="Emp" class="com.pshdhx.spring5.emp">
        <property name="dept" ref="dept" />
    </bean>
    <bean id="dept" class="com.pshdhx.spring5.Dept">
        <property name="dname" value="部门名称1"></property>
    </bean>

或者是:

<bean id="Emp" class="com.pshdhx.spring5.vo.Emp">
    <property name="ename" value="ename-pshdhx" />
    <property name="dept" ref="dept" />
    <property name="dept.dname" value="dept.dname.pshdhx" />
</bean>
<bean id="dept" class="com.pshdhx.spring5.vo.Dept">
</bean>
public class Emp {
    private String ename;
    private Dept dept;

    public void setEname(String ename) {
        this.ename = ename;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public Dept getDept() {
        return dept;
    }

    public void add(){
        System.out.println("emp-add"+ename+"::"+dept);
    }
}

此时要注意:dept.name 要在Emp类中设置getDept的方法;

注入集合属性

注入map list set arr

<bean id="stu" class="com.pshdhx.spring5.vo.Stu">
    <!--数组类型属性注入-->
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--list类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>小三</value>
        </list>
    </property>
    <!--map类型属性注入-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
    <!--set类型属性注入-->
    <property name="sets">
        <set>
            <value>MySQL</value>
            <value>Redis</value>
        </set>
    </property>
    <property name="courseList">
            <ref bean="course1"></ref>
            <ref bean="course2"></ref>
        </property>
</bean>

集合属性提取到外边

<!--第一步:在 spring 配置文件中引入名称空间 util-->
<?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:util="http://www.springframework.org/schema/util" <!--添加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 集合注入提取-->
<!--把集合注入部分提取出来-->
 <!--1 提取list集合类型属性注入-->
    <util:list id="bookList">
        <value>易筋经</value>
        <value>九阴真经</value>
        <value>九阳神功</value>
    </util:list>

 <!--2 提取list集合类型属性注入使用-->
    <bean id="book" class="com.pshdhx.spring5.vo.Book" scope="prototype">
        <property name="list" ref="bookList"></property>
    </bean>

3、工厂模式降低耦合度:

在这里插入图片描述

4、Bean类型

一种是普通Bean:在配置文件中定义bean类型就是返回类型;

一种是factoryBean:在配置文件中定义bean类型和返回类型不一致;

factoryBean的代码如下,是根据泛型返回的。

public class MyBean implements FactoryBean<Course> {
    //定义返回bean
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }
}
<bean id="myBean" class="com.pshdhx.spring5.vo.MyBean">
</bean>
@Test
public void test3() {
	ApplicationContext context =
		new ClassPathXmlApplicationContext("bean3.xml");
    //xml里定义的是MyBean类型的,而返回值是Course类型的
	Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
	System.out.println(course);
}

5、Bean的scope属性

单实例和多实例,spring5 只剩下了这两个了。

<bean id="stu" class="com.pshdhx.spring5.vo.Stu" scope="prototype">
<bean id="stu" class="com.pshdhx.spring5.vo.Stu" scope="singleton">

6、Bean的生命周期

创建到销毁的过程:

​ 1、通过构造器进行创建(无参构造)

​ 2、对其他Bean的引用和赋值,设置set方法的过程-property。

​ 3、调用bean的初始化的方法(初始化前后把bean实例传递前置处理器和后置处理器)

​ 4、bean可以获取到使用

​ 5、当容器在关闭的时候,调用Bean销毁的方法。

<bean id="stu" class="com.pshdhx.spring5.vo.Stu" scope="prototype" init-method="" destroy-method="">
    <bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">	
    <property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>

<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
public class MyBeanPost implements BeanPostProcessor {//创建后置处理器实现类
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}

7、基于xml方式自动装配

手动装配:就是通过ref指向或者是value值进行赋值操作。

自动装配:根据指定的装配规则,(属性名称或者是属性类型)Spring将自动匹配的属性值进行注入操作。

Bean标签属性autowire ,属性自动装配;

autowire:属性常用的有两个值

​ ByName:根据属性名称自动注入;和bean的标签的id一样;

​ ByType:根据属性值类型进行注入;和bean标签的class一样;【如果还有个teacher1的话,就报错。不知道找到那个bean了】

<bean id="stu" class="com.pshdhx.spring5.vo.Stu" scope="prototype" init-method="" destroy-method="" autowire="byName">
    <property name="teacher" ref="teacher"></property>
</bean>
<bean id="teacher" class="com.pshdhx.spring5.vo.Teacher"></bean>
<!--    <bean id="teacher1" class="com.pshdhx.spring5.vo.Teacher"></bean> byType就报错了 -->

8、引入外部属性文件

db.properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/shangguigu
jdbc.username=root
jdbc.password=pshdhx

1、引入上下文的名称空间context

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
        <property name="driver" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

9、基于注解管理Bean

注解:是代码中的特殊标记。格式:@注解名称(属性名称=属性值,属性名称=属性值…)

可以作用在类中,方法中,属性中。

为什么用注解:简化xml配置;

1、创建对象

@Component【普通】

@Service【Service层】

@Controller【控制层】

@Repository【Dao层】

1、如果需要做注解,需要引入AOP依赖。【spring-aop-5.2.6.RELEASE.jar】

2、开启组件扫描,需要知道那些类中有注解,进而创建bean。需要引入context名称空间

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
        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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:db.properties" ></context:property-placeholder>

3、扫描配置

<context:component-scan base-package="com.pshdhx.spring5" use-default-filters="false">
    <!-- 只扫描Controller注解 -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <!-- 不扫描Service注解 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>

2、基于注解进行属性注入

1、@Autowired 根据属性类型进行自动装配

2、@Qualifer 根据属性名称自动注入

3、@Resource 根据属性类型或者是根据属性名称进行自动注入【啥都不写是根据类型注入。@Resource(name=“userServiceImpl1”)根据名称进行注入】

4、@Value(${abc.def}) :可以注入普通属性。@Value(“abc”)

如果根据类型进行属性注入,那么有多个实现类怎么办呢?

如果使用@Autowired,就不知道找哪个了

需要再添加上@Qualifier(“userDaoImpl”),根据名称进行注入。

或者是直接注入其实现类。不注入接口了。

@Repository
public class UserDao {
    public int ins(){
        System.out.println("userDao . ins ....");
        return 0;
    }
}

public interface UserService {

    public int ins();
}

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public int ins() {
        userDao.ins();
        return 0;
    }
}

注意:注解能否添加在接口上?不能。一般都在其实现类上添加注解;

3、完全注解开发

1、做一个配置类,替代xml文件。

@Configuration
@ComponentScan(basePackages = "com.pshdhx.spring5")
public class Spring5Config {

}
    @Test
    public void testSpringAnotation(){
        ApplicationContext context = new AnnotationConfigApplicationContext(Spring5Config.class);
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.ins();
//        String[] beanDefinitionNames = context.getBeanDefinitionNames();
//        System.out.println(Arrays.asList(beanDefinitionNames));
    }

AOP

基本概念:

面向切面编程,可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高了开发的效率。

通俗描述:不通过修改原始代码的方式,在主干功能里边添加新功能;

1、AOP底层原理-代理

AOP的底层使用了动态代理的方式进行实现的。

1、有接口情况,使用JDK动态代理

​ 1、创建接口实现类的代理对象(等同于Impl的代理对象)

2、没有接口情况,使用Cglib动态代理。

​ 1、使用继承的方式。创建当前类的子类的代理对象。

public class User{
    public void add(){
        
    }
}
public class User2 extends User{
    public void add(){
        //...
        super.add();
        //...
    }
}

2、jdk的动态代理

java.lang.reflect

class proxy

static object newProxyInstance(CloassLoader loader,类<?>[] interfaces,InvocationHandler h)

//返回指定接口代理类的实例,该接口的方法调用分派给指定的调用处理程序

public class TestJdkProxy {
    public static void main(String[] args) {
        Class interfaces[] = {UserDao.class};
        JdkProxy proxyInvoke = new JdkProxy(new UserDaoImpl());
        UserDao userDao = (UserDao) Proxy.newProxyInstance(TestJdkProxy.class.getClassLoader(), interfaces, proxyInvoke);
        int addRes = userDao.add(3, 4);
        System.out.println("finally=="+addRes);
    }
}
class JdkProxy implements InvocationHandler {

    private Object obj;

    public JdkProxy(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行之前");
        Object res = method.invoke(obj,args); //此处一定不能是proxy,陷入死循环
        System.out.println("方法执行之后");
        return res;
    }
}

3、术语

连接点:类中那些方法可以被增强,这些方法被称为连接点。

切入点:实际被增强的方法,被称为切入点。

切面:把通知应用到切入点的过程。

通知(增强):增强的逻辑部分。通知有多重类型。

​ 1、前置通知

​ 2、后置通知

​ 3、环绕通知

​ 4、异常通知

​ 5、最终通知

4、AOP-AspectJ包

其本身并不是spring的一部分,它是一个单独的AOP框架。

基于AspectJ的方式有两种,基于注解和基于xml的方式。

1、引入依赖

spring-aspects-5.2.6.RELEASE.jar
cglib-3.0.jar
aspectjweaver-1.9.7.jar
aopalliance-1.0.jar

2、语法结构

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.. (…))

5、基于注解配置AOP

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

    <context:component-scan base-package="com.pshdhx.spring5.aop"></context:component-scan>

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


</beans>
@Component
public class User {
    public void add() {
        System.out.println("add.......");
    }
}
//4、配置不同类型的通知
@Component
@Aspect  //生成代理对象
public class UserProxy {
    @Before("execution(* com.pshdhx.spring5.aop.User.add(..))")
    public void before() {//前置通知
        System.out.println("before......");
    }
    @Around("execution(* com.pshdhx.spring5.aop.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//环绕通知
        System.out.println("around环绕之前...");
        proceedingJoinPoint.proceed();
        System.out.println("around环绕之后...");
    }
    @AfterReturning("execution(* com.pshdhx.spring5.aop.User.add(..))")
    public void afterReturning() {//后置返回通知
        System.out.println("afterReturning......");
    }
    @AfterThrowing("execution(* com.pshdhx.spring5.aop.User.add(..))")
    public void afterTrowing() {//异常通知
        System.out.println("afterTrowing......");
    }
    @After("execution(* com.pshdhx.spring5.aop.User.add(..))")
    public void after() {//后置通知
        System.out.println("after......");
    }
}

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user);
        user.add();

    }
}

结果:

around环绕之前…
before…
add…
around环绕之后…
after…
afterReturning…

【正常情况下没有throwing。】

com.pshdhx.spring5.aop.User@5ab9e72c
around环绕之前…
before…
after…
afterTrowing…

【一旦出现throwing,则会没有afterReturning和环绕之后around】

抽取相同的切入点
//相同切入点抽取
@Pointcut(value = "execution(* com.pshdhx.spring5.aop.User.add(..))")
public void pointdemo() {

}

@Before("pointdemo()")
public void before() {//前置通知
    System.out.println("before......");
}
通知

@Order(1) 在代理类中添加注解,值越小,优先级越高。

6、基于xml配置AOP

public class User {
    public void buy(){
        System.out.println("buy...");
    }
}
public class UserProxy {
    public void befor(){
        System.out.println("buy ...before...");
    }
}
<bean id="user" class="com.pshdhx.spring5.aopxmlconfig.User"></bean>
<bean id="userProxy" class="com.pshdhx.spring5.aopxmlconfig.UserProxy"></bean>


<aop:config>
    <aop:pointcut id="p" expression="execution(* com.pshdhx.spring5.aopxmlconfig.User.buy(..))"/>
    <aop:aspect ref="userProxy">
        <aop:before method="befor" pointcut-ref="p"/>
    </aop:aspect>
</aop:config>

不用实现任何接口,例如BeforeAdvice等接口。接口是不引入aspectj的实现方式。详情可见spring4的那一篇文章。

7、完全基于注解

@Configuration
@ComponentScan(basePackages = "com.pshdhx.spring5")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Spring5Config {

}

jdbcTemplate

概述

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

新增操作

引入相关的jar包;

druid-1.1.9.jar
mysql-connector-java-5.1.49.jar
spring-jdbc-5.2.6.RELEASE.jar
spring-orm-5.2.6.RELEASE.jar
spring-tx-5.2.6.RELEASE.jar

<context:property-placeholder  location="classpath:db.properties"></context:property-placeholder>
<context:component-scan base-package="com.pshdhx.spring5.jdbctemplate"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
      destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClass}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入 dataSource-->
    <property name="dataSource" ref="dataSource"></property><!--set方式注入-->
</bean>
public interface BookDao {
    int add(Book book);
}
@Repository
public class BookDaoImpl implements BookDao {


    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public int add(Book book) {
        String sql = "insert into book values (?,?,?)";
        Object obj[] = {book.getBookId(), book.getBookName(), book.getPrice()};
        int update = jdbcTemplate.update(sql, obj);//影响的行数
        return update;
    }
}
public class Book {
    private String bookId;
    private String bookName;
    private Double price;

    public Book(String bookId, String bookName, Double price) {
        this.bookId = bookId;
        this.bookName = bookName;
        this.price = price;
    }

    public String getBookId() {
        return bookId;
    }

    public void setBookId(String bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }
}
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;

    public void addBook(Book book){
        bookDao.add(book);
        System.out.println("插入完成");
    }
}
CREATE TABLE `book` (
  `book_id` varchar(255) NOT NULL,
  `book_name` varchar(255) DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--jdbc.url=jdbc:mysql://localhost:3306/shangguigu?useUnicode=true&characterEncoding=utf-8&useSSL=false

新增,删除和修改,如上所示,都用update。

查询count(*)

public int selectCount() {
        String sql = "select count(*) from t_book";
		//queryForObject方法中:第一个参数代表--sql语句;第二个参数代表--返回类型class  
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
}

查询返回对象

public List<Book> findAllBook() {
        String sql = "select * from t_book";
        //调用方法
        List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
    }

查询返回集合

public List<Book> findAllBook() {
        String sql = "select * from t_book";
        //调用方法
        List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
 }

批量添加

    public void batchAddBook(List<Object[]> batchArgs) {
        String sql = "insert into t_book values(?,?,?)";
		//batchUpdate方法 第一个参数:sql语句		第二个参数:List集合,添加多条记录数据
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        System.out.println(Arrays.toString(ints));
    }

批量修改和删除

public void batchUpdateBook(List<Object[]> batchArgs) {
    String sql = "update t_book set username=?,ustatus=? where user_id=?";
    int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
    System.out.println(Arrays.toString(ints));
}

spring的事务

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

事务特性acid

原子性:要么都成功,要么都失败。

一致性:操作之前和操作之后,总量不变。例如:银行卡之间转账,减多少,就得加多少,钱的总量不变。

隔离性:多事务操作的时候,不会产生影响。

持久性:事务提交之后,不会发生变化了。

搭建事务操作环境

基于xml实现事务

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

<!-- 配置声明式事务,要想其生效,就必须有切点(基于通知)。真正控制那些方法实现事务,则必须控制具体的方法 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="userInsert" propagation="REQUIRED" isolation="SERIALIZABLE" rollback-for="java.lang.Exception"/>
			<tx:method name="ins*"/>
			<tx:method name="del*"/>
			<tx:method name="upd*"/>
		</tx:attributes>
	</tx:advice>

	<aop:config>
		<aop:pointcut id="mypoint" expression="execution(* com.pshdhx.service.impl.*.*(..))"/>
		<!-- 声明式事务相当于通知,多饶了一个圈 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint" />
	</aop:config>

基于注解实现事务

1、引入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/aop/spring-tx.xsd">

2、创建事务管理器,给数据源绑定事务

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

3、使用注解方式开启事务

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

4、给方法或者类 添加注解

@Service
@Transactional
public class BookService {
    
}

事务的传播行为

当一个事务方法被另一个事务方法调用的时候,这个事务方法如何进行处理。

propagation:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1b3wHrSY-1673500918107)(../../project/spring5/事务传播属性.png)]

required:方法a调用方法b,如果方法a有事务,则方法b在方法a的事务中运行。如果方法a没有事务,则方法a新开一个事务,方法b在方法a的事务中运行。

​ 所以方法a使用都有事务,不管方法b是否有事务,都在方法a的事务中运行

required_new:方法a调用方法b,调用方法b时,必须启动新的事务,新的事务对方法b有效。方法a的事务则进行挂起操作。如果此时,方法a调用方法b出现了 问题,则方法b根据其事务进行提交或者是回滚,但是方法a的事务不会进行回滚。换句话说:方法a的事务和方法b的事务没有任何关系。

supports:如果方法b运行的环境中存在事务,则方法B就按照环境中的事务进行;如果没有,自己搞自己的。

外层有事务,按照外层来。没有事务,按照自己的来。

隔离级别

多事务之间操作不会产生影响。

脏读

一个事务A读取到另一个未提交事务B的数据。事务B未提交的数据可能会发生改变,事务A读取到的数据为脏数据。

幻读(表锁)

事务A按照特定条件查询出结果,事务B去新增了一条符合条件的数据。

事务A中查出的数据和数据库中的数据不一致,事务A好像出现了幻觉,此时加表锁

特点:

针对于的操作是**新增和删除**数据;

两次事务的结果。

不可重复读(行读)

当事务A在第一次读取数据后,事务B对事务A读取的数据进行了修改,事务A中再次读取的数据和之前读取的数据不一致,该过程为不可重复读。

特点:

主要是针对于**某行数据,或者是行中的某一列**。

主要是针对的是**修改操作**。

两次读取在同一个事务内。

场景:张三去取钱,发现余额为20000,想要取出15000。与此同时,他老婆取出了15000,张三再取钱时,发现钱不够了。所以要在读取到20000余额时,对该数据加上行锁

解决读问题

通过设置事务隔离性,解决读问题。

脏读不可重复读幻读
read uncommitted(读未提交)–级别最低,效率最高,问题最大。
read committed(读已提交) --解决脏读
repeatable read(可重复度)–解决脏读和不可重复读(解决表锁问题)
serializable(串行化)–事务之间排队-- 解决行锁问题。
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
public class BookService {

}

mysql默认的隔离级别是【可重复读】

oracle和SQL server 都是【读已提交】

timeout:事务在一定时间内要提交,不能一直占据事务,要不然回滚。

​ 默认值是-1,时间单位是秒。

readonly:是否只读。

​ 默认值:false,可以查询,可以增删改操作。

​ 设置为true之后,只可以做读操作,不可以做增删改操作。

rollbackFor:回滚。

​ 设置哪些出现的异常可以进行回滚。

noRollback:不会滚。

​ 设置那些出现的异常不进行回滚。

完全注解方式

@Configuration  //配置类
@ComponentScan(basePackages = {"com.pshdhx.spring5"})  //组件扫描
@EnableTransactionManagement    //开启事务
public class TxConfig {

    /**
     * 德鲁伊数据源
     * 创建数据库连接池
     * @return {@link DruidDataSource}
     */
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3307/user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("lcj6445254");
        return dataSource;
    }

    /**
     * 获得jdbc模板
     * 创建JdbcTemplate对象
     * @param dataSource 数据源
     * @return {@link JdbcTemplate}
     */
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        //到IOC容器中根据类型找DataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入DataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    /**
     * 事务管理器获取数据源
     * 创建事务管理器
     * @param dataSource 数据源
     * @return {@link DataSourceTransactionManager}
     */
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

spring新特性

https://cntofu.com/book/95/33-what-new-in-the-spring-framework.md

spring5只能整合log4j2-基于jdk8

log4j-api-2.11.2.jar
log4j-core-2.11.2.jar
log4j-slf4j-impl-2.11.2.jar
slf4j-api-1.7.30.jar

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级顺序 off > fatal > error > warn >info >debug > trace > all-->
<!--configuration后边的status 用于设置log4j2自身内部的信息输出 可以不设置,当设置成true时,可以看到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才会生效-->
    <loggers>
        <!--    root:用于指定项目的根日志,如果没有单独指定logger,则会使用root作为默认的日志输出-->
        <root level="info">
            <appender-ref  ref="Console"/>
        </root>
    </loggers>
</configuration>
private static final Logger log = LoggerFactory.getLogger(TestSpring5.class);
@Test
public void testLog4j2(){
    log.info("log4j2 ...");
    log.warn("log4j2 ...");
    log.error("log4j2 ...");
    log.debug("log4j2 ...");
}

@Nullable注解

可以使用在方法上边,属性上边,参数上边。表示方法的返回值可以为空,属性值可以为空,参数值可以为空。

函数式风格创建对象

//函数式风格创建对象
@Test
public void testGenericApplicationContext(){
    //1、创建GenericApplicationContext对象
    GenericApplicationContext context = new GenericApplicationContext();
    //2、调用context方法进行对象的注册
    context.refresh();
    context.registerBean(User.class,()->new User());
    User user = (User) context.getBean("com.pshdhx.spring5.aopannotation.User");
    System.out.println(user);
}

整个JUnit5单元测试框架

1、引入依赖包

spring-test-5.2.6.RELEASE.jar

package com.pshdhx.spring5;

import com.pshdhx.spring5.jdbctemplate.entity.Book;
import com.pshdhx.spring5.jdbctemplate.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Auther: pshdhx
 * @Date: 2023/01/11/15:03
 * @Description:
 */
@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架版本
@ContextConfiguration("classpath:jdbctemplate.xml")//加载外部配置文件
public class Junit4Test {

    @Autowired
    private BookService bookService;

    @Test
    public void test1(){
        Book book = new Book("8848", "book_name1", 8848.99);
        bookService.addBook(book);
    }
}

external library里边有了junit4的依赖。

2、根据@Test注解,导入junit5的包,之后使用junit5的注解。

package com.pshdhx.spring5;

import com.pshdhx.spring5.jdbctemplate.entity.Book;
import com.pshdhx.spring5.jdbctemplate.service.BookService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Auther: pshdhx
 * @Date: 2023/01/11/15:03
 * @Description:
 */
//@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架版本
//@ContextConfiguration("classpath:jdbctemplate.xml")//加载外部配置文件
@SpringJUnitConfig(locations = "classpath:jdbctemplate.xml")
public class Junit4Test {

    @Autowired
    private BookService bookService;

    @Test
    public void test1(){
        Book book = new Book("8849", "book_name1", 8848.99);
        bookService.addBook(book);
    }
}

Webflux-函数式编程模型

前置知识:

SpringMVC

SpringBoot

Maven

java8新特性 lamda表达式,stream流。

简介:

1、是Spring5添加的新的模块,用于web开发的。功能和SpringMVC类似的,WebFlux使用当前一种比较流行的响应式编程出现的框架。

2、使用传统的web框架,比如SpringMVC,这些基于Servlet容器,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。

异步和同步:

​ 针对调用者。

阻塞和非阻塞:

​ 针对被调用者。A调用B,B给了反馈,就是非阻塞。没有给反馈,就是阻塞。

​ 阻塞需要等待反馈,非阻塞则不需要。

webflux优势:

​ 1、异步非阻塞:在不扩充机器的情况下,可以提高系统吞吐量;

与SpringMVC的区别

image-20230111155349009

springMVC采用的是命令式编程,spring webflux采用的是响应式编程。

响应式编程:excel求和,数据变化,结果变化。

public class Observer extends Observable {
    public static void main(String[] args) {
        Observer observer = new Observer();
        observer.addObserver((item,arg)->{
            System.out.println("add observer 01  ...");
        });
        observer.addObserver((item,arg)->{
            System.out.println("add observer 02  ...");
        });

        observer.setChanged();

        observer.notifyObservers();
    }
}

响应式编程:

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.1.5.RELEASE</version>
</dependency>
Flux.just(1,2,3,4);
Flux.just(1);

Integer []arr = {1,2,3};
Flux.fromArray(arr);

List<Integer> list = Arrays.asList(arr);
Flux.fromIterable(list);

Stream<Integer> stream = list.stream();
Flux.fromStream(stream);

三种信号特点:

1、错误信号和完成信号都是终止信号,不能共存的。

2、如果没有发送任何元素值,而是直接发送错误或者是完成信号,表示空数据流。

3、如果没有错误信号,没有完成信号,表示是无限数据流。

//订阅之后才能输出
Flux.just(1222).subscribe(System.out::println);
Flux.just(3344).subscribe(System.out::println);

声明:调用just或者是其他方法知识声明数据流,数据流并没有发出,只有订阅之后,才会触发数据流,不订阅什么都不会发生的。

操作符

map

flatmap

把里边的若干个元素变为若干个流,然后进行流的合并,成为一个大流。

Netty的NIO

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RSfvMAuJ-1673500918107)(../../project/spring5/NIO的selector模型.png)]

高性能,异步,非阻塞框架。

核心API

SpringWebFlux核心控制器DisPatchHandler,实现接口WebHandler。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
package org.springframework.web.server;

import reactor.core.publisher.Mono;

public interface WebHandler {
    Mono<Void> handle(ServerWebExchange var1);
}
public Mono<Void> handle(ServerWebExchange exchange) {//放http请求信息
    return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
        return mapping.getHandler(exchange);//根据请求地址,获取对应的mapping
    }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
        return this.invokeHandler(exchange, handler);//调用具体的业务方法
    }).flatMap((result) -> {
        return this.handleResult(exchange, result);//返回处理结果
    });
}

SpringWebflux里边的DispatchHandler,负责请求的处理

HandlerMapping:请求查询到处理的方法。

HandlerAdapter:真正负责请求处理。

HandlerResultHander:响应结果处理。

函数式编程的接口:

​ RouterFunction:路由处理

​ HandlerFunction:具体方法处理

注解编程模型:

SpringMVC实现:同步阻塞方式,基于SpringMVC+Servlet+Tomcat

SpringWebFlux实现:异步非阻塞方式,基于SpringWebflux+reactor+Netty

代码-已验证

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
@Data
@AllArgsConstructor
public class Book {
    private Integer bookId;
    private String bookName;
    private Double price;

}

public interface BookService {

    Mono<Book> queryBookById(int id);

    Flux<Book> queryBookAll();

    Mono<Void> addBook(Mono<Book> monoBook);

}

@Service
public class BookServiceImpl implements BookService {

    private Map<String,Book> map = new HashMap<>();

    public BookServiceImpl(){
        map.put("1",new Book(1,"book1",1.1));
        map.put("2",new Book(2,"book2",1.2));
        map.put("3",new Book(3,"book3",1.3));
        map.put("4",new Book(4,"book4",1.4));
    }

    @Override
    public Mono<Book> queryBookById(int id) {
        return Mono.justOrEmpty(this.map.get(id+""));
    }


    @Override
    public Flux<Book> queryBookAll() {
        return Flux.fromIterable(this.map.values());
    }

    @Override
    public Mono<Void> addBook(Mono<Book> monoBook) {
        return monoBook.doOnNext(item->{
            int id = map.size()+1;
            map.put(id+"",item);
        }).thenEmpty(Mono.empty());
    }
}

@RestController
public class BookController {


    @Autowired
    private BookService bookService;

    @GetMapping("/book/{id}")
    public Mono<Book> getBookById(@PathVariable int id){
        Mono<Book> bookMono = bookService.queryBookById(id);
        return bookMono;
    }

    @GetMapping("/book/queryAll")
    public Flux<Book> getAllBooks(){
        Flux<Book> bookFlux = bookService.queryBookAll();
        return bookFlux;
    }

    @PostMapping("/book/addBook")
    public Mono<Void> addBook(@RequestBody Book bookMono){
        Mono<Void> voidMono = bookService.addBook(Mono.just(bookMono));
        return voidMono;
    }
}

函数式编程模型:

1、在使用函数式编程模型操作的时候,需要自己初始化服务器;

2、基于函数式编程模型时候,有两个核心接口:

​ RouterFunction:实现路由功能,请求转发给对应的handler

​ HandlerFunction:处理请求生成响应的函数。

核心任务时定义两个函数式接口的实现,且需要启动需要的服务器。

SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse。

代码-已验证

package com.pshdhx.reactor3.webflux.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @Auther: pshdhx
 * @Date: 2023/01/12/9:40
 * @Description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
    private Integer bookId;
    private String bookName;
    private Double price;

}
package com.pshdhx.reactor3.webflux.handler;

import com.pshdhx.reactor3.webflux.entity.Book;
import com.pshdhx.reactor3.webflux.service.BookService;
import com.pshdhx.reactor3.webflux.service.impl.BookServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * @Auther: pshdhx
 * @Date: 2023/01/12/10:41
 * @Description:
 */
public class BookHandler {

    BookService bookService = new BookServiceImpl();

    public Mono<ServerResponse> getBookById(ServerRequest request){
        String id = request.pathVariable("id");
        //空值处理
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        Mono<Book> bookMono = this.bookService.queryBookById(Integer.parseInt(id));
        return bookMono.flatMap(item->ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(fromObject(item)))
                .switchIfEmpty(notFound);
    }

    public Mono<ServerResponse> getAllBook(ServerRequest request){
        Flux<Book> bookFlux = this.bookService.queryBookAll();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(bookFlux,Book.class);
    }

    public Mono<ServerResponse> addBook(ServerRequest request){
        Mono<Book> bookMono = request.bodyToMono(Book.class);
        return ServerResponse.ok().build(this.bookService.addBook(bookMono));
    }

}
package com.pshdhx.reactor3.webflux.server;

import com.pshdhx.reactor3.webflux.entity.Book;
import com.pshdhx.reactor3.webflux.handler.BookHandler;
import com.pshdhx.reactor3.webflux.service.BookService;
import com.pshdhx.reactor3.webflux.service.impl.BookServiceImpl;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.netty.http.server.HttpServer;

import java.io.IOException;

import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;

/**
 * @Auther: pshdhx
 * @Date: 2023/01/12/10:55
 * @Description:
 */
public class BookServer {

    //创建router路由
    public RouterFunction<ServerResponse> routingFunction() {
        BookService bookService = new BookServiceImpl();
        BookHandler bookHandler = new BookHandler();
        return RouterFunctions.route(
                GET("/books/{id}")
                        .and(accept(APPLICATION_JSON)), bookHandler::getBookById)
                .andRoute(GET("/books").and(accept(APPLICATION_JSON)), bookHandler::getAllBook);
    }

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

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

    //调用
    public static void main(String[] args) throws IOException {
        BookServer server = new BookServer();
        server.createReactorServer();
        System.out.println("enter to exit");
        int read = System.in.read();
    }

}

WebClient调用

public class BookClient {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create("http://127.0.0.1:1475");

        String id = "1";
        Book book = webClient.get().uri("/books/{id}", id)
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(Book.class)
                .block();
        System.out.println(book.toString());

        Flux<Book> bookFlux = webClient.get().uri("/books").accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(Book.class);

        bookFlux.map(itme->itme.getBookName()).buffer().doOnNext(System.out::println).blockFirst();

    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值