概述:
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配置文件,否则是没有的。
 
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)]](https://img-blog.csdnimg.cn/d65c74a140c4454eae5107aed427b706.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的区别
 
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)]](https://img-blog.csdnimg.cn/07f75de658144a0ebee7a37c23bc23b5.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();
    }
}
                

















