花了3天时间,Spring也终于看完了,SSM已经干完了两个了,感觉人都要没了😇。还是老样子,留下笔记以免遗忘。
下面的内容都是基于遇见狂神说的Spring5视频教程而来,在此感谢能提供这么优秀的教程。
# Spring
# 简介
Spring就是用来简化Java开发的,只能说学Java不学Spring,就像读四大名著不读红楼梦……
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
Spring理念 : 使现有技术更加实用。 本身就是一个大杂烩 , 整合现有的框架技术。
SSM:SpringMVC+Spring+MyBatis
几个常用的网址:
|
|
# 优点
-
Spring是一个开源免费的框架 , 容器
-
Spring是一个轻量级的框架 , 非侵入式的
-
控制反转(IoC),面向切面(Aop)
-
对事务的支持 , 对框架的支持
一句话概括:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
# 拓展
- Spring Boot
- 一套快速配置脚手架
- 可以基于Spring Boot 快速开发单个微服务
- Spring Cloud
- Spring Cloud是基于Spring Boot实现的
学习路线:Spring -> SpringMVC -> Spring Boot -> Spring Cloud
# IOC理论推导
原先的开发模式:
-
先写一个UserDao接口
1 2 3
public interface UserDao { public void getUser(); }
-
再去写Dao的实现类
1 2 3 4 5 6
public class UserDaoImpl implements UserDao { @Override public void getUser() { System.out.println("获取用户数据"); } }
-
然后去写UserService的接口
1 2 3
public interface UserService { public void getUser(); }
-
最后写Service的实现类
1 2 3 4 5 6 7 8
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); @Override public void getUser() { userDao.getUser(); } }
-
测试一下
1 2 3 4 5 6
@Test public void test(){ //用户实际调用的是业务层,dao层他们并不需要接触! UserService service = new UserServiceImpl(); service.getUser(); }
如果需要更换Dao层的实现方法,每次都需要在UserServiceImpl
里手动修改new
的对象,这样做十分麻烦。
现在可以使用set接口实现
|
|
- 以前所有东西都是由程序去进行控制创建。
- 而现在是由我们自行控制创建对象,把主动权交给了调用者。程序不用去管怎么创建,怎么实现了,它只负责提供一个接口。
这种思想,从本质上解决了问题,我们程序员不再去管理对象的创建了,更多的去关注业务的实现。耦合性大大降低, 这也就是IOC的原型!
# IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
Spring的工作原理:
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
# 第一个Spring程序
-
添加Maven依赖
1 2 3 4 5 6 7 8
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> </dependencies>
-
在
pojo
包下创建实体类Hello
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 top.lbqaq.pojo; /** * @author luoboQAQ * @Date 2021/8/12 上午 10:49 */ public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
-
在
resources
目录下编写Spring的配置文件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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--bean就是java对象 , 由Spring创建和管理 原来是 类型 变量名 = new 类型(); Hello hello = new Hello(); 现在 id=变量名 class=new的对象 property是给变量赋的初值 --> <bean id="hello" class="top.lbqaq.pojo.Hello"> <!--引用另外一个bean , 不是用value 而是用 ref--> <property name="str" value="Spring"/> </bean> </beans>
-
进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import top.lbqaq.pojo.Hello; /** * @author luoboQAQ * @Date 2021/8/12 上午 10:53 */ public class MyTest { public static void main(String[] args) { //获取Spring的上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //我们的对象都在Spring中管理了,我们要使用直接从中取即可 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello); } }
现在Hello对象由Spring来创建和管理,这个过程就叫控制反转
-
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
-
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的。
IOC是一种编程思想,由主动的编程变成被动的接收。
# IOC创建对象方式
-
通过无参构造函数(默认)
-
通过有参构造函数,有三种方式
-
下标赋值
1 2 3
<bean id="user" class="top.lbqaq.pojo.User"> <constructor-arg index="0" value="小明"/> </bean>
-
用类型赋值(不推荐)
1 2 3
<bean id="user" class="top.lbqaq.pojo.User"> <constructor-arg type="java.lang.String" value="小明"/> </bean>
-
通过参数名创建
1 2 3
<bean id="user" class="top.lbqaq.pojo.User"> <constructor-arg name="name" value="小明"/> </bean>
-
在配置文件加载的时候,其中管理的对象都已经初始化了!
# Spring配置
# 别名(alias)
|
|
# Bean的配置
|
|
# import
一般用于团队开发,将多个配置文件合为一个配置文件applicationContext.xml
|
|
# 依赖注入
# 概念
- 依赖注入(Dependency Injection,DI)。
- 依赖 : 指Bean对象的创建依赖于容器,Bean对象的依赖资源。
- 注入 : 指Bean对象所依赖的资源,由容器来设置和装配。
# 构造器注入
见前面的IOC创建对象方式
# set注入【重点】
# 测试环境
|
|
|
|
# xml编写
|
|
具体用法见上面的代码就行了。
# 扩展方式注入
# p命名空间
-
导入命名空间
1
xmlns:p="http://www.springframework.org/schema/p"
-
在
bean
标签里直接使用1
<bean id="user" class="top.lbqaq.pojo.User" p:age="18" p:name="小明"/>
相当于set注入
# c命名空间
-
导入命名空间
1
xmlns:c="http://www.springframework.org/schema/c"
-
在
bean
标签里直接使用1
<bean id="user2" class="top.lbqaq.pojo.User" c:age="20" c:name="小红"/>
相当于有参构造函数注入
# Bean的作用域
类别 | 说明 |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为ServletContext 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为WebSocket 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
-
单例模式(默认):每次获取的都是同一个对象
1
<bean id="user" class="top.lbqaq.pojo.User" p:age="18" p:name="小明" scope="singleton"/>
-
原型模式:每次get都是从容器中产生一个新对象
1
<bean id="user" class="top.lbqaq.pojo.User" p:age="18" p:name="小明" scope="prototype"/>
-
其余
request
、session
、application
、websocket
这些只在web开发中使用。
# Bean的自动装配
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
- 在xml中显式配置
- 在java中显式配置
- 隐式的bean发现机制和自动装配【重要】
# 环境搭建
|
|
|
|
|
|
# ByName自动装配
|
|
会自动在容器上下文中查找,和自己对象set方法后面的值相对应的beanid
# ByType自动装配
|
|
自动在容器上下文中查找,和自己对象属性类型相同的Bean
小结:
- byname要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- bytype的时候,需要保证bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
# 使用注解自动装配
# 准备工作
-
导入aop包
-
导入约束
1 2 3 4
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
-
配置注解支持
<context:annotation-config/>
# @Autowired
-
在属性或在set方法上使用。
-
使用Autowired可以不需要set方法。
-
@Autowired是按类型自动转配的。
-
@Autowired(required=false)
说明对象可以为null
# @Qualifier
-
如果环境比较复杂,自动装配无法通过一个
@Autowired
完成,可以通过@Qualifier(value="xxx")
来实现。 -
@Qualifier
相当于byName
|
|
# @Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
|
|
# 小结
@Autowired与@Resource异同:
-
@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
-
@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
-
@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
# 使用注解开发
使用注解开发需要导入aop的包,导入约束和配置注解支持
|
|
# Bean的实现
|
|
使用@Component
注解,相当于配置文件中的<bean id="user" class="当前注解的类"/>
注意需要在配置文件中指定要扫描的包<context:component-scan base-package="top.lbqaq.pojo"/>
# 属性注入
使用@Value
注解,相当于配置文件中的<property name="name" value="小明"/>
可以在声明上加,也可以在set方法上加
|
|
|
|
# 衍生注解
对于@Component
注解,有三个衍生注解,分别对应MVC的三层。
- Dao层:
@Repository
- Service层:
@Service
- Web层:
@Controller
这四个注解的作用都是代表将某个类注册到Spring中,装配Bean
# 自动装配注解
@Autowired
:按类型自动转配@Qualifier(value="xxx")
:按name自动装配@Resource
:Java提供的,先name再type,比较复杂
详细的解释可以看上面
# 作用域
@Scope
singleton
:单例模式prototype
:多例模式
|
|
# 小结
XML与注解比较
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
推荐用法
- xml管理Bean
- 注解完成属性注入
# 使用Java类进行配置
实体类
|
|
配置类
|
|
这里的@Configuration
表明这是Spring的配置
@Bean
是用来注册bean,这里的返回值就Bean的类型,方法名就是bean的id
测试
|
|
# 代理模式
AOP的底层机制就是动态代理。
# 静态代理
角色分析
- 抽象角色:一般使用接口或者抽象类来实现
- 真实角色:被代理的角色
- 代理角色:代理真实角色。代理真实角色后 , 一般会做一些附属的操作。
- 客户:使用代理角色来进行一些操作。
举例:
Rent.java
即抽象角色
|
|
Host.java
即真实角色
|
|
Proxy.java
即代理角色
|
|
Client.java
即客户
|
|
分析:
在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式。
静态代理的好处:
- 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
- 公共的业务由代理来完成,实现了业务的分工。
- 公共业务发生扩展时变得更加集中和方便。
缺点:
- 类多了,多了代理类,工作量变大了,开发效率降低。
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想。
# 动态代理
-
动态代理的角色和静态代理的一样。
-
动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
-
动态代理分为两类:一类是基于接口动态代理,一类是基于类的动态代理
-
- 基于接口的动态代理—-JDK动态代理
- 基于类的动态代理–cglib
- 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
- 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!
JDK的动态代理需要了解两个类
核心 : InvocationHandler
、Proxy
代码实现:
|
|
|
|
|
|
|
|
动态代理代理接口
通用的代码
|
|
# AOP
# 什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗点说,就是在不改变原有代码的情况下去增加新的功能。
# AOP在Spring中
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ….
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
# 使用Spring实现AOP
导入依赖包
|
|
# 方式一:使用Spring的方法
业务接口和实现类
|
|
|
|
写两个增强类
|
|
|
|
配置xml,这里需要引入aop命名空间
|
|
execution表达式解析:
- execution(修饰符 返回值 包名.类名/接口名.方法名(参数列表))
- 修饰符可以忽略
- (..)可以代表所有参数,(*)代表一个参数,(*,String)代表第一个参数为任何值,第二个参数为String类型
最后测试
|
|
# 方法二:使用自定义类来实现
创建自定义类
|
|
配置xml
|
|
# 方式三:使用注解来实现AOP
-
在类上标注注解
@Aspect
,如果没有该注解,就在maven配置里把<scope>runtime</scope>
去掉 -
在方法前加上
@Before()
或@After()
注解,并在括号中填入表达式1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
//使用注解实现AOP @Aspect public class AnnotationPointCut { @Before("execution(* top.lbqaq.service.UserServiceImpl.*(..))") public void before(){ System.out.println("=====方法执行前====="); } @After("execution(* top.lbqaq.service.UserServiceImpl.*(..))") public void after(){ System.out.println("=====方法执行后====="); } //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点 @Around("execution(* top.lbqaq.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); //执行方法 Object proceed = jp.proceed(); System.out.println("环绕后"); } }
-
在Spring配置文件注册Bean并开启注解支持
1 2 3
<bean id="annotationPointCut" class="top.lbqaq.diy.AnnotationPointCut"/> <!--开启注解支持--> <aop:aspectj-autoproxy/>
# 整合MyBatis
首先要导入相关jar包
|
|
# 回顾MyBatis
-
编写实体类
1 2 3 4 5 6 7
public class User { private int id; private String name; private String pwd; //setter,getter //toString,构造 }
-
编写Mybatis配置文件
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
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--核心配置文件--> <configuration> <typeAliases> <package name="top.lbqaq.pojo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--每一个mapper都需要注册--> <mappers> <mapper class="top.lbqaq.mapper.UserMapper"/> </mappers> </configuration>
-
编写接口
1 2 3
public interface UserMapper { List<User> selectUser(); }
-
编写接口对应的xml文件
1 2 3 4 5 6 7 8 9 10 11
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="top.lbqaq.mapper.UserMapper"> <select id="selectUser" resultType="User"> select * from user </select> </mapper>
-
解决Maven静态资源过滤问题
1 2 3 4 5 6 7 8 9 10 11 12
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
-
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
public class MyTest { @Test public void test() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> userList = mapper.selectUser(); for (User user: userList){ System.out.println(user); } sqlSession.close(); } }
# MyBatis-Spring
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。官方文档
# 整合实现一
-
引入Spring配置文件
spring-dao.xml
1 2 3 4
<?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">
-
配置数据源
1 2 3 4 5 6 7 8 9
<!--DataSource:使用Spring的数据源替换MyBatis的配置 这里使用Spring提供的JDBC --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean>
-
配置
SqlSessionFactory
,关联MyBatis1 2 3 4 5 6
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--绑定Mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:top/lbqaq/mapper/*.xml"/> </bean>
-
注册sqlSessionTemplate,关联sqlSessionFactory
1 2 3 4
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--使用构造器注入,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
-
增加Mapper接口的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class UserMapperImpl implements UserMapper{ //原来我们的所有操作,都使用sqlSession来执行;现在都使用SqlSessionTemplate private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }
-
将实现类注册到Spring中
1 2 3
<bean id="userMapper" class="top.lbqaq.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
-
测试
1 2 3 4 5 6 7 8
@Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); for (User user : userMapper.selectUser()) { System.out.println(user); } }
此时再去看MyBatis的配置文件,里面大部分配置都被Spring整合了
|
|
这里建议将typeAliases
和settings
留在MyBatis的配置文件中。
以后可以将Spring配置文件拆开,spring-dao.xml
用来处理数据库相关代码,applicationContext.xml
作为总配置文件。
|
|
# 整合方式二
使用SqlSessionDaoSupport
-
修改实现类,继承
SqlSessionDaoSupport
1 2 3 4 5 6
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{ @Override public List<User> selectUser() { return getSqlSession().getMapper(UserMapper.class).selectUser(); } }
-
配置Bean
1 2 3
<bean id="userMapper2" class="top.lbqaq.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
-
测试
1 2 3 4 5 6 7 8
@Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper = context.getBean("userMapper2", UserMapper.class); for (User user : userMapper.selectUser()) { System.out.println(user); } }
使用这种方法,就不需要上面的第4步了,步骤就更加简洁了。
# 声明式事务
spring中的事务:
- 编程式事务:在代码中进行事务的管理
- 声明式事务:AOP
一般我们都采用声明式事务,将事务管理作为横切关注点,通过aop方法模块化。
使用方法:
-
引入头文件约束
tx
1 2 3 4
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 3 4
<!--配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
-
配置事务通知
1 2 3 4 5 6 7 8 9 10 11 12
<!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给那些方法配置事务--> <tx:attributes> <!--配置事务的传播特性 propagation--> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
Spring的事务的传播特性一共有7种:
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
一般第一种就够用了。
-
配置AOP
注意要导入头文件依赖
aop
,详细的内容在上面,或者IDEA也会自动补全1 2 3 4 5
<!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* top.lbqaq.mapper.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>