目录
2.2 Spring提供的IOC容器实现的两种方式(两个接口)
6.2 Spring5框架核心容器支持@Nullable注解
6.3 Spring5核心容器支持函数式风格GenericApplicationContext
1. Spring框架概述
- Spring是轻量级的开源的JavaEE框架
- Spring可以解决企业应用开发的复杂性
- Spring有两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象的过程交给Spring进行管理
- AOP:面向切面,不修改源代码进行功能增强
- Spring的特点:
- 方便解耦,简化开发
- AOP编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低API开发难度
2. IOC容器
2.1 什么是IOC(控制反转)
- 控制反转:把对象创建和对象之间的调用过程,交给Spring进行管理
- 使用IOC的目的:为了耦合度降低
- IOC底层:xml解析、工厂模式、反射
2.2 Spring提供的IOC容器实现的两种方式(两个接口)
- BeanFactory接口:IOC容器基本实现,是Spring内部的使用接口,不提供给开发人员进行使用,加载配置文件时候不会创建对象,在获取对象时才会创建对象。
- ApplicationContext接口:BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)推荐使用!
- ApplicationContext接口的实现类有:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext
2.3 入门使用案例
①.导入Spring5相关jar包
②.创建普通类并创建方法
public class User {
public void add() {
System.out.println("add......");
}
}
③.创建Spring配置文件,在配置文件中配置创建的对象
// Spring配置文件使用xml格式:bean1.xml
<?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">
<!--配置User对象创建 -->
<bean id="user" class="com.atguigu.spring5.User"></bean>
</beans>
④.编码测试
@Test
public void testAdd() {
//1.加载spring配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
//2.获取配置创建的对象
User user = context.getBean("user",User.class);
System.out.println(user);//com.atguigu.spring5.User@4abdb505
user.add();//add......
}
2.4 IOC操作Bean管理(概念)
2.4.1 什么是Bean管理
- Spring创建对象
- Spring注入属性
2.4.2 Bean管理的两种操作方式
- 基于xml配置文件方式实现
- 基于注解方式实现
2.4.3 Bean作用域
①.在Spring里面,设置创建bean实例是单实例还是多实例。默认情况下,bean是单实例对象
②.如何设置单实例还是多实例
- 在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
- scope属性值:
- 默认值,singleton,表示对象是单实例对象
- prototype,表示是多实例对象
2.4.4 Bean生命周期
①.生命周期:从对象创建到对象销毁的过程
无后置处理器:
- 通过构造器创建bean实例(执行无参数构造创建bean实例)
- 为bean的属性设置值和对其他bean引用(调用set方法设置属性值)
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- bean可以使用了(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
有后置处理器:
- 通过构造器创建bean实例(执行无参数构造创建bean实例)
- 为bean的属性设置值和对其他bean引用(调用set方法设置属性值)
- 把bean实例传递bean后置处理器的方法 postProcessBeforeInitialization
- 调用bean的初始化的方法(需要进行配置初始化的方法)
- 把bean实例传递bean后置处理器的方法 postProcessAfterInitialization
- bean可以使用了(对象获取到了)
- 当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
②.演示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");
Orders orders = context.getBean("orders",Orders.class);
System.out.println("第四步 获取创建bean实例对象");
System.out.println(orders);
//手动让bean实例销毁
context.close();
}
有后置处理器:
创建类,实现接口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;
}
}
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>
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
@Test
public void testBean3() {
ApplicationContext context = new ClassPathXmlApplicationContext( "bean4.xml");
Orders orders = context.getBean("orders",Orders.class);
System.out.println("第四步 获取创建bean实例对象");
System.out.println(orders);
//手动让bean实例销毁
context.close();
}
2.4.5 xml自动装配:根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
1>根据属性名称自动注入
<!--实现自动装配
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>
2>根据属性类型自动注入
<!--实现自动装配
bean标签属性autowire,配置自动装配
autowire属性常用两个值:
byName根据属性名称注入,注入值bean的id值和类属性名称一样
byType根据属性类型注入
-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
2.4.6 外部属性文件
引入德鲁伊连接池依赖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="abc123"></property>
</bean>
②.引入外部属性文件配置数据库连接池
>创建外部属性文件,properties格式文件,写数据库信息
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.username=root
prop.password=abc123
>把外部properties属性文件引入到spring配置文件中
首先引入context名称空间,再引入外部属性文件
<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>
2.5 IOC操作Bean管理——基于xml方式
2.5.1 基于xml方式创建对象
- ①.在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象的创建
- ②.在bean标签中有很多属性,比较常用的属性是:
- > id属性:唯一标识
- > class属性:类全路径(包类路径)
- ③.创建对象时,默认是执行无参数构造方法完成对象创建
<!--1.配置User对象创建-->
<bean id="user" class="com.atguigu.spring5.User"></bean>
2.5.2 基于xml方式注入属性(DI:依赖注入(注入属性))
①. 使用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配置方式
<!--1.添加p名称空间在配置文件头部-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<!--在这里添加一行p-->
xmlns:p="http://www.springframework.org/schema/p"
<!--2.在bean标签进行属性注入(算是set方式注入的简化操作)-->
<bean id="book" class="com.atguigu.spring5.Book"
p:bname="very" p:bauthor="good">
</bean>
2.5.3 xml注入其他类型属性
①. 字面量
<bean id="book" class="com.atguigu.spring5.Book">
<!--1)null值-->
<property name="address">
<null/><!--属性里边添加一个null标签-->
</property>
<!--2)属性值包含特殊符号
a.把<>进行转义 < >
b.把带特殊符号内容写到CDATA
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
</bean>
②. 注入属性-外部bean
1)创建两个类service类和dao类
2)在service里调用dao里面的方法
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();
}
}
3)在spring配置文件中进行配置
<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
1>一对多关系:部门和员工。一个部门有多个员工,一个员工属于一个部门,部门是一,员工是多
2>在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
// 部门类
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;
}
}
3>在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>
④. 注入属性-级联赋值
1>第一种写法
<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>
2>第二种写法
emp类里面需要有get方法:
public Dept getDept(){
return dept;
}
<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>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>
2.5.4 xml注入集合属性
- 1>注入数组类型属性
- 2>注入List集合类型属性
- 3>注入Map集合类型属性
- 4>在集合里面设置对象类型值
- 5>把集合注入部分提取出来
1>注入数组类型属性
2>注入List集合类型属性
3>注入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配置文件进行配置
<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>
4>在集合里面设置对象类型值
//学生所学多门课程
private List<Course> courseList;//创建集合
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
<!--创建多个course对象-->
<bean id="course1" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="Spring5框架"></property>
</bean>
<bean id="course2" class="com.atguigu.spring5.collectiontype.Course">
<property name="cname" value="MyBatis框架"></property>
</bean>
<!--注入list集合类型,值是对象-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
</bean>
5>把集合注入部分提取出来
<!--第一步:在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"
<!--添加util名称空间-->
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名称空间-->
<!--第二步:使用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" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
2.6 IOC操作Bean管理(FactoryBean)
- Spring有两种类型 bean,一种普通bean,另外一种工厂bean(FactoryBean)
- 普通bean:在配置文件中定义bean类型就是返回类型
- 工厂bean:在配置文件定义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;
}
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext( "bean3.xml");
Course course = context.getBean("myBean",Course. class);
System.out.println(course);
}
2.7 IOC操作Bean管理——基于注解方式
2.7.1 什么是注解
- 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,...)
- 使用注解的目的是:简化xml配置
- 注解作用在类、方法、属性上面
2.7.2 Spring针对Bean管理中创建对象提供注解,这四个注解都可以用来创建bean实例
- @Component:一般用于普通类
- @Service:一般用于service实现类
- @Controller:一般用于Controller类
- @Repository:一般用于Dao实现类
2.7.3 基于注解方式实现对象创建
①.注解+xml配置文件的方式
- 引入依赖
- 开启组件扫描
- 创建类,在类上面添加创建对象的注解
- 基于注解的方式实现属性注入
- @Autowired:根据属性类型进行自动装配
- @Qualifier:根据名称进行注入,这个注解要和上面的@Autowired一起使用。使用的原因是因为一个接口可能有多个实现类,根据类型注入无法找到具体是哪一个,所以需要使用名称注入
- @Resource:可以根据名称进行注入,也可以根据类型进行注入
- @Value:注入普通类型属性
<!--开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!--开启组件扫描
3 开启组件扫描的细节配置
>use-default-filters="false"表示现在不使用默认filter,自己配置filter
>context:include-filter,设置扫描哪些内容
>context:exclude-filter,设置哪些内容不进行扫描
-->
<!--开启组件component扫描scan,扫描包(com.atguigu)中的类的注解,只扫描controller注解-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression= "org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--开启组件component扫描scan,扫描包(com.atguigu)中的类的注解,不扫描controller注解,其他的注解都扫描-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression= "org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--创建类
1 在类上添加创建对象注解
2 在注解里面value属性值可以省略不写
3 默认值是类名称,首字母小写,如UserService - userService
-->
@Component(value="userService")
//注解的方式等价于<bean id="userService" class=".."/>
public class UserService {
public void add() {
System.out.println("service add.......");
}
}
<!--基于注解的方式实现属性注入
1 @Autowired:根据属性类型进行自动装配
第一步:创建service、dao类,在类上添加创建对象注解
第二步:在service类添加dao类型属性,在属性上面使用注解
-->
@Service
public class UserService {
//定义dao类型属性
//不需要添加set方法,因为在注解中已经封装了
//添加注入属性注解
@Autowired
private UserDao userDao; ;
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
<!--基于注解的方式实现属性注入
2 @Qualifier:根据名称进行注入,这个注解要和上面的@Autowired一起使用
使用的原因是因为一个接口可能有多个实现类,根据类型注入无法找到具体是哪一个,所以需要使用名称注入
-->
//定义dao类型属性
//不需要添加set方法,因为在注解中已经封装了
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
<!--基于注解的方式实现属性注入
3 @Resource:可以根据名称进行注入,也可以根据类型进行注入
-->
//@Resource //根据类型进行注入
@Resource(name ="userDaoImpl1") //根据名称进行注入
private UserDao userDao;
<!--基于注解的方式实现属性注入
4 @Value:注入普通类型属性
-->
@Value(value="abc")
private String name;
②.完全注解开发
1 创建配置类,替代xml配置文件
@Configuration //作为配置类,替代xml配置文件
@ComponentScan(basePackages={"com.atguigu"}) //包扫描
public class SpringConfig{
}
2 其他内容不变,见①注解+xml配置文件的方式
3 编写测试类
@Test
public void testService2() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService",UserService.class);
System.out.println(userService);
userService.add();
}
3. AOP
3.1 什么是AOP
- 面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
- 通俗描述:不通过修改源代码的方式,在主干功能里面添加新功能
- 使用登录例子说明AOP
3.2 AOP底层原理
3.2.1 AOP底层使用动态代理(两种情况)
①.有接口情况,使用JDK动态代理
②.没有接口情况,使用CGLIB动态代理
3.2.2 JDK动态代理
①.使用JDK动态代理,使用Proxy类里面的方法创建代理对象
调用newProxyInstance方法,返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序
static Object newProxyInstance(ClassLoader loader,类<?> interfaces,InvocationHandler h)
第一个参数:类加载器
第二个参数:增强方法所在的类实现的接口,支持多个接口
第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强的部分
②.编写JDK动态代理代码
- 创建接口,定义方法
- 创建接口实现类,实现方法
- 使用Proxy类创建接口代理对象
1 创建接口,定义方法
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
2 创建接口实现类,实现方法
public class UserDaoImpl implements UserDao {
@Override
public int add(int a,int b) {
return a+b;
}
@Override
public String update(String id) {
return id;
}
}
3 使用Proxy类创建接口代理对象
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
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;
}
}
3.3 AOP术语
- 连接点:类里面哪些方法可以被增强,这些方法称为连接点
- 切入点:实际被真正增强的方法,称为切入点
- 通知(增强):实际增强的逻辑部分称为通知,通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知
- 切面:是动作,把通知应用到切入点的过程
3.4 AOP操作(准备工作)
- Spring框架一般都是基于AspectJ实现AOP操作,AspectJ不是Spring组成部分,独立于AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
- 基于AspectJ实现AOP操作:
- 基于xml配置文件实现
- 基于注解方式实现(推荐使用)
- 切入点表达式
- 作用:知道对哪个类里面的哪个方法进行增强
- 语法结构:execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
- 举例:
- 对com.atguigu.dao.BookDao类里面的add进行增强
- execution(* com.atguigu.dao.BookDao.add(..))
- 对com.atguigu.dao.BookDao类里面的所有方法进行增强
- execution(* com.atguigu.dao.BookDao.*(..))
- 对com.atguigu.dao包里面所有类,类里面的所有方法进行增强
- execution(* com.atguigu.dao.*.*(..))
3.5 AOP操作(AspectJ注解)
- 创建类,在类里面定义方法
- 创建增强类(编写增强逻辑)
- 进行通知的配置
- 配置不同类型的通知
- 相同的切入点抽取
- 有多个增强类对同一个方法进行增强,设置增强类优先级
- 完全使用注解开发
1 创建类,在类里面定义方法
@Component
public class User{
public void add(){
System.out.println("add...");
}
}
2 创建增强类(编写增强逻辑)
(1)在增强类里面,创建方法,让不同方法代表不同通知类型
@Component
@Aspect // 表示生成代理对象
public class UserProxy{
public void before(){//前置通知
System.out.println("before...")
}
}
3 进行通知的配置
(1)在spring配置文件中,开启注解扫描
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
(2)使用注解创建User和UserProxy对象,即在User类和UserProxy类上添加注解@Component
(3)在增强类上添加注解@Aspect,表示生成代理对象
(4)在spring配置文件中开启生成代理对象
<!-- 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4 配置不同类型的通知
(1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
@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(..))")
public 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("环绕之后...");
}
}
5 相同的切入点抽取
//相同切入点抽取
@Pointcut(value="execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo(){
}
//前置通知
//@Before注解表示作为前置通知
@Before(value="pointdemo()")
public void before(){
System.out.println("before...");
}
6 有多个增强类对同一个方法进行增强,设置增强类优先级
(1)在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy
7 完全使用注解开发
(1)创建配置类,不需要创建xml配置文件
@Configuration
@ComponentScan(basePackages={"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class ConfigAop {
}
3.6 AOP操作(AspectJ配置文件)
- 创建两个类,增强类和被增强类,创建方法
- 在spring配置文件中创建两个类对象
- 在spring配置文件中配置切入点
2 创建对象
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
3 在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>
4. JdbcTemplate
4.1 什么是JdbcTemplate
- spring框架对jdbc进行封装,使用JdbcTemplate方便实现对数据库的操作
4.2 准备工作
- 引入相关jar包
- 在spring配置文件中配置数据库连接池
- 配置JdbcTemplate对象,注入DataSource
- 创建Service类、Dao类,在dao注人jdbcTemplate对象
2 在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>
3 配置JdbcTemplate对象,注入DataSource
<!--JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
4 创建Service类、Dao类,在dao注人jdbcTemplate对象
<!--组件扫描-->
<context:component-scan base-package="com.atguigu"/>
@Service
public class BookService{
// 注入dao
@Autowired
private BookDao bookDao;
}
@Repository
public class BookDaoImpl implements BookDao{
// 注入JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
4.3 JdbcTemplate操作数据库
4.3.1 添加
- 对应数据库创建实体类
- 编写service和dao
- 测试类
1 对应数据库创建实体类
public class Book {
private String bid;
private String bname;
private String bstatus;
public String getBid() {
return bid;
}
public void setBid(String bid) {
this.bid = bid;
}
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
public String getBstatus() {
return bstatus;
}
public void setBstatus(String bstatus) {
this.bstatus = bstatus;
}
}
2 编写service和dao
@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.getBid(),book.getBname(),book.getBstatus()};
int update = jdbcTemplate.update(sql,args);
System.out.println(update);
}
}
3 测试类
@Test
public void testJdbcTemplate() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
BookService bookService = context.getBean("bookService",BookService.class);
Book book = new Book();
book.setBid("1");
book.setBname("java");
book.setBstatus("a");
bookService.addBook(book);
}
4.3.2 修改和删除
1 修改
@Override
public void updateBook(Book book) {
String sql = "update t_book set bname=?,bstatus=? where b_id=?";
Object[] args = {book.getBname(),book.getBstatus(),book.getBid()};
int update = jdbcTemplate.update(sql,args);
System.out.println(update);
}
2 删除
@Override
public void delete(String id) {
String sql = "delete from t_book where b_id=?";
int update = jdbcTemplate.update(sql,id);
System.out.println(update);
}
4.3.3 查询返回某个值
查询表里有多少条记录,返回是某个值
queryForObject(String sql,Class<T> requiredType)
参数1:sql语句;参数2:返回类型Class
// 查询表记录数
@Override
public int selectCount() {
String sql = "select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql,Integer.class);
return count;
}
4.3.4 查询返回对象
queryForObject(String sql,RowMapper<T> rowMapper,Object ... args)
参数1:sql语句
参数2:RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
参数3:sql语句值
// 查询返回对象
@Override
public Book findBookInfo(String id){
String sql = "select * from t_book where b_id = ?";
// 调用方法
Book book = jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Book>(Book.class),id);
return book;
}
4.3.5 查询返回集合
query(String sql,RowMapper<T> rowMapper,Object ... args)
// 查询返回集合
@Override
public List<Book> findAllBook(){
String sql = "select * from t_book";
List<Book> bookList = jdbcTemplate.query(sql,new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
4.3.6 批量操作
操作表里面的多条记录
1 比如实现批量添加操作
batchUpdate(String sql,List<Object[]> batchArgs)
参数2: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);
2 实现批量修改操作
//批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
String sql = "update t_book set bname=?,bstatus=? where b_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);
3 实现批量删除操作
//批量删除
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
String sql = "delete from t_book where b_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);
5. 事务管理
5.1 事务概念
5.1.1 什么是事务
- 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
- 典型场景:银行转账,lucy转账100元给mary,lucy少100,mary多100
5.1.2 事务四个特性(ACID)
- 原子性Atomicity:事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只允许出现两种状态之一,要么都成功,要么都失败。任何一项操作都会导致整个事务的失败,同时其它已经被执行的操作都将被撤销并回滚,只有所有的操作全部成功,整个事务才算是成功完成。
- 一致性Consistency:事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱。
- 隔离性Isolation:事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。
- 持久性Duration:事务的持久性是指事务一旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态。
5.2 搭建事务操作环境
5.2.1 思路图
5.2.2 创建service,搭建dao。完成对象创建和注入关系
- service注入 dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
- 在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账的方法)
@Service
public class UserService {
//注入 dao
@Autowired
private UserDao userDao;
//转账的方法
public void accountMoney() {
//lucy 少 100
userDao.reduceMoney();
//模拟异常
int i = 10/0;
//mary 多 100
userDao.addMoney();
}
}
@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");
}
}
// 事务操作过程
public void accountMoney(){
try{
//第一步 开启事务
//第二步 进行业务操作
//lucy 少 100
userDao.reduceMoney();
//模拟异常
int i = 10/0;
//mary 多 100
userDao.addMoney();
//第三步 没有发生异常,提交事务
}catch(Exception e){
//第四步 出现异常,事务回滚
}
}
5.3 Spring事务管理介绍
- 事务添加到JavaEE三层结构里面的Service层(业务逻辑层)
- 在Spring进行事务管理操作有两种方式:
- 编程式事务管理
- 声明式事务管理(使用)
- 基于注解的方式(使用)
- 基于xml配置文件方式
- 在spring进行声明式事务管理,底层使用AOP原理
5.4 注解声明式事务管理
- 在spring配置文件中配置事务管理器
- 在spring配置文件,开启事务注解
- (1)在spring配置文件引入名称空间tx
- (2)开启事务注解
- 在service类上面(或者service类里面方法上面)添加事务注解
- @Transactional,这个注解添加到类上面,也可以添加在方法上面
- 如果把这个注解添加到类上面,这个类里面所有的方法都添加事务
- 如果把这个注解添加到方法上面,为这个方法添加事务
1 在spring配置文件中配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
2 在spring配置文件,开启事务注解
(1)在spring配置文件引入名称空间tx
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
(2)开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
3 在service类上面(或者service类里面方法上面)添加事务注解
@Service
@Transactional
public class UserService {
5.5 声明式事务管理参数配置
5.5.1 在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数
5.5.2 propagation:事务传播行为
①. 多事务方法直接进行调用,这个过程中事务是如何进行管理的
- REQUIRED:
- 如果add方法本身有事务,调用update方法之后,update使用当前add方法里面事务
- 如果add方法本身没有事务,调用update方法之后,创建新事务
- REQUIRED_NEW:使用add方法调用update方法,如果add无论是否有事务,都创建新的事务
②. 事务的传播行为可以由传播属性指定。Spring定义了7种类传播行为:
- REQUIRED:如果有事务在运行,当前的方法就在这个事物内运行,否则,就启动一个新的事务,并在自己的事务内运行
- REQUIRED_NEW:当前的方法必须启动新事务,并在它自己的事物内运行,如果有事务正在运行,应该将它挂起
- SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
- NOT_SUPPORTED:当前的方法不应该运行在事务中。如果有运行的事务,将它挂起
- MANDATORY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
- NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常
- NESTED:如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行
@Service
@Transactional(propagation=Propagation.REQUIRED)
public class UserService{
}
5.5.3 isolation:事务隔离级别
事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题。有三个读问题:脏读、不可重复读、虚(幻)读
①. 脏读:一个未提交事务读取到另一个未提交事务的数据
②. 不可重复读:一个未提交事务读取到另一提交事务修改数据
③. 虚(幻)读:一个未提交事务读取到另一提交事务添加数据
④. 解决:通过设置事务隔离级别,解决读问题
@Service
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.REPEATABLE_READ)
public class UserService{
}
5.5.4 timeout:超时时间
- 事务需要在一定时间内进行提交,如果不提交进行回滚
- 默认值是 -1 ,设置时间以秒单位进行计算
5.5.5 readOnly:是否只读
- 读:查询操作,写:添加修改删除操作
- readOnly默认值false,表示可以查询,可以添加修改删除操作
- 设置readOnly值是true,则只能查询
5.5.6 rollbackFor:回滚
- 设置出现哪些异常进行事务回滚
5.5.7 noRollbackFor:不回滚
- 设置出现哪些异常不进行事务回滚
5.6 XML声明式事务管理
- 第一步 配置事务管理器
- 第二步 配置通知
- 第三步 配置切入点和切面
<!--1 创建事务管理器-->
<bean id="transactionManager" class= "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</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.7 完全注解声明式事务管理
@Configuration//配置类
@ComponentScan(basePackages = "com.atguigu.spring5.jdbc1")//组件扫描
@EnableTransactionManagement//开启事务
public class SpringConfig {
//创建数据库的连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/user_db?serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("abc123");
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 transactionManager1 = new DataSourceTransactionManager();
transactionManager1.setDataSource(dataSource);
return transactionManager1;
}
}
6. Spring5新特性
6.1 Spring5框架自带了通用的日志封装
- Spring5已经移除了Log4JConfigListener,官方建议使用Log4j2
- Spring5框架整合Log4j2
<?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>
6.2 Spring5框架核心容器支持@Nullable注解
- @Nullable注解可以使用在方法、属性、参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
1 注解用在方法上面,方法返回值可以为空
@Nullable
String getId();
2 注解使用在方法参数里面,方法参数可以为空
public <T> void registerBean(
@Nullable String beanName,
this.reader.registerBean(beanClass,beanName,suppli)
){
}
3 注解使用在属性上面,属性值可以为空
@Nullable
private String bookName;
6.3 Spring5核心容器支持函数式风格GenericApplicationContext
@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);
}
6.4 Spring5支持整合JUnit5
6.4.1 整合JUnit4
- 引入Spring相关针对测试依赖
- 创建测试类,使用注解方式完成
@RunWith(SpringJUnit4ClassRunner. class) //单元测试框架
@ContextConfiguration("classpath:bean1.xml") //加载配置文件
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test1() {
userService.accountMoney();
}
}
6.4.2 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();
}
}
6.5 Spring5框架新功能(Webflux)
6.5.1 SpringWebflux介绍
- 是Spring5添加新的模块,用于web开发的,功能和SpringMVC类似,Webflux使用当前一种比较流行的响应式编程出现的框架
- 使用传统web框架,比如SpringMVC,这些基于Servlet容器,Webflux 是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。
- 异步非阻塞的解释:
- 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步。
- 阻塞和非阻塞针对被调用者,被调用者受到请求之后,做完请求任务之后才给出反馈就是阻塞,受到请求之后马上给出反馈然后再去做事情就是非阻塞
- Webflux特点:
- 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
- 函数式编程:Spring5框架基于java8,Webflux使用java8函数式编程方式实现路由请求
- 比较SpringMVC:
- 两个框架都可以使用注解方式,都运行在Tomcat等容器中
- SpringMVC采用命令式编程,Webflux采用异步响应式编程