课程地址:https://www.bilibili.com/video/BV1Vf4y127N5
jar包等资料地址:链接:https://pan.baidu.com/s/1dsgpwfj1Jrn16PCsteo-lQ?pwd=bjqz 提取码:bjqz
Spring5 概述
Spring 是轻量级的开源的 JavaEE 框架
Spring 可以解决企业应用开发的复杂性
Spring 有两个核心的部分:IOC、AOP
IOC:控制反转,把创建对象的过程交给Spring进行管理
AOP:面向切面,不修改源代码进行功能增强
Spring 特点:
方便解耦,简化开发
AOP编程支持
方便程序测试
方便进行事务操作
降低API开发难度
下载Spring5相关jar包 下载地址:https://repo.spring.io/ui/native/libs-release-local/org/springframework/spring
这里我们选择下载 5.2.6 版本进行下载
点击链接,选择下载
下载解压后的目录如图所示,其中所有的 jar 包都在 libs 文件中
入门案例 首先看一下 Spring 的框架结构
Core Container 模块表示核心模块,我们演示入门案例,只需用到日下几个包即可
其中 commons-logging 是一个日志包,其他都依赖于这个日志包
commons-logging-1.1.1 下载地址:https://search.maven.org/artifact/commons-logging/commons-logging-api/1.1/jar
首先创建java项目,目录结构如下
在 pro01 module 包的src 下新建 beans.xml 文件,文件添加如下代码
1 2 3 4 5 6 7 8 9 10 <?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" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" > <bean id ="user" class ="com.szx.spring5.demo.User" > </bean > </beans >
id:表示 User 类的一个别名
class:指向 User 类的地址
创建一个User类
1 2 3 4 5 public class User { public void add () { System.out.println("Hello Spring" ); } }
通过Spring的方式创建User类的实例,并调用add方法
1 2 3 4 5 6 7 8 9 10 11 public class test { @Test public void testuser () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); User user = context.getBean("user" , User.class); user.add(); } }
运行效果,可以正常打印add方法中的 Hello Spring
IOC接口
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
Spring 提供 IOC 容器实现两种方式:(两个接口)
BeanFactory:IOC 容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用,加载配置文件时不会创建对象,在获取对象时才会去创建对象
ApplicationContext:BeanFactory 接口的子接口,提供更多的更强大的功能,一般由开发人员进行使用。加载配置文件时就会把配置文件对象进行创建
Bean管理XML方式 通过xml配置文件注入属性值 首先新建 Book.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Book { String name; String author; public String getName () { return name; } public void setName (String name) { this .name = name; } public String getAuthor () { return author; } public void setAuthor (String author) { this .author = author; } @Override public String toString () { return "Book{" + "name='" + name + '\'' + ", author='" + author + '\'' + '}' ; } }
然后再 xml 文件中添加如下配置
1 2 3 4 <bean id ="book" class ="com.szx.spring5.demo.Book" > <property name ="name" value ="三体" > </property > <property name ="author" value ="刘慈欣" > </property > </bean >
name:类中定义的属性名
value:设置属性值
编写测试类来测试
1 2 3 4 5 6 @Test public void test () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Book book = context.getBean("book" , Book.class); System.out.println("book = " + book); }
通过Spring 获取 Book 实例,然后打印 book,可以看到我们在配置文件中设置的初始值已经被赋值了
有参构造注入赋值 新建Order类,类中声明一个带参数的构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Order { String name; String address; public Order (String name, String address) { this .name = name; this .address = address; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getAddress () { return address; } public void setAddress (String address) { this .address = address; } @Override public String toString () { return "Order{" + "name='" + name + '\'' + ", address='" + address + '\'' + '}' ; } }
在 xml 中添加配置文件
1 2 3 4 5 <bean id ="order" class ="com.szx.spring5.demo.Order" > <constructor-arg name ="name" value ="手机" > </constructor-arg > <constructor-arg name ="address" value ="中国" > </constructor-arg > </bean >
name:构造器参数中的属性名
value:构造器参数中的属性值
添加测试代码
1 2 3 4 5 6 @Test public void test () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml" ); Object order = context.getBean("order" ); System.out.println("order = " + order); }
p名称注入(了解) 首先在 xml 中添加一行约束
1 xmlns:p="http://www.springframework.org/schema/p"
然后注入属性的操作可以这样写
1 2 3 <bean id ="book" class ="com.szx.spring5.demo.Book" p:name ="平凡的世界" p:author ="莫言" > </bean >
运行后也可以正常打印值
注入null值和特殊符号 配置xml
1 2 3 4 5 6 7 8 9 <bean id ="student" class ="com.szx.spring5.demo.Student" > <property name ="name" > <null > </null > </property > <property name ="bookName" > <value > <![CDATA[ <<特殊值>> ]]>></value > </property > </bean >
null 值直接使用 <null></null>
标签
特殊值使用 CDATA
运行看效果
注入外部bean 新建 UserDao 接口
1 2 3 public interface UserDao { void update () ; }
创建 UserDao 实现类 UserDaoImpl
1 2 3 4 5 6 public class UserDaoImpl implements UserDao { @Override public void update () { System.out.println("update......" ); } }
创建 UserService
1 2 3 4 5 6 7 8 9 10 11 12 13 public class UserService { private UserDao userDao; public void setUserDao (UserDao userDao) { this .userDao = userDao; } public void update () { System.out.println("userservice...." ); userDao.update(); } }
配置xml
1 2 3 4 5 6 7 <bean id ="userservice" class ="com.szx.spring5.service.UserService" > <property name ="userDao" ref ="userdao" > </property > </bean > <bean id ="userdao" class ="com.szx.spring5.dao.impl.UserDaoImpl" > </bean >
编写测试方法
内部注入bean 这里使用 部门 和 员工 来举例
创建一个部门类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Dept { String deptName; public Dept () { } public String getDeptName () { return deptName; } public void setDeptName (String deptName) { this .deptName = deptName; } @Override public String toString () { return "Dept{" + "deptName='" + deptName + '\'' + '}' ; } }
创建员工类,员工类中声明一个部门属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Emp { String name; String gender; Dept dept; public Emp () { } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getGender () { return gender; } public void setGender (String gender) { this .gender = gender; } public Dept getDept () { return dept; } public void setDept (Dept dept) { this .dept = dept; } @Override public String toString () { return "Emp{" + "name='" + name + '\'' + ", gender='" + gender + '\'' + ", dept=" + dept + '}' ; } }
配置xml
1 2 3 4 5 6 7 8 9 10 11 12 <bean name ="emp" class ="com.szx.spring5.bean.Emp" > <property name ="name" value ="张三" > </property > <property name ="gender" value ="男" > </property > <property name ="dept" > <bean id ="dept" class ="com.szx.spring5.bean.Dept" > <property name ="deptName" value ="技术部" > </property > </bean > </property > </bean >
编写测试方法
级联赋值 方式一 xml配置代码
1 2 3 4 5 6 7 8 9 10 <bean id ="emp" class ="com.szx.spring5.bean.Emp" > <property name ="name" value ="李四" > </property > <property name ="gender" value ="女" > </property > <property name ="dept" ref ="dept" > </property > </bean > <bean id ="dept" class ="com.szx.spring5.bean.Dept" > <property name ="deptName" value ="财务部" > </property > </bean >
运行效果
方式二 这种方式要在员工类中声明部门属性值的 getters 方法
xml配置代码
1 2 3 4 5 6 7 8 9 10 11 <bean id ="emp" class ="com.szx.spring5.bean.Emp" > <property name ="name" value ="李四" > </property > <property name ="gender" value ="女" > </property > <property name ="dept" ref ="dept" > </property > <property name ="dept.deptName" value ="人事部" > </property > </bean > <bean id ="dept" class ="com.szx.spring5.bean.Dept" > <property name ="deptName" value ="财务部" > </property > </bean >
运行效果
注入集合类型的属性 首先新建User类,里面包含各种类型的属性值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class User { String[] strings; List<String> lists; Map<String,String> maps; Set<String> sets; public String[] getStrings() { return strings; } public void setStrings (String[] strings) { this .strings = strings; } public List<String> getLists () { return lists; } public void setLists (List<String> lists) { this .lists = lists; } public Map<String, String> getMaps () { return maps; } public void setMaps (Map<String, String> maps) { this .maps = maps; } public Set<String> getSets () { return sets; } public void setSets (Set<String> sets) { this .sets = sets; } public void pring () { System.out.println(Arrays.toString(strings)); System.out.println(lists.toString()); System.out.println(maps.toString()); System.out.println(sets.toString()); } }
编写xml配置代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <bean id ="user" class ="com.szx.spring.pojo.User" > <property name ="strings" > <array > <value > java课程</value > <value > mysql课程</value > </array > </property > <property name ="lists" > <list > <value > 张三</value > <value > 李四</value > </list > </property > <property name ="maps" > <map > <entry key ="name" value ="张三" > </entry > <entry key ="sex" value ="男" > </entry > </map > </property > <property name ="sets" > <set > <value > javaweb</value > <value > jdbc</value > </set > </property > </bean >
编写测试方法,调用 print 方法
往集合中注入对象类型 首先新建 Course 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Course { String name; public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "Course{" + "name='" + name + '\'' + '}' ; } }
然后再User类中添加 Course 的集合属性
1 2 3 4 5 6 7 8 9 List<Course> courseList; public List<Course> getCourseList () { return courseList; } public void setCourseList (List<Course> courseList) { this .courseList = courseList; }
添加xml配置文件
1 2 3 4 5 6 7 <bean id ="course1" class ="com.szx.spring.pojo.Course" > <property name ="name" value ="Java" > </property > </bean > <bean id ="course2" class ="com.szx.spring.pojo.Course" > <property name ="name" value ="Mysql" > </property > </bean >
然后再user对象bean中通过如下方式注入
1 2 3 4 5 6 7 <property name ="courseList" > <list > <ref bean ="course1" > </ref > <ref bean ="course2" > </ref > </list > </property >
运行效果
提取集合的注入部分 首先新建UtilList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class UtilList { List<String> nameList; public List<String> getNameList () { return nameList; } public void setNameList (List<String> nameList) { this .nameList = nameList; } public void eachList () { nameList.forEach(item -> { System.out.println(item); }); } }
编写配置文件
第一步:修改xml配置文件的约束
在头部 beans 标签中添加如下两行代码
1 xmlns:util="http://www.springframework.org/schema/util"
1 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd"
完整的引入
1 2 3 4 5 6 7 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-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd" >
第二步:抽取list的注入
1 2 3 4 5 6 <util:list id ="utilList" > <value > Java</value > <value > JavaWeb</value > <value > MySql</value > </util:list >
第三步:配置UtilList对象bean
1 2 3 <bean id ="util" class ="com.szx.spring.pojo.UtilList" > <property name ="nameList" ref ="utilList" > </property > </bean >
测试查看运行效果
定义工厂Bean 上面我们定义的都是普通Bena,定义的是什么对象类型,返回的就是这个类型,而工厂Bean则可以返回不同的对象类型。要想实现工厂Bean,在类中要实现 FactoryBean 接口
定义 FactoryBean 接口的实现类 MyBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MyBean implements FactoryBean <Course > { @Override public Course getObject () throws Exception { Course course = new Course(); course.setName("Java" ); return course; } @Override public Class<?> getObjectType() { return null ; } @Override public boolean isSingleton () { return FactoryBean.super .isSingleton(); } }
配置xml文件
1 <bean id ="myBean" class ="com.szx.spring.pojo.MyBean" > </bean >
编写测试方法
1 2 3 4 5 6 @Test public void test () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml" ); Course myBean = context.getBean("myBean" , Course.class); System.out.println(myBean); }
通过运行可以发现返回的是 Course 对象
xml方式自动注入属性值 首先新建员工类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Emp { Dept dept; public void setDept (Dept dept) { this .dept = dept; } @Override public String toString () { return "Emp{" + "dept=" + dept + '}' ; } }
然后创建部门类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Dept { String name; public void setName (String name) { this .name = name; } @Override public String toString () { return "Dept{" + "name='" + name + '\'' + '}' ; } }
添加xml配置代码
在bean标签中添加 autowire 属性实现自动注入
autowire 的值有两个,分别为:
1 2 3 4 5 <bean id ="emp" class ="com.szx.spring.pojo.Emp" autowire ="byName" > </bean > <bean id ="dept" class ="com.szx.spring.pojo.Dept" > <property name ="name" value ="开发部" > </property > </bean >
测试运行效果
Bean 的作用域 我们在xml中配置bena时,默认生成的对象都是单例的,也就是我们调用多次getBean,返回的都是同一个对象实例,例如:
要想实现多例模式,可以在bean 标签上添加 scope=”prototype” 属性
此时再来调用两次 getBean 方法
可以发现两次打印的地址不同,这就代表两次返回的不是同一个对象实例
scope 的值有多个,其中常用的有两个
singleton (默认值)单例模式
prototype 多例模式
singleton 和 prototype 的区别
singleton 是默认值,表示生成的对象是单例的
prototype 表示生成的对象时多例的
创建的时机不同,singleton 是在读取 xml 配置文件时就已经创建好了。prototype 在调用 getBean 方法时才会创建对象实例
Bean 的生命周期 一般情况下,Bean 的声明周期有五步
第一步 调用空参构造器
第二步 调用set方法设置属性值
第三步 调用初始化方法,需要额外配置
第四步 获取到对象实例
第五步 对象实例销毁方法,需要额外配置
通过代码演示
首先新建 Order 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Order { public Order () { System.out.println("第一步 调用空参构造器" ); } 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("第五步 调用销毁方法" ); } }
配置xml
init-method 属性用来设置调用初始化方法
destroy-method 属性用来设置调用销毁方法
1 2 3 <bean id ="order" class ="com.szx.spring.pojo.Order" init-method ="initMethod" destroy-method ="destroyMethod" > <property name ="oname" value ="Spring5" > </property > </bean >
编写测试方法
1 2 3 4 5 6 7 8 9 10 @Test public void test () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml" ); Order order = context.getBean("order" , Order.class); System.out.println("第四步 获取对象实例" ); System.out.println("order = " + order); context.close(); }
执行效果
Bean 的后置处理器 添加了后置处理器后,Bean 的生命周期会变成七个
添加后置处理器要让类实现 BeanPostProcessor 接口,并重写如下两个方法
postProcessBeforeInitialization 在初始化之前执行
postProcessAfterInitialization 在初始化之后执行
1 2 3 4 5 6 7 8 9 10 11 12 13 public class BeanPost 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; } }
然后在 xml 配置文件中添加这个 bean
1 <bean id ="beanpost" class ="com.szx.spring.pojo.BeanPost" > </bean >
添加了后置处理器bean后,会为xml中每一个配置的bean添加后置处理器。这样bean的声明周期会由原来的五个变成七个,分别如下
配置XML读取配置文件 首先需要导入两个jar包
然后编写 xml 配置文件
1 2 3 4 5 6 7 <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="url" value ="jdbc:mysql://127.0.0.1:3306/spring5" /> <property name ="username" value ="root" /> <property name ="password" value ="abc123" /> <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> </bean >
测试是否连接成功
除了直接写在xml文件中之外,我们也可以吧连接数据库的信息写在配置文件中,然后在xml中通过读取配置文件的方式来获取连接
新建一个配置文件 mysql.properties,内容如下
1 2 3 4 prop.url =jdbc:mysql://127.0.0.1:3306/spring5 prop.name =root prop.pwd =abc123 prop.class =com.mysql.jdbc.Driver
然后在 xml 配置文件的 bean 空间里面添加如下属性
1 2 3 xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
然后引入外部文件
1 2 <context:property-placeholder location ="classpath:mysql.properties" />
接着配置连接池,连接池的属性值可以通过 ${xxx}
的方式直接从配置文件中读取,大括号里面的内容就是在配置文件中定义的key
1 2 3 4 5 6 7 <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="url" value ="${prop.url}" /> <property name ="username" value ="${prop.name}" /> <property name ="password" value ="${prop.pwd}" /> <property name ="driverClassName" value ="${prop.class}" /> </bean >
修改后再来测试连接是否成功
Bean 管理配置注解的方式 创建对象 首先需要一个依赖包
然后再xml 的命名空间中添加 context
1 2 3 4 5 6 7 8 <?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" 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" >
开启组件扫描配置
1 2 <context:component-scan base-package ="com.szx.spring" />
base-package 表示要扫描的包的上一级,这样会自动扫描这个目录下的所有类,发现有添加注解的类会自动完成类创建
在poji包下面新建一个 UserServer 类
1 2 3 4 5 6 @Component(value = "userServer") public class UserServer { public void add () { System.out.println("add..." ); } }
添加一个注释 @Component(value = "userServer")
其中 value 表示我们要给这个类起的名字,和 bean 标签中的 id 相同。value 也可以不写,不写默认会将类的首字母小写来作为对象实例名
编写测试类,来测试是否创建成功
通过运行可以看到add方法成功调用,表示类创建成功
除了设置 Component 注解外,可以设置其他注解来完成类的创建,具体有:
@Component
@Service
@Controller
@Repository
这几个的注解功能是一样的,都是用来创建对象
注意:这里推荐使用 jdk1.8 版本。因为我原本使用的是 17 版本,但是添加组件扫描配置后,启动会报错 ,切换到1.8后错误消失
组件扫描配置 开启组件扫描时除了默认扫描方式外,也可以开启自动以扫描配置
配置一:
1 2 3 4 <context:component-scan base-package ="com.szx.spring.pojo" use-default-filters ="false" > <context:include-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan >
配置二:
1 2 3 4 <context:component-scan base-package ="com.szx.spring.pojo" use-default-filters ="false" > <context:exclude-filter type ="annotation" expression ="org.springframework.stereotype.Controller" /> </context:component-scan >
两个区别:一个是 include,一个是 exclude
注入属性@Autowired和@Qualifier的使用 注入属性的注解有四个
@Autowired 根据类型注入
@Qualifier 根据名称注入
@Resource 可以根据类型注入,也可以根据名称注入
@Value 注入普通类型属性
添加 UserDao,使用 @Repository 注解完成对象创建
1 2 3 4 5 6 @Repository public class UserDao { public void add () { System.out.println("dao..." ); } }
然后在 UserServer 类中添加 UserDao 类型的属性,并使用 @Autowired 完成属性赋值
1 2 3 4 5 6 7 8 9 10 11 @Service public class UserServer { @Autowired public UserDao userDao; public void add () { System.out.println("server..." ); userDao.add(); } }
@Qualifier 要和 @Autowired 一起使用
当一个接口有多个实现类时,就不能在通过类型注入,而是要通过名称注入。比如先给 UserDao 设置一个实例名
1 2 3 4 5 6 @Repository(value = "userDao1") public class UserDao { public void add () { System.out.println("dao..." ); } }
然后使用 @Qualifier 注入值
1 2 3 4 5 6 7 8 9 10 11 12 13 @Service public class UserServer { @Autowired @Qualifier(value = "userDao1") public UserDao userDao; public void add () { System.out.println("server..." ); userDao.add(); } }
编写测试方法,来测试是否注入成功。通过结果看到两个add方法都被成功执行
注入属性 @Resource 和 @Value @Resource 不设置name值时表示通过类型注入,设置了name值表示通过名称注入
注意:@Resource 是从 import javax.annotation.Resource; 包中导出来的,并不是 Spring 提供的,所以不推荐使用
1 2 @Resource(name = "userDao1") public UserDao userDao;
@Value 表示普通值注入
1 2 @Value(value = "张三") String name;
在add方法中将name值打印出来
1 2 3 4 public void add () { System.out.println("server..." + name); userDao.add(); }
编写测试方法
完全注解开发
@Configuration // 添加注解,表示这个类是一个配置类
@ComponentScan(basePackages = {“com.szx.spring”}) // 添加自动扫描的注解
首先添加配置类,目的是替代 xml 配置文件
1 2 3 4 5 @Configuration @ComponentScan(basePackages = {"com.szx.spring"}) public class SpringConfig {}
修改测试方法
使用 AnnotationConfigApplicationContext 类,传入配置类的 class
1 2 3 4 5 6 @Test public void test1 () { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserServer userServer = context.getBean("userServer" , UserServer.class); userServer.add(); }
运行效果和之前相同
AOP概述
面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率
通俗描述:不通过修改源代码的方式,在主干功能里面添加新功能
AOP底层使用动态代理 有两种情况的动态代理
有接口情况下,使用JDK动态代理
没有接口情况下,使用CGLIB动态代理
JDK动态代理实现 首先创建一个接口
1 2 3 4 5 public interface UserDao { int add (int a,int b) ; String updateName (String name) ; }
创建接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 public class UserDaoImpl implements UserDao { @Override public int add (int a, int b) { return a + b; } @Override public String updateName (String name) { return "传入的名字是" + name; } }
创建代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.szx.spring.proxy;import java.lang.reflect.Array;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;public class UserProxy { public static void main (String[] args) { Class[] interfaces = {UserDao.class}; UserDaoImpl userDao = new UserDaoImpl(); UserDao proxyUser = (UserDao) Proxy.newProxyInstance(UserProxy.class.getClassLoader(), interfaces, new MyInvocationHandler(userDao)); int add = proxyUser.add(1 , 2 ); System.out.println("add = " + add); } } class MyInvocationHandler implements InvocationHandler { Object obj; public MyInvocationHandler (Object obj) { this .obj = obj; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用方法前触发" ); System.out.println("调用的方法是" + method.getName()); System.out.println("调用的参数是" + Arrays.toString(args)); Object invoke = method.invoke(obj, args); System.out.println("方法执行结束" ); return invoke; } }
测试运行效果
APO术语
连接点
切入点
通知
切面
切面是一个动作,把通知应用到切入点的操作被称为切面
AOP操作准备
AspectJ 注解开发1 首先配置xml配置文件,在命名空间中添加如下命名
1 2 3 4 5 xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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
然后开启自动扫描和AspectJ代理对象
1 2 3 4 5 <context:component-scan base-package ="com.szx.spring.apoanno" > </context:component-scan > <aop:aspectj-autoproxy > </aop:aspectj-autoproxy >
完整的xml配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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.szx.spring.apoanno" > </context:component-scan > <aop:aspectj-autoproxy > </aop:aspectj-autoproxy > </beans >
然后我们在 apoanno 包下新建一个 User 类作为被增强的类
1 2 3 4 5 6 7 8 @Component public class User { public void add () { System.out.println("User add..." ); } }
然后新建 UserProxy 作为 User 类的增强类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.szx.spring.apoanno;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Component @Aspect public class UserProxy { @Before(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void before () { System.out.println("Before 前置通知" ); } @After(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void after () { System.out.println("After 后置通知" ); } @Around(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("Around 环绕之前" ); proceedingJoinPoint.proceed(); System.out.println("Around 环绕之后" ); } @AfterThrowing(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void afterthrowing () { System.out.println("AfterThrowing 异常通知" ); } @AfterReturning(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void afterreturning () { System.out.println("AfterReturning 返回后通知" ); } }
增强类中有多个通知类型的注解
Before 前置通知
After 后置通知(最终通知),无论成功失败都会执行
Around 环绕通知,需要接收 ProceedingJoinPoint 类型的参数,然后在环绕之后执行
AfterThrowing 异常通知,在被增强的方法发生异常后调用
AfterReturning 方法正确执行后调用
编写测试方法运行查看效果
可以看到异常通知没有触发,这是因为 User 类的 add 方法中没有发生异常,现在手动触发一个异常。修改 add 方法
然后再次运行测试方法
异常通知被执行,after 最终通知也被执行
AspectJ 注解开发2 切入点抽取 可以吧相同的切入点进行抽取,在多个通知方法上引调用同一个抽取方法
1 2 3 4 5 6 7 8 9 10 11 @Pointcut(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void pointcut () {} @Before(value = "pointcut()") public void before () { System.out.println("Before 前置通知" ); }
通知优先级 当有多个增强类对同一个类进行增强时,可以设置增强类的优先级,使用 @Order(数字) ,数字越小,优先级越高
新建一个增强类 UserProxy2,也对 User 类进行增强,设置 @Order 的优先级为1
1 2 3 4 5 6 7 8 9 @Component @Aspect @Order(1) public class UserProxy2 { @Before(value = "execution(* com.szx.spring.apoanno.User.add(..))") public void before () { System.out.println("第二个增强类的前置通知" ); } }
运行测试方法
AspectJ 配置文件开发 新建被增强类 Book
1 2 3 4 5 public class Book { public void add () { System.out.println("book add ......" ); } }
新建增强类 BookProxy
1 2 3 4 5 public class BookProxy { public void before () { System.out.println("增强方法" ); } }
配置xml代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?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.szx.spring" > </context:component-scan > <aop:aspectj-autoproxy > </aop:aspectj-autoproxy > <bean id ="book" class ="com.szx.spring.aopxml.Book" > </bean > <bean id ="bookProxy" class ="com.szx.spring.aopxml.BookProxy" > </bean > <aop:config > <aop:pointcut id ="p" expression ="execution(* com.szx.spring.aopxml.Book.add(..))" /> <aop:aspect ref ="bookProxy" > <aop:after method ="before" pointcut-ref ="p" > </aop:after > </aop:aspect > </aop:config > </beans >
测试运行效果
1 2 3 4 5 6 @Test public void test () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml" ); Book book = context.getBean("book" , Book.class); book.add(); }
JdbcTemplate 概述和准备工作 什么是 jdbctemplate?JdbcTemplate 是 Spring 对 jdbc 的封装,可以很方便的对数据库进行增删改查操作
准备工作
导入相关jar包
druid-1.1.10.jar
mysql-connector-java-8.0.28.jar
spring-jdbc-5.2.6.RELEASE.jar
spring-orm-5.2.6.RELEASE.jar
spring-tx-5.2.6.RELEASE.jar
在xml配置文件中开启自动扫描
1 2 <context:component-scan base-package ="com.szx.spring" > </context:component-scan >
配置DataSource
1 2 3 4 5 6 7 <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="url" value ="jdbc:mysql://127.0.0.1:3306/spring5" /> <property name ="username" value ="root" /> <property name ="password" value ="abc123" /> <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> </bean >
配置jdbcTemplate,注入DataSource
1 2 3 4 5 <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref ="dataSource" > </property > </bean >
新建UserDao接口
1 2 3 public interface UserDao {}
添加接口实现类,在dao中注入JdbcTemplate模板
1 2 3 4 5 6 @Component public class UserDaoImpl implements UserDao { @Autowired JdbcTemplate jdbcTemplate; }
添加service类,注入dao
1 2 3 4 5 6 @Component public class UserService { @Autowired UserDao userDao; }
至此,前期准备工作完成
使用 JdbcTemplate 操作数据库 添加数据 首先创建数据库实体类User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.szx.spring.jdbc.pojo;public class User { String id; String name; String age; public String getId () { return id; } public void setId (String id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getAge () { return age; } public void setAge (String age) { this .age = age; } }
在UserDAO接口中增加UserDao方法
1 2 3 public interface UserDao { void addUser (User user) ; }
实现addUser方法,调用 jdbcTemplate.update 方法,返回受影响的行数
1 2 3 4 5 6 7 8 9 10 11 12 13 @Component public class UserDaoImpl implements UserDao { @Autowired JdbcTemplate jdbcTemplate; @Override public void addUser (User user) { String sql = "insert into t_user values(?,?,?)" ; int update = jdbcTemplate.update(sql, user.getId(), user.getName(), user.getAge()); System.out.println(update); } }
然后在UserService中调用addUser方法
1 2 3 4 5 6 7 8 9 10 @Component public class UserService { @Autowired UserDao userDao; public void addUser (User user) { userDao.addUser(user); } }
编写测试方法
查看数据库内容
修改和删除数据 修改和删除和添加调用的方法一样,都是调用 jdbcTemplate 的 update 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public void updateUser (User user) { String sql = "UPDATE t_user SET name = ?" ; int update = jdbcTemplate.update(sql, user.getName()); System.out.println(update); } @Override public void deleteUser (String id) { String sql = "DELETE FROM t_user WHERE id = ?" ; int update = jdbcTemplate.update(sql, id); System.out.println(update); }
查询数据 查询某个值 调用 jdbcTemplate.queryForObject
方法
例如获取表中数据的总条数
1 2 3 4 5 public Integer getCount () { String sql = "select count(*) from t_user" ; Integer integer = jdbcTemplate.queryForObject(sql, Integer.class); return integer; }
测试查看返回
查询返回对象 还是调用 jdbcTemplate.queryForObject
方法。参数有三个,分别意思是
第一个,SQL 语句
第二个,RowMapper 接口的实现类,但是 Spring 已经帮我们做了封装,直接传入 new BeanPropertyRowMapper<>(User.class)
实例
第三个,参数
例如:根据id查询user信息
1 2 3 4 5 public User getUserById (String id) { String sql = "select * from t_user where id = ?" ; User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id); return user; }
测试查询结果,返回的是一个User对象
查询返回集合 返回集合时需要调用 jdbcTemplate.query
方法,方法参数和获取对象的参数相同,没有查询的参数时可以不传
例如:获取所有的User信息放到一个集合中
1 2 3 4 5 6 @Override public List<User> getUserList () { String sql = "select * from t_user" ; List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class)); return userList; }
测试查询结果,返回的是一个User集合
批量添加 调用 jdbcTemplate.batchUpdate
方法,方法接收两个参数
第一个参数:SQL 语句
第二个参数:传入一个集合,里面接受一个Object类型的数组,将多条数据放到集合中,完成批量添加
方法运行逻辑是遍历集合,循环调用SQL语句完成批量新增
例如:完成一个批量添加用户的功能
1 2 3 4 5 6 7 @Override public void batchAdd (List<Object[]> userList) { String sql = "insert into t_user values(?,?,?)" ; int [] ints = jdbcTemplate.batchUpdate(sql, userList); System.out.println("ints = " + Arrays.toString(ints)); }
在service里面调用DAO中定义的方法
1 2 3 4 public void batchAddUser (List<Object[]> userList) { userDao.batchAdd(userList); }
编写测试方法,测试批量新增功能
1 2 3 4 5 6 7 8 9 @Test public void testBatchAdd () { List<Object[]> userList = new ArrayList<>(); Object[] o1 = {"3" ,"王五" ,"23" }; Object[] o2 = {"4" ,"赵六" ,"15" }; userList.add(o1); userList.add(o2); userService.batchAddUser(userList); }
注意:数组中的顺序要和SQL语句中的问号顺序一致
查看数据库,两条数据成功添加
批量删除 批量删除和批量添加方法一致,只是执行的SQL语句不同
1 2 3 4 5 6 7 @Override public void batchUpdate (List<Object[]> userList) { String sql = "update t_user set name = ?,age = ? where id = ?" ; int [] ints = jdbcTemplate.batchUpdate(sql, userList); System.out.println(Arrays.toString(ints)); }
测试调用
1 2 3 4 5 6 7 8 9 @Test public void testBatchUpdate () { List<Object[]> userList = new ArrayList<>(); Object[] o1 = {"王五111" ,"23" ,"3" }; Object[] o2 = {"赵六222" ,"15" ,"4" }; userList.add(o1); userList.add(o2); userService.batchUpdate(userList); }
查看效果
批量删除 1 2 3 4 5 6 7 @Override public void batchDel (List<Object[]> ids) { String sql = "delete from t_user where id = ?" ; int [] ints = jdbcTemplate.batchUpdate(sql, ids); System.out.println(Arrays.toString(ints)); }
测试调用
1 2 3 4 5 6 7 8 9 @Test public void testBatchDel () { List<Object[]> ids = new ArrayList<>(); Object[] o1 = {"3" }; Object[] o2 = {"4" }; ids.add(o1); ids.add(o2); userService.batchDel(ids); }
查看效果,数据被删除
事务概念 事务是数据库操作的最基本单元,逻辑上一组操作,要么都成功,如果有一个操作失败,则操作都失败
事务的特征:
原子性;体现要成功都成功,有一个失败,全部失败
一致性;例如转账操作,转账前和转账后的总金额不变
隔离性;多个事务之间不互相影响
持久性;数据是真正的被保存起来的
搭建事务准备环境 编写程序模拟转账过程
首先创建 t_accout 表,并添加两条数据
添加配置文件 bean.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?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.szx.spring" > </context:component-scan > <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="url" value ="jdbc:mysql://127.0.0.1:3306/spring5" /> <property name ="username" value ="root" /> <property name ="password" value ="abc123" /> <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> </bean > <bean id ="jdbcTemplate" class ="org.springframework.jdbc.core.JdbcTemplate" > <property name ="dataSource" ref ="dataSource" > </property > </bean > </beans >
然后新建 Account 接口
1 2 3 4 5 6 7 8 9 10 11 package com.szx.spring.dao;public interface Account { void addMoney () ; void subMoney () ; }
创建 Account 实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.szx.spring.dao.impl;import com.szx.spring.dao.Account;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Component;@Component public class AccountImpl implements Account { @Autowired public JdbcTemplate jdbcTemplate; @Override public void addMoney () { String sql = "update t_account set money = money + ? where name = ?" ; jdbcTemplate.update(sql,100 ,"lucy" ); } @Override public void subMoney () { String sql = "update t_account set money = money - ? where name = ?" ; jdbcTemplate.update(sql,100 ,"jack" ); } }
编写 service 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.szx.spring.service;import com.szx.spring.dao.impl.AccountImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;@Component public class AccountService { @Autowired public AccountImpl accountImpl; public void accountMoney () { accountImpl.addMoney(); accountImpl.subMoney(); } }
编写测试方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.szx.spring.test;import com.szx.spring.dao.impl.AccountImpl;import com.szx.spring.service.AccountService;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestAccount { @Test public void testaccount () { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml" ); AccountService accountService = context.getBean("accountService" , AccountService.class); accountService.accountMoney(); } }
目录结构
查看运行效果
查看表数据
Spring 事务管理介绍
事务添加到 JavaEE 三层结构里面的 Service 层最合适
在 Spring 进行事务管理操作
声明式事务管理
注解式声明事务管理 首先在 xml 中创建事务管理器
1 2 3 4 5 <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" > </property > </bean >
xml 头部添加 tx 命名空间
1 2 3 xmlns:tx="http://www.springframework.org/schema/tx" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
开启事务注解
1 2 <tx:annotation-driven transaction-manager ="transactionManager" > </tx:annotation-driven >
在 Service 类上面添加注解 @Transactional
,这个注解可以添加到类上和方法上
如果添加类上则为这个类中的所有方法都添加事务
如果添加到方法上则只为这个方法开启事务
在运行测试类之前先把表中的数据重置为各 1000 元
运行测试方法
再次看表数据,查看是否有异常
事务管理注解的参数管理 主要的参数有如下
propagation 传播行为
isolation 事务隔离级别
timeout 超时时间
readOnly 只读
rollbackFor 回滚
boRollbackFor 不回滚
传播行为 propagation 什么是事务传播行为:多事务方法直接进行调用,这个过程中的事务是如何进行管理的,被称为事务传播
事务传播有七种方式
详解:https://blog.csdn.net/qq_34115899/article/details/115602002
隔离级别 isolation
其中 mysql 中默认使用的是可重复读
超时时间 timeout
事务需要在一定时间内进行提交,如果不提交进行回滚
默认值是-1,表示不超时,设置的时间以秒为单位
只读 readOnly 读:表示查询操作;写:增加、删除、修改
readOnly 默认值为 false,表示可以进行增删改查操作
设置 readOnly 为 true 时,改方法只能进行查询操作
回滚 rollbackFor 设置出现那些异常后进行事务回滚
不回滚 boRollbackFor 设置出现那些异常后不进行回滚
配置文件方式声明事务管理
Spring5新特性 整合日志功能 log4j2 首先导入 jar 包
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
新建 log4j2.xml 配置文件,名字是固定的,内容也比较固定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <configuration status ="INFO" > <appenders > <console name ="Console" target ="SYSTEM_OUT" > <PatternLayout pattern ="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" /> </console > </appenders > <loggers > <root level ="info" > <appender-ref ref ="Console" /> </root > </loggers > </configuration >
然后运行测试方法在控制台中查看日志打印
除了被动打印日志外,也可以手动的输入一段日志
1 2 3 4 5 6 7 public class logs { public static Logger logger = LoggerFactory.getLogger(logs.class); public static void main (String[] args) { logger.info("hello logs" ); logger.error("hello logs" ); } }
@Nullable 注解 @Nullable 注解可以用在方法、属性、参数上
用在方法上,表示这个方法的返回值可以为空
1 2 3 4 5 @Nullable public String getName () { return name; }
用在属性上表示这个属性可以为空
用在参数中表示这个参数可以为空
1 2 3 4 public void setName (@Nullable String name1) { this .name = name1; }
函数式风格创建对象 1 2 3 4 5 6 7 8 9 10 11 @Test public void testGenericApplicationContext () { GenericApplicationContext context = new GenericApplicationContext(); context.refresh(); context.registerBean("user1" , User.class,()->new User()); User user1 = context.getBean("user1" , User.class); System.out.println(user1); }
整合JUnit5单元测试框架 导入相关jar包
spring-test-5.2.6.RELEASE.jar
首先整合 Junit4,编写测试类,用到注解有两个
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(“classpath:bean.xml”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.szx.spring.test;import com.szx.spring.service.AccountService;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;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:bean.xml") public class JTest4 { @Autowired private AccountService accountService; @Test public void test () { accountService.accountMoney(); } }
这里的 @Test 注解用的是 JUnit4
整合 Junit5,编写测试类
用到的注解是:
@SpringJUnitConfig(locations = “classpath:bean.xml”)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.szx.spring.test;import com.szx.spring.service.AccountService;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;@SpringJUnitConfig(locations = "classpath:bean.xml") public class JTest5 { @Autowired private AccountService accountService; @Test public void testAccount () { accountService.accountMoney(); } }
这里的 @Test 注解用到的是 JUnit5.7.0
SpringWebflux的使用 等我先去学习SpringMVC和SpringBoot再来补充……