Spring5
Spring 框架概述
-
Spring 是轻量级的开源的JavaEE框架
-
Spring 可以解决企业应用开发的复杂性
-
Spring两个核心IOC和Aop
1)IOC:控制反转
2)Aop:面向切面
-
Spring特点
-
Spring5开始学习!!!
入门前下载相关问题
1)Spring版本:5.2.25
2)下载地址:下不了一点 直接找百度网盘安装包
Spring 5.2.5压缩包
链接:https://pan.baidu.com/s/1sEKdJx7D-PDnyvNIZIS9sw 提取码:fc3n
3)jdk版本:1.8关于为什么用这个版本小白条哥解释了
4)导入相关五个基本jar包
其中一个jar包下载地址common-loging-1.1.1.jar包的下载地址https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/
其他jar包spring5文件中都有
IOC容器
IOC概念和原理
什么是IOC
1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
2)目的:为了耦合度降低
IOC底层原理
用到:xml解析 工厂模式 反射
1)IOC过程
a.xml配置文件
b.创建工厂类
题外话:工厂模式是什么?
是一种简单的设计模式,是一种解耦的方式,但是工厂还是会有耦合度的,为了进一步降低耦合度,使用IOC容器
IOC接口(BeanFactory)
1)IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2)Spring提供IOC容器实现两种方式(两个接口)
a.BeanFactory:IOC容器基本实现,是Spring内部的接口,一般不提供开发人员使用
加载配置文件的时候不会*创建对象,在获取对象(使用)才去创建对象,说人话:什么时候用什么时候创建对象
b.ApplicationContext:BeanFactory接口的子接口,提供更强大的功能,一般由开发人员使用
加载配置文件时候就会*把在配置文件对象进行创建
ApplicationContext接口的实现类
1)FileSystemXmlApplicationContext
new FileSystemXmlApplicationContext(“磁盘下的路径如:D:\java\spring01\src\bean1.xml”)
2)ClassPathXmlApplicationContext
new ClassPathXmlApplicationContext(“类路径如:bean1.xml”)
IOC操作 Bean管理
什么是Bean管理
0)Bean管理指的是两个操作:
1)Spring创建对象
2)Spring注入属性
String name
setName()原先的这种方法现在交给Spring来做,这叫注入属性
Bean管理操作
xml配置文件方式
基于xml方式创建对象
1)在Spring配置文件中,使用bean标签,标签里添加对应属性,就可以实现对象创建
2)bean标签中的常用属性
*id属性:给你的对象取一个别名,唯一的标识
*class属性:创建对象所在类的全路径(包+类路径)
*name属性:和id一样,不过其中可以加特殊符号,了解即可
3)创建对象的时候,默认也是执行无参构造方法
基于xml方式注入属性
DI:依赖注入(注入属性)
第一种:使用set()方法注入
1)创建类,定义属性和对应的set方法
2)在Spring配置文件配置对象创建,配置属性引入
<bean id="book" class="com.zhe.spring5.Book"> 创建对象
<property name="bname" value="小黄书"></property> 属性注入
<property name="bauto" value="段元哲"></property>
</bean>
第二种:使用有参构造进行注入
1)创建类,定义属性和对应的有参构造方法
2)在Spring配置文件配置对象创建,配置属性引入
<bean id="zhe" class="com.zhe.spring5.Orders"> 创建对象
<constructor-arg name="oname" value="蛋糕"></constructor-arg>属性注入
<constructor-arg name="oadress" value="china"></constructor-arg>
</bean>
第三种(了解):p名称空间注入
就是set方法的简化版本
使用这种方式,简化基于xml配置方式
1)添加p名称空间在配置文件中
<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.xsd">
2)进行属性注入,在bean标签里面进行操作
<bean id="book" class="com.zhe.spring5.Book" p:bname="小黄书" p:bauto="段元哲">//现在p名称空间方式实现
<!-- <property name="bname" value="小黄书"></property>-->
<!-- <property name="bauto" value="段元哲"></property>-->//原先set方法实现
</bean>
xml注入其他属性类型
字面量
1)null值
<property name="adress">
<null/>//就是把value这个属性换成了<null/>标签
</property>
2)属性值包含特殊符号
<property name="adress"> 要设置的属性值为value="<< 南 京 >>"
<value><![CDATA[<< 南 京 >>]]></value>
</property>
也可以用转义 < >
注入属性-外部Bean
对对象的属性进行注入
需求:
1)创建两个类
2)在一个类中调用另一个类的方法
配置方法:
3)在Spring文件中进行配置
<bean id="userService" class="com.zhe.spring5.service.UserService">
<property name="UserDao" ref="UserDapImpl"></property>name中放的是需要调用方法所在类的名,这个类需要在UserService中定义并且有set方法 name:类里面对象类型的属性名称 ref中放的是创建UserDaoImpl对象bean标签id值
</bean>
<bean id="UserDapImpl" class="com.zhe.spring5.dao.UserDaoImpl"></bean>
注入属性-内部Bean
<bean id="emp" class="com.zhe.spring5.bean.Emp">
<property name="name" value="段元哲"></property>
<property name="gender" value="男"></property>
<property name="dept">
<bean id="dept" class="com.zhe.spring5.bean.Dept">
<property name="dname" value="安保"></property>
</bean>
</property>
</bean>
注入属性-级联赋值
配置外部Bean赋值后再引入到另一个Bean
<bean id="emp" class="com.zhe.spring5.bean.Emp">
<property name="name" value="段元哲"></property>
<property name="gender" value="男"></property>
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="安保部门"></property>//写法二 需要在Emp中生成个dept的get方法
</bean>
<bean id="dept" class="com.zhe.spring5.bean.Dept">
<property name="dname" value="安保部门"></property>//写法一
</bean>
注入集合类型属性
(以set方法方式为例)
1)注入数组类型属性
<property name="courses">
<array>
<value>mysql</value>
<value>java</value>
</array>
</property>
2)注入List集合类型属性
<property name="list">
<list>
<value>mysql</value>
<value>java</value>
</list>
</property>
3)注入Map集合类型属性
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
细节
1)在集合里面设置对象类型值
<bean id="stu" class="com.zhe.spring5.bean.Spring.Stu">
<property name="courseList">
<list>
<ref bean="course1"></ref> 引入对象
<ref bean="course2"></ref>
</list>
</property>
</bean>
两个对象 为他两个设置值 在外面设置
<bean id="course1" class="com.zhe.spring5.bean.Spring.Course">
<property name="courseName" value="mhyhsql"></property>
</bean>
<bean id="course2" class="com.zhe.spring5.bean.Spring.Course">
<property name="courseName" value="java"></property>
</bean>
上面这种方法只能用在bean中 只有一个类可以使用,我想让多个类使用该怎么办呢?
2)把集合注入部分提取出来
提取出来作为公共部分
1)在Spring配置文件中引入名称空间util
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
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">
2)使用util标签实现注入集合标签(以注入list为例)
1)设置属性
<util:list id="bookList">
<value>水浒传</value>
<value>西游记</value>
<value>红楼梦</value>
</util:list>
2)注入
<bean id="book" class="com.zhe.spring5.bean.Spring.Book">
<property name="list" ref="bookList"></property> 将ref里面的bookList注入到name当中的list里面
</bean>
FactoryBean
String有两种类型的Bean,一种普通Bean,一种工厂Bean(FactoryBean)
普通Bean
在配置文件中定义Bean类型就是返回类型
工厂Bean
在配置文件中定义Bean类型可以和返回类型不一样
1)创建类,让这个类作为工厂Bean,实现接口FactoryBean
2)实现接口里面的方法,在实现的方法中定义返回的Bean类型
这样就可以实现即使在配置文件中配置的是一个类(工厂类),返回值却是另一个类(工厂类中方法返回的类型)
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
//定义返回的Bean类型
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCourseName("Java");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
Bean作用域
单实例和多实例
1)在Spring里面,设置创建Bean实例是单实例还是多实例
2)默认是单实例对象
3)如何设置单实例还是多实例
scope属性
1)Spring配置文件bean标签里面属性scope用于设置单实例还是多实例
2)默认值singleton,表示是单实例对象
3)属性值prototype,表示是多实例对象
二者区别
singleton加载Spring配置文件时候就会创建单实例对象
prototype在调用getBean方法时候创建多实例对象
Bean生命周期
生命周期
从对象创建到对象销毁的过程
bean生命周期
1)通过构造器创建bean实例(无参构造)
2)为bean的属性设置值和对其他bean引用(调用set方法)
3)调用bean的初始化的方法(需要进行配置 init-method=“”)
4)bean 对象可以使用了(获取到了)
5)当容器关闭时,调用bean销毁方法(需要配置 destroy-method=“” )
bean后置处理器
1)通过构造器创建bean实例(无参构造)
2)为bean的属性设置值和对其他bean引用(调用set方法)
** 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
3)调用bean的初始化的方法(需要进行配置 init-method=“”)
** 把bean实例传递bean后置处理器的方法postProcessAfterInitialization
4)bean 对象可以使用了(获取到了)
5)当容器关闭时,调用bean销毁方法(需要配置 destroy-method=“” )
自动装配(xml方式)
什么是自动装配
根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
autowire标签
bean标签autowire,配置自动装配
属性值
byName 根据属性名称注入,注入值bean的id值要和类属性名称一样
byType 根据属性类型注入
<bean id="emp" class="com.zhe.spring5.bean.bean.Emp" autowire="byType">
<!-- <property name="dept" ref="dept"></property> 就是代替了这部分-->
</bean>
<bean id="dept2" class="com.zhe.spring5.bean.bean.Dept">
<property name="name" value="财务部"></property>
</bean>
引入外部属性文件(xml方式)jdbc没学过暂时跳过
直接配置数据库信息
1)配置德鲁伊连接池
2)引入德鲁伊连接池依赖jar包
德鲁伊jar包下载地址
https://repo1.maven.org/maven2/com/alibaba/druid/1.1.9/
要下载名称为druid-1.1.9.jar的不要有后缀,有后缀的不全
好像还有弄jdbc的包啊
这一集(p19)应该有jdbc没学过的知识先暂时跳过
引入外部属性文件配置数据库
注解配置文件方式
什么是注解
1)注解是代码特殊标记,格式:@注解(属性名称=属性值,属性名称=属性值…)
2)注解作用在类上面,方法上面,属性上面
3)使用目的:简化xml配置
基于注解方式实现对象创建
创建对象注解种类
1)@Component
2)@Service
3)@Controller
4)@Repository
功能相同,都可以用来创建bean实例
基于注解方式创建对象实现流程
1)引入依赖
2)开启组件扫描
在spring配置文件中
引入contxt,然后用标签开启组件扫描
<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">
<context:component-scan base-package="com.zhe.spring5"></context:component-scan>进行组件扫描,如果扫描多个包就用逗号隔开,扫描包上层目录
</beans>
注意:
<!-- 不扫描包中全部类-->
<context:component-scan base-package="com.zhe.spring5" use-default-filters="false">
<!-- 扫描带该注解的类-->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Component"/>
</context:component-scan>
<!--扫描包中全部类-->
<context:component-scan base-package="com.zhe.spring5">
<!-- 不扫描带该注解的类-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>
3)创建类,在类上面添加创建对象注解
注意:在注解里面value的属性值即为bean标签的id值
可省略不写,默认值是类名称首字母小写
基于注解方式实现属性注入
注入属性注解种类
1)@AutoWired:根据属性类型进行注入
第一步:在两个类中添加创建对象注解
第二步:在其中一个类中添加另一个类的属性,在属性上面使用注解(不需要添加set方法)
2)@Qualifier:根据属性名称注入
和上面@AutoWired一起使用
3)@Resource:可以根据类型,也可以根据名称进行注入
@Resource 根据类型注入
@Resource(name = “ddd”) 根据名称注入,就是创建对象注解里面的名称@Component(value = “ddd”)
4)@Value:注入普通类型
@Value(value = "abc")
private String name; 将abc注入到name
完全注解开发
1)创建配置文件,替代xml配置文件
@Configuration 代表这是配置文件
@ComponentScan(basePackages = {"com.zhe.spring5"}) 配置文件的扫描范围
public class springConfig {
}
2)测试类写法
@Test
public void test3() {
ApplicationContext context = new AnnotationConfigApplicationContext(springConfig.class); 注意这里没有双引号!!!!
service2 service2 = context.getBean("service2", service2.class);
System.out.println(service2);
service2.add();
}
AOP
AOP概念和原理
什么是AOP
1)面向切面编程,隔离业务逻辑各个部分,使耦合度降低,提高程序可重用性
2)人话:不通过修改源代码的方式,在主干功能里面添加新功能
3)通过登录例子来理解
AOP底层原理
AOP底层使用动态代理
1)有接口情况,使用JDK动态代理
- 创建接口实现类代理对象,增强类的方法
2)没有接口情况,使用CGLIB动态代理
- 创建子类的代理对象,增强类的方法
JDK动态代理实现
1.使用JDK动态代理,使用Proxy类里面的方法创建代理对象
1)调用newProxyInstance方法
方法的三个参数
第一个参数:类加载器
第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口
第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强的方法
编写代码实现JDK动态代理
1)创建接口,定义方法 (UserDao)定义的方法是add()
2)创建接口实现类,实现方法(User)
3)使用Proxy类创建接口代理对象
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(new User()));
int result = dao.add(1, 2);
System.out.println(result);
}
//创建代理对象
static class UserDaoProxy implements InvocationHandler{
//把创建的是谁的代理对象,把谁传递过来
//有参数构造传递
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操作术语
连接点
类里面哪些方法可以被增强,这些方法称为连接点
切入点
实际被增强的方法,称为切入点
通知(增强)
1)实际增强的逻辑部分称为通知(增强)
2)通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
切面
把通知应用到切入点的过程
AOP操作(准备工作)
Spring框架一般都是基于AspectJ实现AOP操作
什么是AspectJ
AspectJ不是Spring组成部分,是独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
基于AspectJ实现AOP操作
1)基于xml配置文件实现
2)基于注解方式实现
在项目工程中引入AOP相关依赖
jar包下载连接在这里Spring/AOP at main · Aha-lxj/Spring · GitHub
赶紧用maven吧hh…
切入点表达式
1)作用:知道对哪个类里面的哪个方法进行增强
2)语法结构
execution(【权限修饰符】【返回类型】 【类全路径】 【方法名称】 (【参数列表】))
注意:权限修饰符可以省略,返回类型不能省略,可以用通配符*来表示,参数列表可以用…来表示
AOP操作(AspectJ注解)
1)创建类,在类里面定义方法
2)创建增强类(编写增强逻辑)
在增强类里面创建方法,让不同方法代表不同通知类型
3)进行通知的配置
-
在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.zhe.spring5.aop"></context:component-scan>
-
使用注解创建类和增强类的对象
-
在增强类上面添加注解@Aspect
-
在Spring配置文件中开启生成代理对象
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
4)配置不同类型的通知
在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
package com.zhe.spring5.aop; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; @Component @Aspect//生成代理对象 public class UserProxy { //前置通知 @Before(value = "execution(* com.zhe.spring5.aop.User.add(..))") public void before(){ System.out.println("before..."); } //最终通知 //在方法执行之后执行,不管有没有异常都会执行 @After("execution(* com.zhe.spring5.aop.User.add(..))") public void after(){ System.out.println("after......"); } //后置通知(返回通知) //在方法返回结果之后执行,有异常出现就不执行 @AfterReturning("execution(* com.zhe.spring5.aop.User.add(..))") public void afterReturning(){ System.out.println("afterReturning......."); } //异常通知,出现异常之后执行 @AfterThrowing("execution(* com.zhe.spring5.aop.User.add(..))") public void afterThrowing(){ System.out.println("afterThrowing......."); } //环绕通知 @Around("execution(* com.zhe.spring5.aop.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("环绕之前......."); //被增强的方法执行 proceedingJoinPoint.proceed(); System.out.println("环绕之后......."); } }
5)相同的切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.zhe.spring5.aop.User.add(..))")
public void pointDemo(){
}
//前置通知
@Before(value = "pointDemo()")
public void before(){
System.out.println("before...");
}
6)多个增强类对同一个方法进行增强,设置增强类优先级
在增强类上面添加注解@Order(数字类型值),值越小,优先级越高
7)完全注解开发
创建配置类,不需要创建xml配置文件
@Configuration
@ComponentScan(basePackages = {"com.zhe.spring5.aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class configAop {
}
AOP操作(AspectJ配置文件)了解即可
1)创建两个类,增强类和被增强类,在类里面定义方法
2)在Spring配置文件中创建两个类对象
<bean id="book" class="com.zhe.spring5.aop_xml.Book"></bean>
<bean id="bookProxy" class="com.zhe.spring5.aop_xml.BookProxy"></bean>
3)在Spring配置文件中配置切入点
<!-- 配置aop增强-->
<aop:config>
<!-- 切入点-->
<aop:pointcut id="p" expression="execution(* com.zhe.spring5.aop_xml.Book.add(..))"/>
<!-- 配置切面-->
<aop:aspect ref="bookProxy">
<aop:before method="before" pointcut-ref="p"></aop:before>
</aop:aspect>
</aop:config>
事务
事务概念
什么是事务
事务是数据库操作的基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有的操作都失败
事务四个特性(ACID)
1)原子性
2)一致性
3)隔离性
4)持久性
搭建事务操作环境
(需要jdbc技术啊。。。ok学成归来,继续事务)
1)使用JdbcTemplate需要先引入jar包
2)场景:经典转账,1号向2号转100块钱
实现步骤
1)创建数据库表,添加记录
2)创建service 搭建dao,完成对象创建和注入关系
3)创建转账方法,实现转账
xml配置
<!-- 组件扫描-->
<context:component-scan base-package="com.zhe"></context:component-scan>
<!--创建数据库连接池 德鲁伊连接池-->
<bean id="dataSouce" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql:///atguigu"/>
<property name="username" value="root"/>
<property name="password" value="dyz200472"/>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> 注意mysql8+要有cj
</bean>
<!--创建jdc模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSouce"></property>
</bean>
dao中代码
@Repository
public class UserDaoImpl implements UserDao {//UserDao是自己实现的接口,里面定义了两个转账方法
@Autowired//注入jdbc模板对象
private JdbcTemplate jdbcTemplate;
@Override
public void addMoney() {
String sql = "update t_bank set money=money+? where id = ?";
jdbcTemplate.update(sql,100,2);
}
@Override
public void subMoney() {
String sql = "update t_bank set money=money-? where id = ?";
jdbcTemplate.update(sql,100,1);
}
}
service层代码
@Service(value = "userService")
public class UserService {
//注入UserDao
@Autowired
private UserDao userDao;
public void accountMoney(){
userDao.subMoney();
userDao.addMoney();
}
}
大致逻辑:在service层注入dao,在dao层注入JdbcTemplate,在JdbcTemplate中又注入数据库连接池
事务场景引入
发现问题
如果转账时,subMoney方法后面出现了异常,就会出现1号钱少了,但是因为异常导致程序中断导致2号钱没有增加,于是1号:我钱呢?哎不对我钱呢?
解决问题
*使用事务进行解决,用事务就可以拯救1号的钱钱~
*事务操作过程
1)开启事务
2)进行业务操作
3)没有异常:提交事务 出现异常:事务回滚
Spring事务管理介绍
事务添加地点
事务添加到javaEE三层结构里面的Service层(业务逻辑层)
在Spring进行事务管理操作
1)有两种方式:编程式事务管理和声明式事务管理
2)编程式就是在代码中写,spring中一般使用声明式事务管理
声明式事务管理
1)基于注解方式
2)基于xml方式
3)底层使用AOP原理
Spring事务管理API
提供一个接口,代表事务管理器,这个接口针对不同的框架实现不同的实现类
PS:idea快捷键ctrl+h 查看向下或者向上的继承操作
Spring声明式事务管理-注解方式实现
1.在Spring配置文件中配置事务管理器
<!-- 创建事务管理器-->
<bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSouce"></property>
</bean>
2.在Spring配置文件中,开启事务注解
1)引入名称空间tx
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
2)开启事务注解
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="TransactionManager"></tx:annotation-driven>
3.在Service类上面(或者Service类里面的方法上面)添加事务注解
1)@Transactional,可以添加到类上面,也可以添加到方法上面
2)如果添加到类上面,为这个类里面所有的方法都添加事务
3)如果添加到方法上面,为这个方法添加事务
Sping声明式事务管理-事务参数
传播行为
propagation:事务传播行为
1)当一个事务方法被另一个事务方法调用时,这个事务方法如何进行
2)Spring定义了7种类传播行为,重点记住以下三种
required:如果有事务在运行,当前方法就在这个事务内运行,否则,就启动一个新的事务,并在新的事务内运行
required_new:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起
supports:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中
隔离级别
ioslation:事务隔离级别
1)事务的特性之一隔离性,使得多事务操作之间不会产生影响,不考虑隔离性会产生很多问题
2)产生的读问题:脏读,不可重复读,虚(幻)读
脏读
一个未提交事务读取到另一个未提交事务的数据
岳不群改变金额后,东方不败读取到数据,但是岳不群进行事务回滚,没有提交,对数据库进行修改,发生了脏读
不可重复读
一个未提交事务读取到另一个提交事务修改数据
导致一个事务先后读取同一条记录,但两次读取的数据不同,即不可重复读
幻读
一个未提交事务读取到另一个提交事务添加数据
3)通过设置事务隔离级别,解决读问题
级别分类
设置隔离级别,MySQL默认REPEATABLE_READ这个级别
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
其他参数
timeout
1)事务需要在一定时间内进行提交,如果不提交进行回滚
2)默认值是-1,设置时间以秒单位进行计算
readOnly:是否只读
1)读:查询操作,写:添加修改删除操作
2)默认值false,表示可以查询,添加修改删除操作
3)设置为true之后,只能查询
rollbackFor:回滚
设置出现哪些异常进行事务回滚
noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚
Spring声明式事务管理-完全注解开发
配置类
Configuration//配置类
@ComponentScan(basePackages = "com.zhe")//组件扫描
@EnableTransactionManagement//开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");//驱动
druidDataSource.setUrl("jdbc:mysql:///atguigu");//数据库地址
druidDataSource.setUsername("root");
druidDataSource.setPassword("dyz200472");
return druidDataSource;
}
//创建jdbcTemplate对象
@Bean
//到ioc容器中找到DruidDataSource
public JdbcTemplate getjdbcTemplate(DruidDataSource druidDataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入DruidDataSource
jdbcTemplate.setDataSource(druidDataSource);
return jdbcTemplate;
}
//创建事务管理器对象
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource druidDataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(druidDataSource);
return dataSourceTransactionManager;
}
}
测试类
@Test
public void test2(){
ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}