一、Spring框架概述
- Spring 是轻量级的开源的 Java EE 框架。
- 目的:解决企业应用开发的复杂性
- Spring 有两个核心部分:IOC 和 Aop
- IOC:控制反转,把创建对象过程交给 Spring 进行管理
- Aop:面向切面,不修改源代码的情况下进行功能加强
- Spring 特点:
- 方便解耦,简化开发
- Aop 编程支持
- 方便程序的测试
- 方便集成各种优秀框架
- 降低 Java EE API 的使用难度
- 方便进行事务操作
下载地址:https://repo.spring.io/release/org/springframework/spring/5.2.6.RELEASE/
1、Spring能做什么
1.1、Spring的能力
1.2、Spring的生态
https://spring.io/projects/spring-boot
覆盖了:
-
web开发
-
数据访问
-
安全控制
-
分布式
-
消息服务
-
移动开发
-
批处理
…
2、Spring5重大升级
2.1 响应式编程
2.2 内部源码设计
基于Java8的一些新特性,如:接口默认实现。重新设计源码架构。
3、Spring构造
- Spring5模块:
4、入门案例
步骤:
-
下载相关依赖:下载地址:https://repo.spring.io/release/org/springframework/spring/5.2.6.RELEASE/
-
创建工程
-
导入依赖
-
Spring IOC基本包
-
日志
-
开始写代码:
-
创建普通类,在这个类创建普通方法
public class User { public void add() { System.out.println("add....."); } }
-
创建 Spring 配置文件,在配置文件配置创建的对象
- Spring 配置文件使用 xml 格式
<?xml version="1.0" encoding="UTF-8"?> <!-- 需引入Spring相关声明 --> <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"> <!--配置 User 对象创建--> <bean id="user" class="test.User"></bean> </beans>
- Spring 配置文件使用 xml 格式
-
测试对象的使用
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @Description: * @author: Qiang * @date: 2021/5/13 14:58 */ public class UserTest { public static void main(String[] args) { //1 加载 spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //2 获取配置创建的对象 User user = context.getBean("user", User.class);//user:xml中bean的id值,User.class:对象类型 System.out.println(user); user.add(); } } /** * 结果: * test.User@464bee09 * add..... */
二、IOC 容器
IOC 介绍
什么是IOC?
控制反转(Inversion of Control),把对象创建和对象之间的调用过程,交给 Spring 进行管理。
控制反转是面向对象编程的一种设计原则。最常见的方式是“依赖注入(Dependency Injection,简称DI)。
使用 IOC 目的:为了降低耦合度
IOC 底层原理 :xml 解析、工厂模式、反射
IOC解耦合的思想:
- 原始方式,耦合度太高。
- 通过工厂模式,能降低一点耦合,但我们的目的是最大限度降低耦合度。
- IOC,进一步降低耦合。
IOC 接口(BeanFactory)
IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂。
Spring 提供IOC容器实现的两种方式:(两个接口)
-
BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供给开发人员进行使用。
特点:加载配置文件的时候不会创建对象 , 在获取对象 (使用) 时才去创建对象 -
ApplicationContext : BeanFactory 接口的子接口 , 提供更多更强大的功能 , 一般由开发人员进行使用。
特点:加载配置文件时候就会把配置文件的对象进行创建
(对于web,一般耗时耗资源在服务器启动时完成好点)
-
ApplicationContext 接口有实现类
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rT5z1UT-1627623797927)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608115810276.png)]
-
红框圈中的是主要使用的类:
- FileSystemXmlApplicationContext:根据在硬盘中的路径读取xml文件
- ClassSystemXmlApplicationContext:根据在工程中的路径读取xml文件
-
IOC 操作 (Bean 管理)
-
什么是Bean管理:是指两个操作:
-
Spring 创建对象
-
Spring 属性值注入
-
-
Bean管理操作有两种实现方式
-
基于xml配置文件方式实现
-
基于注解方式实现
-
1. 基于xml方式
1.1 创建对象
<!--配置 User 对象创建-->
<bean id="user" class="test.User"></bean>
-
在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
-
在 bean 标签有很多属性,介绍常用的属性:
- id 属性:唯一标识。(保证能通过唯一标识获取到唯一对象)
- class 属性:类全路径(包类路径)
-
创建对象时候,默认也是执行无参数构造方法完成对象创建。(小心抛异常)
1.2 注入属性值
DI :依赖注入。(就是注入属性)
有以下方式:
-
第一种注入方式:使用set方法进行注入
-
创建类,定义属性和对应的set方法
/** * 演示使用 set 方法进行注入属性 */ public class Book { //创建属性 private String bname; private String bauthor; //创建属性对应的 set 方法 public void setBname(String bname) { this. bname = bname; } public void setBauthor(String bauthor) { this. bauthor = bauthor; } }
-
在 spring 配置文件配置对象创建,配置属性注入
<!-- set 方法注入属性--> <bean id= "book" class= "com.atguigu.spring5.Book"> <!--使用 property 完成属性注入 name:类里面属性名称 value:向属性注入的值 --> <property name="bname" value="易筋经"></property> <property name="bauthor" value="达摩老祖"></property> </bean>
-
-
第二种注入方式:使用有参数构造进行注入
-
创建类,定义属性,创建属性对应有参数构造方法
/** * 使用有参数构造注入 */ public class Orders { //属性 private String oname; private String address; //有参数构造 public Orders(String oname,String address) { this. oname = oname; this. address = address; } }
-
在 spring 配置文件中进行配置
<!-- 有参数构造注入属性--> <bean id="orders" class="com.atguigu.spring5.Orders"> <constructor-arg name="oname" value="电脑"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
-
-
p 名称空间注入(了解)
- 使用 p 名称空间注入,可以简化基于 xml 配置方式
-
第一步 添加 p 名称空间在配置文件中[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MrdubMR2-1627623797928)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608123656691.png)]
-
第二步 进行属性注入,在 bean 标签里面进行操作
<!--2 set 方法注入属性--> <bean id="book" class="com.atguigu.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏"></bean>
注入其他类型属性
-
字面量
- 字面量:固定内容
- null 值
<!--null 值--> <property name="address"> <null/><!--向属性设置null值--> </property>
- 属性值包含特殊符号
<!--属性值包含特殊符号的处理方式: 1 把<>进行转义 < > 2 把带特殊符号内容写到 CDATA --> <property name="address"> <value><![CDATA[<<南京>>]]></value> </property>
-
外部 bean
举例说明:
-
创建两个类 service 类和 dao 类
-
在 service 调用 dao 里面的方法
-
在 spring 配置文件中进行配置
public class UserService { //创建 UserDao 类型属性,生成 set 方法 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add() { System. out .println( "service add..............."); userDao.update(); } } dao类省略...
<!--1 service 和 dao 对象创建--> <bean id="userService" class="com.atguigu.spring5.service.UserService"> <!--注入 userDao 对象 name 属性:类里面属性名称 ref 属性:创建 userDao 对象 bean 标签 id 值 --> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
-
-
内部 bean
一对多关系:如:部门和员工
- 举例:在实体类之间表示一对多关系
//部门类 public class Dept { private String dname; public void setDname(String dname) { this. dname = dname; } } //员工类 public class Emp { private String ename; private String gender; //员工属于某一个部门,使用对象形式表示 private Dept dept; public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } }
- 在spring配置文件中进行配置
<!--内部 bean--> <bean id= "emp" class= "com.atguigu.spring5.bean.Emp"> <!--设置两个普通属性--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--设置对象类型属性--> <property name="dept"> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="安保部"></property> </bean> </property> </bean>
级联赋值
-
第一种写法
<!--级联赋值--> <bean id= "emp" class= "com.atguigu.spring5.bean.Emp"> <!--设置两个普通属性--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value= "财务部" ></property> </bean>
-
第二种写法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bCcOy4lq-1627623797929)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608133450280.png)]
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref= "dept"></property>
<property name="dept.dname" value="技术部"></property><!--类中需要有get方法-->
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
注入集合属性
- 注入数组类型属性
- 注入List集合类型属性
- 注入Map集合类型属性
例:
① 创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {
//1 数组类型属性
private String[] courses;
//2 list 集合类型属性
private List<String> list;
//3 map 集合类型属性
private Map<String,String> maps;
//4 set 集合类型属性
private Set<String> sets;
public void setSets(Set<String> sets) {
this. sets = sets;
}
public void setCourses(String[] courses) {
this. courses = courses;
}
public void setList(List<String> list) {
this. list = list;
}
public void setMaps(Map<String, String> maps) {
this. maps = maps;
}
}
② 在 spring 配置文件进行配置
<!--1 集合类型属性注入-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.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>
</bean>
在集合里面设置对象类型值
<!--创建多个 course 对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value=5"Spring5 框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="MyBatis 框架"></property>
</bean>
----------------------------------------------------------------------
<!--注入 list 集合类型,值是对象-->
<property name= "courseList">
<list>
<ref bean= "course1"></ref>
<ref bean= "course2"></ref>
</list>
</property>
把集合注入部分提取出来
① 在 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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/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 标签完成 list 集合注入提取(将这部分单独提取出来,方便重复使用)
<!--1 提取 list 集合类型属性注入-->
<util:list id="bookList">
<value>易筋经</ value>
<value>九阴真经</ value>
<value>九阳神功</ value>
</util:list>
<!--2 提取 list 集合类型属性注入使用-->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book">
<property name="list" ref="bookList"></property>
</bean>
1.3 xml 自动装配
-
什么是自动装配
-
根据指定装配规则(根据属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
-
演示自动装配过程
package com.atguigu.spring5.autowire; public class Dept { @Override public String toString() { return "Dept{}"; } }
package com.atguigu.spring5.autowire; public class Emp { private Dept dept; public void setDept(Dept dept) { this.dept = dept; } @Override public String toString() { return "Emp{" + "dept=" + dept + '}'; } public void test() { System.out.println(dept); } }
- 根据属性名称自动注入
<!--实现自动装配 bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值: byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 byType 根据属性类型注入 --> <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"> <!--<property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
- 根据属性类型自动注入
<!--实现自动装配 bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值: byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 byType 根据属性类型注入 --> <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"><!--相同类型只能一个,不然不能确定调哪个,会报异常--> </bean> <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></ bean>
测试:
@Test public void test4() { ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml"); Emp emp = context.getBean("emp", Emp.class); System.out.println(emp); }
1.4 外部属性文件
例:
-
直接配置数据库信息
-
配置德鲁伊连接池
-
引入德鲁伊连接池依赖jar包
<!--直接配置连接池--> <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource"> <property name ="driverClassName" value ="com.mysql.jdbc.Driver"></property> <property name ="url" value ="jdbc:mysql://localhost:3306/userDb"></property> <property name ="username" value ="root"></property> <property name ="password" value ="root"></property> </bean>
-
-
引入外部属性文件配置数据库连接池
-
创建外部属性文件,properties 格式文件,写数据库信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnpu0G6f-1627623797929)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608154426980.png)]
-
把外部 properties 属性文件引入到 spring 配置文件中
<!--引入 context 名称空间--> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema- - instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:util ="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" 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"> 在 spring 配置文件使用标签引入外部属性文件 <!--引入外部属性文件--> <context:property-placeholder location ="classpath:jdbc.properties"/> <!--配置连接池--> <bean id ="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.password}"></property> </bean>
-
Spring中的Bean
1.FactoryBean
- Spring 有两种类型 bean ,一种普通 bean ,另外一种工厂 bean (FactoryBean )
- 普通 bean :在配置文件中定义 bean 类型就是返回类型
- FactoryBean :在配置文件定义 bean 类型可以和返回类型不一样
- 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
- 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> {
//定义返回 bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname( "abc");
return course;
}
@Override
public Class<?> getObjectType() {//这里是默认,没有修改
return null;
}
@Override
public boolean isSingleton() { //是否是个单例
return false;
}
}
@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext( "bean3.xml");
Course course = context.getBean( "myBean", Course.class);//返回类型可以和定义类型不一样
System. out .println(course);
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
2.bean 作用域
- 在 Spring 里面,可以设置创建 bean 实例是单实例还是多实例
- 在 Spring 里面 ,默认情况下,bean 是单实例对象
单实例 如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h0XFKWeL-1627623797930)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608152120444.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMJuoO3p-1627623797930)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608152126503.png)]
-
如何设置单实例还是多实例?
-
在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
scope 属性值
- singleton:默认值,表示是单实例对象
- prototype:表示是多实例对象
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yfoXMtfo-1627623797931)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608152303861.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4p3X9gh-1627623797931)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608152315373.png)]
- singleton 和 prototype 区别
- singleton 单实例,prototype 多实例
- 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
- 设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,而是在调用getBean 方法时候创建多实例对象
scope 还有 request 、session 值:即将对象放到 request域或者session域
3.bean 生命周期
1)生命周期:从对象创建到对象销毁的过程
2)bean生命周期
-
通过构造器创建bean实例(无参数构造)
-
为bean的属性设置值和其他bean引用(调用set方法)
-
调用bean的初始化的方法(需要进行配置)
-
bean可以使用了(对象获取到了)
-
当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
3)演示bean生命周期
public class Orders {
//无参数构造
public Orders() {
System. out .println("第一步 执行无参数构造创建 bean 实例");
}
private String oname;
public void setOname(String oname) {
this. oname = oname;
System. out .println("第二步 调用 set 方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod() {
System. out .println("第三步 执行初始化的方法“);
}
//创建执行的销毁的方法
public void destroyMethod() {
System. out .println("第五步 执行销毁的方法");
}
}
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
@Test
public void testBean3() {
// ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "bean4.xml");
Orders orders = context.getBean( "orders", Orders.class);
System.out.println("第四步 获取创建bean 实例对象");
System.out.println(orders);
//手动让 bean 实例销毁
context.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7TvtSaP2-1627623797932)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608153410070.png)]
4)bean 的后置处理器,bean 生命周期有七步
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
5)演示添加后置处理器效果
① 创建类,实现接口 BeanPostProcessor,创建后置处理器
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;
}
}
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
系统会为所有bean实例都配置上
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fTzFJpWC-1627623797932)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608153722285.png)]
2. 基于注解方式
Ⅰ 、什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
- 使用注解,注解作用在类上面、方法上面、属性上面
- 使用注解目的:简化 xml 配置
Ⅱ 、Spring 针对 Bean 管理中创建对象提供注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
Ⅲ 、基于注解方式实现对象创建
-
第一步 引入依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCKEaO5k-1627623797933)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608155922868.png)]
-
第二步 开启组件扫描
- 引入名称空间(context的那两行)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WE8HNMHA-1627623797933)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200905152517148.png)]
<!--开启组件扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 --> <context:component-scan base-package="com.atguigu"></context:component-scan>
-
第三步 创建类,在类上面添加创建对象注解
//在注解里面 ,value的值相当于bean里的id, //value 属性值可以省略不写,默认值是类名称(首字母小写) :UserService -- userService @Component(value = "userService") //相当于 <bean id="userService" class=".."/> public class UserService { public void add() { System. out .println( "service add......."); } }
Ⅳ、开启组件扫描细节配置
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter:设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type= "annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
Ⅴ、基于注解方式实现属性注入
-
@Autowired:根据属性类型进行自动装配
- 第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
- 第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
@Service public class UserService { //定义 dao 类型属性 //不需要添加 set 方法 //添加注入属性注解 @Autowired private UserDao userDao; public void add() { System. out .println( "service add......."); userDao.add(); } }
-
@Qualifier:根据名称进行注入
- 这个@Qualifier 注解的使用,需要和@Autowired 一起使用(即根据类型不知找哪个时,可以根据名称找)
//定义 dao 类型属性 //不需要添加 set 方法 //添加注入属性注解 @Autowired //根据类型进行注入 @Qualifier(value = "userDaoImpl1") //根据名称进行注入,value可不写,默认是类型名称(首字母小写) private UserDao userDao;
-
@Resource:可以根据类型注入,也可以根据名称注入
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
包有所不同:
org.springframework.beans.factory.annotation.Autowired
org.springframework.beans.factory.annotation.Qualifier
javax.annotation.Resource(不是spring的,而是Java扩展包中的)
- @Value:注入普通类型属性
@Value(value = "abc")
private String name;
Ⅵ、完全注解开发
-
创建配置类,用于替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件 @ComponentScan(basePackages = { "com.atguigu"}) public class SpringConfig { }
-
编写类
package com.atguigu.UserService; @Service public class UserService { public void add() { System.out.println("add....") } }
-
测试
@Test public void testService2() { //加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig. class); UserService userService = context.getBean( "userService",UserService. class); System. out .println(userService); userService.add(); }
三、AOP
Ⅰ、什么是AOP
Aspect Oriented Programming
- 面向切面(方面)编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
- 下图使用登录例子说明 AOP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tbdRSKkX-1627623797934)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608161224237.png)]
Ⅱ、AOP底层原理
-
AOP 底层使用动态代理
-
有两种情况动态代理。(Spring内部已经封装好了,我们只需使用)
-
第一种: 有接口情况:使用 JDK 动态代理
- 创建接口实现类代理对象,增强类的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6GlOWyi-1627623797934)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608161652382.png)]
-
第二种:没有接口情况:使用 CGLIB 动态代理
- 创建子类的代理对象,增强类的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lZncqJyh-1627623797935)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608161744079.png)]
Ⅲ、AOP (JDK 动态代理)
-
使用 JDK 动态代理,使用 Proxy 类(java.lang包下)里面的方法创建代理对象
-
调用 newProxyInstance 方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-68ZRWbYG-1627623797935)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608161853082.png)]
方法有三个参数:
第一参数:类加载器
第二参数:增强方法所在的类,这个类实现的接口,支持多个接口
第三参数:实现这个接口 InvocationHandler,创建代理对象,写增强的部分
-
-
编写 JDK 动态代理代码
-
创建接口,定义方法
public interface UserDao { public int add( int a, int b); public String update(String id); }
-
创建接口实现类,实现方法
public class UserDaoImpl implements UserDao { @Override public int add( int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
-
使用 Proxy 类创建接口代理对象
public class JDKProxy { public static void main(String[] args) { //创建接口实现类代理对象 Class[] interfaces = {UserDao. class}; // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, //new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) //throws Throwable { // return null; // } // }); UserDaoImpl userDao = new UserDaoImpl(); UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces,new UserDaoProxy(userDao)); int result = dao.add(1, 2); System.out.println( "result:"+result); } } //创建代理对象代码 class UserDaoProxy implements InvocationHandler { //1 把创建的是谁的代理对象,把谁传递过来 //有参数构造传递 private Object obj; public UserDaoProxy(Object obj) { this. obj = obj; } //增强的逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System. out .println("方法之前执行...."+method.getName()+ ":传递的参数 ..."+ Arrays. toString (args)); //被增强的方法执行 Object res = method.invoke( obj, args); //方法之后 System. out .println("方法之后执行...."+ obj); return res; } }
-
Ⅳ、AOP 术语
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kC21XMPK-1627623797936)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608162733827.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wgUpsUc9-1627623797936)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608162743362.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3sL5cxGf-1627623797937)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608162810417.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ywVfqz20-1627623797937)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608162820731.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TSiXvRVq-1627623797938)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608162908816.png)]
Ⅴ、AOP 操作
1. 准备工作
-
Spring 框架一般都是基于 AspectJ 实现 AOP 操作
- AspectJ 不是 Spring 组成部分,是一个单独的框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作
-
基于 AspectJ 实现 AOP 操作
- 基于 xml 配置文件实现
- 基于注解方式实现(常用)
-
在项目工程里面引入 AOP 相关依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lo1Pqbl9-1627623797938)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200905202638410.png)]
加上之前的就:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bfo9DVWM-1627623797939)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608163048210.png)]
-
切入点表达式
-
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
-
语法结构:
execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
- 举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强:
execution(public void com.atguigu.dao.BookDao.add(..))
- 举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
- 举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强:
-
execution(* com.atguigu.dao.BookDao.* (..))
- (可以用 * 表示使用任意修饰符,也可以表示任意方法,若返回值为void可以省略不写。)
- 举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强:
execution(* com.atguigu.dao. * . * (..))
2. AspectJ 注解
-
创建类,在类里面定义方法
public class User { public void add() { System. out .println( "add......."); } }
-
创建增强类(编写增强逻辑)
- 在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类 public class UserProxy { public void before() { //前置通知 System. out .println( "before......"); } }
-
进行通知的配置
- 在 spring 配置文件中,开启注解扫描
<?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.atguigu.spring5.aopanno"></context:component-scan>
-
使用注解创建 User 和 UserProxy 对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVxTVGfN-1627623797939)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608163853654.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S2RBwOHG-1627623797940)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608163902569.png)]
-
在增强类上面添加注解 @Aspect
//增强的类 @Component @Aspect //生成代理对象 public class UserProxy {
-
在 spring 配置文件中开启生成代理对象
<!-- 开启 Aspect 生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-utoproxy>
-
配置不同类型的通知
-
在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//增强的类 @Component @Aspect //生成代理对象 public class UserProxy { //前置通知 //@Before 注解表示作为前置通知 @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void before() { System. out .println( "before........."); } //后置通知(返回通知):有异常时不执行 @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterReturning() { System. out .println( "afterReturning........."); } //最终通知 @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") pub lic void after() { System. out .println( "after........."); } //异常通知 @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterThrowing() { System. out .println( "afterThrowing........."); } //环绕通知 @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System. out .println("环绕之前 ........."); //被增强的方法执行 proceedingJoinPoint.proceed(); System. out .println("环绕之后 ........."); } }
-
-
相同的切入点抽取
//相同切入点抽取 @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void pointdemo() { } //前置通知 //@Before 注解表示作为前置通知 @Before(value = "pointdemo()") public void before() { System.out.println( "before........."); }
-
有多个增强类多同一个方法进行增强,设置增强类优先级
- 在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高(越早执行)
@Component @Aspect @Order(1) public class PersonProxy
-
完全使用注解开发
- 创建配置类,不需要创建 l xml 配置文件
@Configuration @ComponentScan(basePackages = { "com.atguigu"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop { }
AspectJ 配置文件
-
创建两个类,增强类和被增强类,创建方法
-
在 spring 配置文件中创建两个类对象
<!--创建对象--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
-
在 spring 配置文件中配置切入点
<!--配置 aop 增强--> <aop:config> <!--切入点--> <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/> <!--配置切面--> <aop:aspect ref="bookProxy"> <!--增强作用在具体的方法上--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
四、 JdbcTemplate
1. 概念和准备
-
什么是 JdbcTemplate
- Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
-
准备工作
- 引入相关 jar 包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DYPWC8Qx-1627623797940)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608164834718.png)]
-
在 spring 配置文件配置数据库连接池
<!-- 数据库连接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="jdbc:mysql:///user_db" /> <property name="username" value="root" /> <property name="password" value="root" /> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> </bean>
-
配置 JdbcTemplate 对象,注入 DataSource
<!-- JdbcTemplate 对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入 dataSource--> <property name="dataSource" ref="dataSource"></property> </bean>
-
创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象
配置文件
<!-- 组件扫描 --> <context:component-scan base-package="com.atguigu"></context:component-scan>
Service
@Service public class BookService { //注入 dao @Autowired private BookDao bookDao; }
Dao
@Repository public class BookDaoImpl implements BookDao { //注入 JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; }
2. JdbcTemplate 操作数据库
添加
- 对应数据库创建实体类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-94ExwHuX-1627623797941)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608165620265.png)]
-
编写 service 和 和 dao
- 在 dao 进行数据库添加操作
- 调用 JdbcTemplate 对象里面 update 方法实现添加操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gefb7Jem-1627623797941)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608165651663.png)]
-
有两个参数:第一个参数:sql 语句、第二个参数:可变参数,设置 sql 语句值
@Repository public class BookDaoImpl implements BookDao { //注入 JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; //添加的方法 @Override public void add(Book book) { //1 创建 sql 语句 String sql = "insert into t_book values(?,?,?)"; //2 调用方法实现 Object[] args = {book.getUserId(), book.getUsername(), book.getUstatus()}; int update = jdbcTemplate.update(sql,args); System.out.println(update); } }
-
测试类
@Test public void testJdbcTemplate() { ApplicationContext context = new ClassPathXmlApplicationContext( "bean1.xml"); BookService bookService = context.getBean( "bookService", BookService.class); Book book = new Book(); book.setUserId( "1"); book.setUsername( "java"); book.setUstatus( "a"); bookService.addBook(book); }
修改和删除
-
修改
@Override public void updateBook(Book book) { String sql = "update t_book set username=?,ustatus=? where user_id=?"; Object[] args = {book.getUsername(), book.getUstatus(),book.getUserId()}; int update = jdbcTemplate.update(sql, args); System. out .println(update); }
-
删除
@Override public void delete(String id) { String sql = "delete from t_book where user_id=?"; int update = jdbcTemplate.update(sql, id); System. out .println(update); }
查询返回某个值
- 查询表里面有多少条记录,返回是某个值
- 使用 JdbcTemplate 实现查询返回某个值代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTo8BjTL-1627623797942)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608170106863.png)]
有两个参数
第一个参数:sql 语句
第二个参数:返回类型 Class
//查询表记录数
@Override
public int selectCount() {
String sql = "select count (*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
查询返回对象
- 场景:查询图书详情
- JdbcTemplate 实现查询返回对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZmALipJQ-1627623797942)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608170309922.png)]
有三个参数
第一个参数:sql 语句
第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
第三个参数:sql 语句值
@Override
public Book findBookInfo(String id) {
String sql = "select * from t_book where user_id=?";
//调用方法
Book book = jdbcTemplate.queryForObject(sql, new
BeanPropertyRowMapper<Book>(Book.class), id);
return book;
}
查询返回集合
- 场景:查询图书列表分页…
- 调用 JdbcTemplate 方法实现查询返回集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LAtrLZsx-1627623797943)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608170458452.png)]
有三个参数
第一个参数:sql 语句
第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
第三个参数:sql 语句值
//查询返回集合
@Override
public List<Book> findAllBook() {
String sql = "select * from t_book";
//调用方法
List<Book> bookList = jdbcTemplate.query(sql, new
BeanPropertyRowMapper<Book>(Book. class));
return bookList;
}
批量操作
-
批量操作:操作表里面多条记录
-
JdbcTemplate 实现批量添加操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gkcr2Y3O-1627623797943)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608170615836.png)]
有两个参数
- 第一个参数:sql 语句
- 第二个参数:List 集合,添加多条记录数据
//批量添加 @Override public void batchAddBook(List<Object[]> batchArgs) { String sql = "insert into t_book values(?,?,?)"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System. out.println(Arrays.toString(ints)); } // 批量添加测试 List<Object[]> batchArgs = new ArrayList<>(); Object[] o1 = { "3", "java", "a"}; Object[] o2 = { "4", "c++", "b"}; Object[] o3 = { "5", "MySQL", "c"}; batchArgs.add(o1); batchArgs.add(o2); batchArgs.add(o3); //调用批量添加 bookService.batchAdd(batchArgs);
-
JdbcTemplate 实现批量 实现批量 修改操作
//批量修改 @Override 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)); } // 批量修改 List<Object[]> batchArgs = new ArrayList<>(); Object[] o1 = { "java0909", "a3", "3"}; Object[] o2 = { "c++1010", "b4", "4"}; Object[] o3 = { "MySQL1111", "c5", "5"}; batchArgs.add(o1); batchArgs.add(o2); batchArgs.add(o3); //调用方法实现批量修改 bookService.batchUpdate(batchArgs);
-
JdbcTemplate 实现批量 实现批量 删除操作
//批量删除 @Override public void batchDeleteBook(List<Object[]> batchArgs) { String sql = "delete from t_book where user_id=?"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System. out .println(Arrays. toString (ints)); } // 批量删除 List<Object[]> batchArgs = new ArrayList<>(); Object[] o1 = { "3"}; Object[] o2 = { "4"}; batchArgs.add(o1); batchArgs.add(o2); //调用方法实现批量删除 bookService.batchDelete(batchArgs);
五、事务操作
1. 事务概念
-
什么事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
- 典型场景:银行转账
- lucy 转账 100 元 给 mary
- lucy 少 100,mary 多 100
-
事务四个特性(ACID )
- 原子性:要么都成功,要么都失败
- 一致性:操作前后总量不变
- 隔离性:多个事务之间不影响
- 持久性:提交之后表中数据真正变化
2. 搭建事务操作环境
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cZ6MzYGn-1627623797944)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608171050191.png)]
1、创建数据库表,添加记录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Z3fvOmG-1627623797944)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608171120838.png)]
2 、创建 service ,搭建 dao ,完成对象创建和注入关系
(1)service 注入 dao,在 dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource
@Service
public class UserService {
//注入 dao
@Autowired
private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
}
3 、在 dao 创建两个方法:多钱和少钱的方法,在 service 创建方法(转账的方法)
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//lucy 转账 100 给 mary
//少钱
@Override
public void reduceMoney() {
String sql = "update t_account set money=money-? where username=?";
jdbcTemplate.update(sql,100, "lucy");
}
//多钱
@Override
public void addMoney() {
String sql = "update t_account set money=money+? where username=?";
jdbcTemplate.update(sql,100, "mary");
}
}
@Service
public class UserService {
//注入 dao
@Autowired
private UserDao userDao;
//转账的方法
public void accountMoney() {
//lucy 少 100
userDao.reduceMoney();
//mary 多 100
userDao.addMoney();
}
}
4、上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常 : 有问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-juTm09e8-1627623797945)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608171401639.png)]
(1)上面问题如何解决呢?
使用事务进行解决
(2)事务操作过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mwGwTsFK-1627623797945)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608171434955.png)]
3. Spring 事务管理介绍
-
事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
-
在 Spring 进行事务管理操作
- 有两种方式:
- 编程式事务管理(写在代码里:不方便,一般不用)
- 声明式事务管理(使用多)
- 有两种方式:
-
声明式事务管理
- 基于注解方式(使用多)
- 基于 xml 配置文件方式
在 Spring 进行声明式事务管理,底层使用 AOP 原理
- Spring 事务管理 API
- 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ajnJ9DB8-1627623797946)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608171614488.png)]
3. 注解声明式事务管理
-
在 spring 配置文件配置事务管理器
<!--创建事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入数据源--> <property name= "dataSource" ref="dataSource"></property> </bean>
-
在 在 spring 配置文件,开启事务注解
-
在 spring 配置文件引入名称空间 tx
<beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema- - instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
-
开启事务注解
<tx:annotation-driven transaction-manager "transactionManager"></tx:annotation-driven>
-
-
在 service 类上面( 类上面(或者 service 类里面方法上面)添加事务注解
- @Transactional,这个注解添加到类上面,也可以添加方法上面
- 如果把这个注解添加类上面,这个类里面所有的方法都添加事务
- 如果把这个注解添加方法上面,为这个方法添加事务
@Service @Transactional public class UserService {
- @Transactional,这个注解添加到类上面,也可以添加方法上面
参数配置
- 在 service 类上面添加注解@Transactional ,在这个注解里面可以配置事务相关参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpKH84RM-1627623797946)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172204869.png)]
-
propagation :事务传播行为
- 多事务方法直接进行调用,这个过程中事务是如何进行管理的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-05CwOrmA-1627623797947)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172251264.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yg092wcF-1627623797947)(C:\Users\17812\Desktop\笔记(work)]\typora_images\事务传播行为.bmp)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RsbcTilP-1627623797948)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172313720.png)]
-
ioslation :事务隔离级别
-
事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
-
有三个读问题:脏读、不可重复读、虚(幻)读
- 脏读:一个未提交事务读取到另一个未提交事务的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pMFPCWzZ-1627623797948)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172347825.png)]
- 不可重复读:一个未提交事务读取到另一提交事务修改数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EdYA5nr2-1627623797949)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172407695.png)]
- 虚读(幻读):一个未提交事务读取到另一提交事务添加数据
-
解决:通过设置事务隔离级别,解决读问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ja4OyOIM-1627623797949)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172440239.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9pojdh7b-1627623797949)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608172451933.png)]
-
-
timeout :超时时间
- 事务需要在一定时间内进行提交,如果不提交进行回滚
- 默认值是 -1 ,设置时间以秒单位进行计算
-
readOnly :是否只读
- 读:查询操作。 写:添加修改删除操作
- readOnly 默认值 false,表示可以查询,可以添加修改删除操作
- 设置 readOnly 值是 true,设置成 true 之后,只能查询
-
rollbackFor :回滚
- 设置出现哪些异常进行事务回滚
-
noRollbackFor :不回滚
- 设置出现哪些异常不进行事务回滚
4. XML 声明式事务管理
- 在 spring 配置文件中进行配置
- 第一步 配置事务管理器
- 第二步 配置通知
- 第三步 配置切入点和切面
<!--1 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置事务的通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>--><!--表示匹配accout开头的方法-->
</tx:attributes>
</ tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref= "pt"/>
</aop:config>
5. 完全注解声明式事务管理
1 、创建配置类,使用配置类替代 xml 配置文件
@Configuration //配置类
@ComponentScan(basePackages = "com.atguigu") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean//创建对象
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName( "com.mysql.jdbc.Driver");
dataSource.setUrl( "jdbc:mysql:///user_db");
dataSource.setUsername( "root");
dataSource.setPassword( "root");
return dataSource;
}
//创建 JdbcTemplate 对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到 ioc 容器中根据类型找到 dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入 dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager
getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new
DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
六、Spring5 框架新功能
-
整个 Spring5 框架的代码基于 Java8 ,运行时兼容 JDK9, 许多不建议使用的类和方法在代码库中删除
-
Spring 5.0 框架自带了通用的日志封装
-
Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2
-
Spring5 框架整合 Log4j2,步骤:
-
第一步 引入 jar 包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rpE8EzMV-1627623797950)(C:\Users\17812\Desktop\笔记(work)]\typora_images\image-20200608173130076-1620964825690.png)
-
第二步 创建 log4j2.xml 配置文件
<?xml version="1.0" encoding="UTF- - 8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置, 当设置成 trace 时,可以看到 log4j2 内部各种详细输出--> <configuration status= "INFO"> <!--先定义所有的 appender--> <appenders> <!--输出日志信息到控制台--> <console name= "Console" target= "SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] % - 5level %logger{36} - %msg%n"/> </console> </appenders> <!--然后定义 logger,只有定义 logger 并引入的 appender,appender 才会生效--> <!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root 作为 默认的日志输出--> <loggers> <root level="info"> < appender-ref ref= "Console"/> </ root> </loggers> </configuration>
-
-
-
Spring5 框架核心容器 支持@Nullable 注解
-
@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以
为空,参数值可以为空-
注解用在方法上面,方法返回值可以为空
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtgG4bNs-1627623797951)(C:\Users\17812\Desktop\笔记(work)]\typora_images\image-20200608173502505-1620964930680.png)
-
注解使用在方法参数里面,方法参数可以为空
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nY1pYfgk-1627623797951)(C:\Users\17812\Desktop\笔记(work)]\typora_images\image-20200608173521949-1620964961945.png)
-
-
-
Spring5 核心容器支持函数式风格GenericApplicationContext
//函数式风格创建对象,交给 spring 进行管理 @Test public void testGenericApplicationContext() { //1 创建 GenericApplicationContext 对象 GenericApplicationContext context = new GenericApplicationContext(); //2 调用 context 的方法对象注册 context.refresh();//清空 context.registerBean( "user1",User. class,() -> new User()); //3 获取在 spring 注册的对象// User user (User)context.getBean("com.atguigu.spring5.test.User"); User user = (User)context.getBean( "user1"); System.out.println(user); }
-
Spring5 支持整合 JUnit5
-
整合 JUnit4
-
第一步 引入 Spring 相关针对测试依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4WASolTT-1627623797952)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200907152747737.png)]
-
第二步 创建测试类,使用注解方式完成
@RunWith(SpringJUnit4ClassRunner. class) //单元测试框架 @ContextConfiguration( "classpath:bean1.xml") //加载配置文件 public class JTest4 { @Autowired private UserService userService; @Test public void test1() { userService.accountMoney(); } }
-
-
Spring5 整合 JUnit5
-
第一步 引入 JUnit5 的 jar 包
-
第二步 创建测试类,使用注解完成
@ExtendWith(SpringExtension.class) @ContextConfiguration( "classpath:bean1.xml") public class JTest5 { @Autowired private UserService userService; @Test public void test1() { userService.accountMoney(); } }
-
使用一个复合注解替代上面两个注解完成整合
@SpringJUnitConfig(locations = "classpath:bean1.xml") public class JTest5 { @Autowired private UserService userService; @Test public void test1() { userService.accountMoney(); } }
-
-
-
Webflux
Webflux
1. SpringWebflux 介绍
- 是 Spring5 添加新的模块,用于 web 开发的,功能和 SpringMVC 类似的,Webflux 适用当前一种比较流行响应式编程出现的框架。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9psJYxkn-1627623797953)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608174015894.png)]
-
使用传统 web 框架,比如 SpringMVC,这些基于 Servlet 容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1 以后才支持,核心是基于 Reactor 的相关 API 实现的。
-
解释什么是异步非阻塞:(针对对象不一样)
- 异步和同步
- 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
- 非阻塞和阻塞
- 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞
- 异步和同步
-
Webflux 特点:
- 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性(处理更多的请求),以 Reactor 为基础实现响应式编程
- 函数式编程:Spring5 框架基于 java8,Webflux 使用 Java8 函数式编程方式实现路由请求
-
与SpringMVC比较
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emY67bWR-1627623797953)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608174249832.png)]
-
第一: 两个框架都可以使用注解方式,都运行在 Tomet 等容器中
-
第二 :SpringMVC 采用命令式编程(一行一行解释代码),Webflux 采用异步响应式编程
-
2. 响应式编程(Java 实现)
-
什么是响应式编程
- 响应式编程是一种面向数据流和变化传播的编程方式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
- 电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
-
Java8 及其之前版本
-
提供的观察者模式两个类: Observer 和 Observable
public class ObserverDemo extends Observable { public static void main(String[] args) { ObserverDemo observer = new ObserverDemo(); //添加观察者 observer.addObserver((o,arg)->{ System.out.println("发生变化"); }); observer.addObserver((o,arg)->{ System.out.println("手动被观察者通知,准备改变"); }); observer.setChanged(); //数据变化 observer.notifyObservers(); //通知 } }
-
-
Java9中用 Flow取代了之前的两个类
- Flow才是真正实现了响应式编程。Reactor底层就是对Flow功能进行封装。
3. 响应式编程(Reactor 实现)
-
响应式编程操作中,Reactor 是满足 Reactive 规范框架
-
Reactor 有两个核心类,Mono 和 和 Flux ,这两个类实现接口 Publisher,提供丰富的操作符。Flux 对象实现发布者,返回 N 个元素;Mono 实现发布者,返回 0 或者 1 个元素
-
Flux 和 Mono 都是数据流的发布者,使用 Flux 和 Mono 都可以发出三种数据信号:元素值,错误信号,完成信号。错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者。
-
代码演示 Flux 和 和 Mono
-
第一步 引入依赖
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core< artifactId> <version>3.1.5.RELEASE</version> </dependency>
-
第二步 编程代码
public static void main(String[] args) { //just 方法直接声明 Flux.just (1,2,3,4); Mono.just (1); //其他的方法 Integer[] array = {1,2,3,4}; Flux.fromArray (array); List<Integer> list = Arrays.asList (array); Flux.fromIterable (list); Stream<Integer> stream = list.stream(); Flux.fromStream (stream); }
-
-
三种信号特点
- 错误信号和完成信号都是终止信号,不能共存的
- 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
- 如果没有错误信号,没有完成信号,表示是无限数据流
-
调用 just 或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的。
-
操作符:对数据流进行一道道操作,称为操作符,比如工厂流水线
- 第一 :map :元素映射为新元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FKZNSBRV-1627623797954)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175011458.png)]
-
第二: flatMap :元素映射为流
- 把每个元素转换流,把转换之后多个流合并大的流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5YC7zJEM-1627623797954)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175045905.png)]
4. SpringWebflux 执行流程和核心 API
SpringWebflux 基于 Reactor 实现,默认使用容器是 Netty,Netty 是高性能的 NIO 框架(异步非阻塞的框架)。
-
阻塞与非阻塞
- BIO:block-IO
-
NIO:Non-block-IO
-
SpringWebflux 执行过程和 SpringMVC 是相似的
-
SpringWebflux 核心控制器 DispatchHandler,实现接口 WebHandler
-
接口 WebHandler 有一个方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FL3XNTAa-1627623797955)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175231488.png)]
DispatchHandler实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WpV45KRG-1627623797955)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175240318.png)]
-
-
-
SpringWebflux 里面 DispatcherHandler,负责请求的处理
组件:
- HandlerMapping:请求查询到处理的方法
- HandlerAdapter:真正负责请求处理
- HandlerResultHandler:响应结果处理
-
SpringWebflux 实现函数式编程,表现在两个接口:RouterFunction(路由处理)和 HandlerFunction(处理具体函数)
5. SpringWebflux (基于注解编程模型)
-
SpringWebflux 实现方式有两种:注解编程模型和函数式编程模型
-
使用注解编程模型方式,和之前 SpringMVC 使用相似的,只需要把相关依赖配置到项目中,
- SpringBoot 自动配置相关运行容器,默认情况下使用 Netty 服务器
- 第一步 创建 SpringBoot 工程,引入 Webflux 依赖
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oyFtRBM2-1627623797956)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175357180.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4YBhoIqI-1627623797956)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175414133.png)]
- 第二步 配置启动端口号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wPqw16xh-1627623797957)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175429421.png)]
-
第三步 创建包和相关类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lff21lmo-1627623797957)(C:\Users\17812\AppData\Roaming\Typora\typora-user-images\image-20200608175450125.png)]
创建接口、定义操作的方法
//用户操作接口 public interface UserService { //根据 id 查询用户 Mono<User> getUserById(int id); //查询所有用户 Flux<User> getAllUser(); //添加用户 Mono<Void> saveUserInfo(Mono<User> user); }
接口实现类
public class UserServiceImpl implements UserService { //创建 map 集合存储数据 private final Map<Integer,User> users = new HashMap<>(); public UserServiceImpl() { this.users.put(1, new User( "lucy", "nan",20)); this.users.put(2, new User( "mary", "nv",30)); this.users.put(3, new User( "jack", "nv",50)); } //根据 id 查询 @Override public Mono<User> getUserById(int id) { return Mono.justOrEmpty (this.users.get(id)); } //查询多个用户 @Override public Flux<User> getAllUser() { return Flux.fromIterable (this.users.values()); } //添加用户 @Override public Mono<Void> saveUserInfo(Mono<User> userMono) { return userMono.doOnNext(person -> { //向 map 集合里面放值 int id = users.size()+1; users.put(id,person); }).thenEmpty(Mono.empty ()); } }
创建 controller
@RestController public class UserController { //注入 service @Autowired private UserService userService; //id 查询 @GetMapping( "/user/{id}") public Mono<User> geetUserId(@PathVariable int id) { return userService.getUserById(id); } //查询所有 @GetMapping( "/user") public Flux<User> getUsers() { return userService.getAllUser(); } //添加 @PostMapping( "/saveuser") public Mono<Void> saveUser(@RequestBody User user) { Mono<User> userMono = Mono.just (user); return userService.saveUserInfo(userMono); } }
说明
SpringMVC 方式实现,同步阻塞的方式,基于 SpringMVC+Servlet+Tomcat
SpringWebflux 方式实现,异步非阻塞 方式,基于 SpringWebflux+Reactor+Netty
6. SpringWebflux (基于函数式编程模型)
-
在使用函数式编程模型操作时候,需要自己初始化服务器
-
基于函数式编程模型时候,有两个核心接口:
- RouterFunction(实现路由功能,请求转发给对应的 handler)
- HandlerFunction(处理请求生成响应的函数)。
- 核心任务是定义两个函数式接口的实现并且启动需要的服务器。
-
SpringWebflux 请 求 和 响 应 不 再 是 ServletRequest 和 ServletResponse , 而 是ServerRequest 和ServerResponse
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bRN50Nn-1627623797958)(C:\Users\17812\Desktop\笔记(work)]\typora_images\image-20210514151343609.png)
示例:
-
第一步 把注解编程模型工程复制一份 ,保留 entity 和 service 内容
-
第二步 创建 Handler(具体实现方法)
public class UserHandler { private final UserService userService; public UserHandler(UserService userService) { this. userService = userService; } //根据 id 查询 public Mono<ServerResponse> getUserById(ServerRequest request) { //获取 id 值 int userId = Integer.valueOf (request.pathVariable( "id")); //空值处理 Mono<ServerResponse> notFound = ServerResponse.notFound().build(); //调用 service 方法得到数据 Mono<User> userMono = this.userService.getUserById(userId); //把 userMono 进行转换返回 //使用 Reactor 操作符 flatMap return userMono.flatMap(person -> ServerResponse.ok ().contentType(MediaType.APPLICATION_JSON).body(fromObject(person))).switchIfEmpty(notFound); } //查询所有 public Mono<ServerResponse> getAllUsers() { //调用 service 得到结果 Flux<User> users = this. userService.getAllUser(); return ServerResponse.ok().contentType(MediaType. APPLICATION_JSON ).body(users,User.class); } //添加 public Mono<ServerResponse> saveUser(ServerRequest request) { //得到 user 对象 Mono<User> userMono = request.bodyToMono(User. class); return ServerResponse.ok().build(this.userService.saveUserInfo(userMono)); } }
-
第三步 初始化服务器,编写 Server
-
创建路由的方法
-
创建服务器完成适配
public class Server { //1 创建 Router 路由 public RouterFunction<ServerResponse> routingFunction() { //创建 hanler 对象 UserService userService = new UserServiceImpl(); UserHandler handler = new UserHandler(userService); //设置路由 return RouterFunctions.route( GET("/users/{id}").and(accept(APPLICATION_JSON)), handler::getUserById).andRoute(GET("/users").and(accept(APPLICATION_JSON)), handler::getAllUsers); } //2 创建服务器完成适配 public void createReactorServer() { //路由和 handler 适配 RouterFunction<ServerResponse> route = routingFunction(); HttpHandler httpHandler = toHttpHandler(route); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); //创建服务器 HttpServer httpServer = HttpServer.create (); httpServer.handle(adapter).bindNow(); } }
-
最终调用
public static void main(String[] args) throws Exception{ Server server = new Server(); server.createReactorServer(); System.out.println( "enter to exit"); System.in.read(); }
-
-
使用 WebClient 调用
public class Client { public static void main(String[] args) { //调用服务器地址 WebClient webClient = WebClient.create( "http://127.0.0.1:5794"); //根据 id 查询 String id = "1"; User userresult = webClient.get().uri("/users/{id}", id) .accept(MediaType.APPLICATION_JSON ).retrieve().bodyToMono(User. class).block(); System.out.println(userresult.getName()); //查询所有 Flux<User> results = webClient.get().uri("/use rs") .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class); results.map(stu -> stu.getName()).buffer().doOnNext(System. out::println).blockFirst(); } }