Spring5的全细节回顾总结

news2024/11/24 5:27:43

概述:

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

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/157640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第一天 Blender操作 | 大帅老猿threejs特训【超详细】

前言 这一天主要是基础理论的学习。 本人学习资料仓库 https://gitee.com/zhang_dezheng_hsr/three-demo.git YCY-TrainingCamp-S2: 在原有的文件上添加本人的学习记录 第一天 Blender操作 | 大帅老猿threejs特训【超详细】【我在掘金的同名文章】 一、大纲 二、THREE 基础概…

Linux查看某个应用的CPU/内存/网卡使用情况

1.查看CPU的使用率 # ps -ef | grep zabbix 进程号是1715 # top -p 1715 可以看到CPU的使用率是0 &#xff0c; 内存的使用率是0 2.查看内存真实使用了多少 #cat /proc/[pid]/status #cat /proc/1715/status VmPeak:进程所使用的虚拟内存的峰值 VmSize: 进程当前使用…

22.字符串初始化方法及赋值,字符串和指针总结

目录 初始化 1.字符数组初始化 2.指针指向文字常量区&#xff0c;初始化 3.指针指向堆区&#xff0c;堆区存放字符串 使用时赋值 1.字符数组&#xff0c;使用scanf或者strcpy 2.指针指向文字常量区 3.指针指向堆区&#xff0c;堆区存放字符串 初始化 1.字符数组初始化 …

各国家语言代码对照表

来源如下 Language Code Tablehttp://www.lingoes.cn/zh/translator/langcode.htm 详情如下 语言代码语言名称af南非语af-ZA南非语ar阿拉伯语ar-AE阿拉伯语(阿联酋)ar-BH阿拉伯语(巴林)ar-DZ阿拉伯语(阿尔及利亚)ar-EG阿拉伯语(埃及)ar-IQ阿拉伯语(伊拉克)ar-JO阿拉伯语(约旦…

万字详解 Linux 网络管理

万字详解 Linux 网络管理1.Linux处理数据包过程2.和网络相关的几个文件说明网卡配置文件ifcfg-*DNS配置文件/etc/resolv.conf&#xff08;CentOS6环境&#xff09;/etc/services3.网络接口配置和主机名ifconfigifcfghostname命令4.网关/路由5.网关/路由相关命令route命令配置永…

netty(1):NIO 基础之三大组件

1 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream&#xff0c;它就是读写数据的双向通道&#xff0c;可以从 channel 将数据读入 buffer&#xff0c;也可以将 buffer 的数据写入 channel&#xff0c;而之前的 stream 要么是输入&#xff0c;要么是输出&…

如何用idea快速的debug本地程序

介绍大家都经常用idea开发, 开发过程中运行程序就会出现各种意料之外的异常, 如果解决这些异常, 尤其是三方jar包抛出的异常,就是一个很关键和棘手的问题.配置环境在第一个选项位置点开后会弹出配置页面,里面可以配置一些启动需要的环境变量.第二个是debug启动按钮第三个是程序…

SQL 优化方案(规范)

SQL优化1、SQL执行顺序2、前置条件2.1、使用explain分析SQL执行计划2.2、开启慢sql日志2.3、慢查询时间设置。默认情况下long_query_time的值为10秒&#xff0c;可以使用命令修改&#xff0c;也可以在my.cnf参数里面修改。3、基础Sql优化3.1、小表驱动大表3.2、高效的分页3.3、…

【linux入门】Linux基础知识学习笔记

文章目录【第一章-宏观知识】1.硬件和软件的关系2.操作系统 是什么、作用是什么3.常见的操作系统4.Linux的诞生5.Linux内核 是什么6.Linux发行版 是什么7.WSL是什么8.虚拟机快照9.FinalShell&#xff08;Xshell替代品&#xff09;【第二章-Linux基础命令】1.Linux目录结构2.什么…

Linux下ElasticSearch安装和基本使用

安装 下载安装目录:/home/es-7.12.0 es启动用户:zmsz 出于安全考虑,elasticsearch默认不允许以root账号运行,所有一定要创建一个其他用户执行启动命令,不然一定会报错!! 创建用户:useradd zmsz 设置密码:passwd zmsz下载解压 下载:官网 以最新版8.6为准,执行wget …

MySQL中的limit分页的使用

SQL准备 create table tb_students (id int auto_increment primary key comment 主键ID,studentid char(9) unique not null comment 学生学号,name varchar(10) not null comment 学生姓名,gender char(1) not null comment 学生性别 ) comment 学生表;insert into tb_stude…

【机器学习 - 2】:数据集的处理

文章目录训练集和数据集分离获取最优模型超参数寻找最优模型网格搜索的使用训练集和数据集分离 训练集和数据集分离的原理&#xff1a;当我们获取一个数据集时&#xff0c;我们需要将其一小部分拿出来作为测试集&#xff0c;剩余的作为训练集。例如对于一个训练集&#xff0c;将…

RocketChip RISC-V生成RTL到仿真全流程

一、Scala配置项修改和RTL代码生成可以通过对scala中的配置项修改&#xff0c;来达到定制化配置RISC-V的目的&#xff0c;这里总结几个比较常用的配置项、配置项含义和所在的scala中的位置&#xff1a;1.$rocket-chip/src/main/scala/system/Config.scala1&#xff09;new With…

机器学习-2-安装Python 3.6和Pytorch 1.1.0

0. 说明&#xff1a; 之前根据GPU版本安装了CUDA 9.0&#xff0c;因此现安装与CUDA 9.0相对应的Pytorch版本&#xff0c;但在安装Pytorch之前要先确认一下Python的版本。 1. 查看 CUDA 9.0 对应的 Pytorch 从https://pytorch.org/get-started/previous-versions/中查找CUDA …

程序的机器级表示part1——程序编码与数据格式

目录 1. 汇编语言和机器级语言 1.1 不同的编程语言 1.2 Linux下的汇编语言 2. 程序编码 1.1 机器级代码 1.2 代码示例 3. 数据格式 本文基于CSAPP第三章撰写&#xff0c;主要介绍部分x86-64汇编的相关知识&#xff0c;后续会将该部分内容慢慢完善&#xff08;PS&a…

Web Spider XHR断点 千千XX 歌曲下载(三)

Web Spider XHR断点 千千XX 歌曲下载 首先声明: 此次案例只为学习交流使用&#xff0c;切勿用于其他非法用途 注&#xff1a;网站url、接口url请使用base64.b64decode自行解码 文章目录Web Spider XHR断点 千千XX 歌曲下载前言一、资源推荐二、任务说明三、网站分析四、XHR断点…

knife4j使用与步骤

1、导入依赖<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version> </dependency>2、knife4j的配置类&#xff08;可有可无&#xff09;package…

为什么程序员的工资比其他行业高这么多?

不止一次听到有人说程序员工资高&#xff0c;更有甚者喊着“把IT工资打下来”。 拜托大哥大姐们&#xff01;看事情要客观好吧&#xff01;&#xff01; 虽然看起来程序员工资是不少&#xff0c;对比其他行业确实会高一些&#xff0c;但并不代表程序员这个岗位工资就要压到三千…

Elasticsearch:如何在 Elasticsearch 中正确使用同义词功能

同义词用于提高搜索质量并扩大匹配范围。 例如&#xff0c;搜索 England 的用户可能希望找到包含 British 或 UK 的文档&#xff0c;尽管这三个词完全不同。 Elasticsearch 中的同义词功能非常强大&#xff0c;如果实施得当&#xff0c;可以使你的搜索引擎更加健壮和强大。 在…

详解结构体内存对齐

目录 前言 一、结构体内存对齐规则 二、 offsetof 宏 三、结构体内存对齐的原因 四、 修改默认对齐数 前言 引入问题&#xff1a; #include <stdio.h>struct S {char c1;int i;char c2; };int main() {printf("%zd\n", sizeof(struct S));return 0; } 程…