Spring
容器
一种轻量级java框架,用于整合其他框架
-
核心:控制反转IOC和面向切面编程AOP
-
主要作用:解耦合
-
特点:
-
轻量级
由20多个很小的jar包组成
对代码无污染,不需要按照特定结构构建项目
-
面向接口
项目可扩展性,可维护性高
接口不关心实现类类型,接口指向实现类,切换实现类即可切换整个功能
-
AOP 面向前面编程
将公共的通用的重复的代码单独开发,在需要时返回
底层原理是动态代理
-
整合其他框架
是其他第三方框架更容易使用
-
IOC 控制反转
是一种设计模式
将对象的创建和依赖关系的管理从代码中转移到容器spring中
依赖注入DI实现IOC
容器 spring 负责管理对象之间的依赖关系。
spring通过XML配置文件或注解来描述对象之间的依赖关系,并负责创建和管理这些对象,
开发者只需在配置文件或代码中声明对象及其依赖关系,Spring容器会根据配置来创建对象并完成依赖注入
这样,目标对象只需关注自身的业务逻辑,而不需要关心如何获取和创建所需的依赖对象
反转
由容器spring创建对象和依赖注入,容器掌握控制权
正转:程序员创建对象和依赖注入,程序员掌握控制权
三层架构项目
使用三层架构进行用户数据插入
非spring接管:界面层,业务逻辑层,数据访问层(模拟)
-
实体类 org.example.pojo
Users (javabean)
{id,name,age}
-
数据访问层 **org.example.dao ** 或 org.example.mapper
- UsersMapper.java (接口)
public interface usersMapper { int insert(users u); }
- UsersMapperImpl.java (实现类)
public class usersMapperImpl implements usersMapper { @Override public int insert(users u) { //模拟实现数据库插入的具体做法(sql) System.out.println(u.getUname()+"创建成功"); return 1; } }
原来需要写与UsersMapper同名映射文件UsersMapper.xml,并放到target同一地方
但是这里还没讲到spring整合mybaitis
所以模拟的建了个UsersMapperImpl.java
-
业务逻辑层 org.example.service
- UsersService.java (接口)
public interface usersService { //增加用户 int insert(users u); }
- UsersServiceImpl.java (实现类)
public class usersServiceImpl implements usersService { //在所有业务逻辑层都有 数据访问层对象 private usersMapper usersMapper; //=new usersMapperImpl()程序做 //加setter()交给spring做 public void setUsersMapper(usersMapper usersMapper) { this.usersMapper = usersMapper; } @Override public int insert(users u) { //...可以进行复杂业务 return usersMapper.insert(u); } }
-
界面层 org.example.controller
UsersController.java
public class usersController { //所有界面层都有业务逻辑层对象 private usersService usersService; //new usersServiceImpl();程序员做 //加setter()交给spring接管 public void setUsersService(usersService usersService) { this.usersService = usersService; } //界面层功能实现,对外提供访问功能 public int insert(users u){ return usersService.insert(u); } }
XML方式实现ioc
-
在pom.xml中引入spring依赖,并配置资源目录的路径,文件类型和名称
<!--spring--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.11</version> </dependency>
- 标签:这个标签是 Maven POM 文件的顶级元素,用于指定构建相关的信息和配置。
- 标签:这个标签定义了一个资源目录,可以包含多个 子标签,用于指定要包含的资源文件类型和名称模式。
- 标签:这个标签用于指定资源目录的路径
- 标签:这个标签指定了要包含的文件类型和名称模式
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
- 在main是resources中创建spring的核心配置文件applicationContext.xml
-
在applicationContext.xml里创建学生对象spring创建
<!--创建学生对象 = student s = new student();--> <bean id="s" class="org.example.pojo.student"></bean>
id:创建的对象名称;
class:创建的对象类型,底层通过反射构建对象
程序员自己创建:
-
创建容器对象并启动
ApplicationContext 容器对象名
=new ClassPathXmlApplicationContext("核心配置文件")
容器启动时,对象自动被创建
//创建spring容器对象并启动 ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
-
从容器spring中取对象
容器对象名.getBean("对象名")
取出的对象是object类型
//从spring中取对象 student s= (student) ac.getBean("s"); System.out.println(s);
-
给创建的对象赋值
引用类型可以用
autowire
来自动检索,参数:-
byName:相当于注解里的 @Autowired+@Qualifier(“对象名称”)
-
byType:相当于注解里的 @Autowired
-
setter注入
value:简单类型注入
ref:引用类型注入
必须提供无参构造和setter方法才能使用被spring接管
<!--创建爱好对象--> <bean id="h" class="org.example.pojo.hobby"> <property name="food" value="apple" ></property> <property name="color" value="orange" /> </bean> <!--创建学生对象--> <bean id="s" class="org.example.pojo.student"> <property name="name" value="aaa"/> <property name="age" value="22"/> <property name="hobby" ref="h"/> </bean>
在中如果是
-
构造方法注入
- 使用构造方法参数名称注入值
- 使用构造方法参数下标注入值
- 使用默认构造方法参数顺序注入值
<!--创建学生对象--> <bean id="s1" class="org.example.pojo.student"> <constructor-arg name="name" value="aaa"/> <!--使用构造方法名称注入--> <constructor-arg index="1" value="21"/> <!--使用构造方法参数索引注入--> <constructor-arg ref="h"/> <!--使用构造方法默认顺序注入--> </bean>
<!--创建爱好对象--> <bean id="h" class="org.example.pojo.hobby"> <property name="food" value="apple" ></property> <property name="color" value="orange" /> </bean>
-
-
测试
@Test public void test_student_constructor() { ApplicationContext ac=new ClassPathXmlApplicationContext("student_constructor/applicationContext.xml"); Student s= (Student) ac.getBean("s"); System.out.println(s); }
基于xml三层架构项目
-
在pom.xml中导入依赖
<!--添加spring依赖--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.11</version> </dependency>
-
在resources中创建spring核心配置文件来接管
-
自下而上创建(实现类)对象接口不行
-
创建的对象若有成员变量必须提供setter和无参构造(自动创建)
<!--创建数据访问层对象--> <bean id="UMapper" class="org.example.dao.usersMapperImpl"/> <!--创建业务逻辑层对象--> <bean id="UService" class="org.example.service.usersServiceImpl"> <property name="usersMapper" ref="UMapper"/> </bean> <!--创建界面层对象--> <bean id="UController" class="org.example.controller.usersController"> <property name="usersService" ref="UService"/> </bean>
-
-
给对应被spring创建的对象添加setter方法
dao层没有创建变量不需要setter()
-
usersServiceImpl
//spring接管:(必须有setter 和 无参构造/默认有/ ) public void setUsersMapper(org.example.dao.usersMapper usersMapper) { this.usersMapper = usersMapper; }
-
usersController
//想要被spring接管必须提供 setter() 和 无参构造/默认有/ public void setUsersService(org.example.service.usersService usersService) { this.usersService = usersService; }
-
-
测试
//启动容器 ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); //取出对象(调用controller,其他自动嵌套调用) usersController uController = (usersController) ac.getBean("UController"); //测试功能(增加) int a=uController.insert(new Users(10001,"aaa",22));
注解方式实现ioc
也称DI,是基于ioc思想的一种实现技术
创建哪个对象在哪个对象头上放对应注解,
在核心配置文件中加入包扫描就ok
-
创建对象注解
-
@Component
:可以创建任何类型对象创建的对象名默认为类名的驼峰命名法.
也可以指定对象名称@Component(“自定义名称”)
-
@Controller
:专门用来创建控制器对象(servlet),接收用户请求并且返回给客户端//界面层 @Controller public class UsersController { //所有界面层都有业务逻辑层对象 @Autowired private UsersService usersService;//程序员做=new UsersServiceImpl(); //界面层功能实现,对外提供访问功能 public int insert(Users u){ return usersService.insert(u); } }
-
@Service
:专门用来创建业务逻辑层对象,负责向下访问数据访问层,处理结果返回给界面层//业务逻辑层实现类 @Service public class UsersServiceImpl implements UsersService { //在所有业务逻辑层都有 数据访问层对象 @Autowired private UsersMapper usersMapper;//程序员做=new UsersMapperImpl(); @Override public int insert(Users u) { //...可以进行复杂业务 return usersMapper.insert(u); } }
-
@Repository
:创建数据访问层对象,负责数据库中增删改查操作//数据访问层实现类 @Repository public class UsersMapperImpl implements UsersMapper { @Override public int insert(Users u) { //模拟实现数据库插入的具体做法(sql) System.out.println(u.getUname()+"创建成功"); return 1; } }
-
-
普通类型依赖注入注解
@Value
:给简单类型(8钟基本类型 + String)注入 -
引用类型依赖注入注解
-
@Autowired
:使用类型注入值,从整个Bean工厂搜索同源类型对象进行注入
同源类型:
-
被注入的类型和注入的类型是相同类型
-
被注入的类型(父)和注入的类型(子)是父子类
按类型注入有多个可注入对象(父子类),
此时按名称进行二次筛选选中与被注入对象名称相同的对象注入
-
被注入的类型(接口)和注入的类型(接口实现类)是接口和接口实现类类型
-
-
@Autowired+@Qualifier("对象名称")
:使用名称注入值,从整个Bean工厂搜索同源类型对象进行注入
-
Studen类
@Component("stu") //交给spring创建对象,容器启动时创建.指定对象名字stu public class Student { @Value("小明") //普通类型赋值 private String name; @Value("21") private int age; //@Autowired //引用类型赋值 自动扫描包下被创建的对应类型(Hobby) //private Hobby hobby; @Autowired @Qualifier("ho") //引用类型赋值 自动扫描包下被创建的对应名称("ho") private Hobby hobby; ... }
Hobby类
@Component("ho") //注解实现让spring创建对象,对象名ho public class Hobby { @Value("cookie") private String food; @Value("red") private String color; ... }
spring核心配置文件
<!--添加包扫描 创建容器时自动启动,调用无参构造--> <context:component-scan base-package="org.example.pojo.comment"></context:component-scan>
扫描"org.example.pojo.comment",
Student和Hobby类都在此包下
测试
…
基于注解三层架构项目
-
三层
pojo:User{uid,uname,uage}
-
dao
UsersMapper
UsersMapperImpl
//数据访问层实现类 @Repository public class UsersMapperImpl implements UsersMapper { @Override public int insert(Users u) { //模拟实现数据库插入的具体做法(sql) System.out.println(u.getUname()+"创建成功"); return 1; } }
-
service
UsersService
UsersServiceImpl
//业务逻辑层实现类 @Service public class UsersServiceImpl implements UsersService { //在所有业务逻辑层都有 数据访问层对象 @Autowired private UsersMapper usersMapper;//程序员做=new UsersMapperImpl(); @Override public int insert(Users u) { //...可以进行复杂业务 return usersMapper.insert(u); } }
-
controller
UsersController
//界面层 @Controller public class UsersController { //所有界面层都有业务逻辑层对象 @Autowired private UsersService usersService;//程序员做=new UsersServiceImpl(); //界面层功能实现,对外提供访问功能 public int insert(Users u){ return usersService.insert(u); } }
-
-
applicationContext中添加包扫描
<!--单个包扫描--> <context:component-scan base-package="org.example.controller"/> <context:component-scan base-package="org.example.service"/> <context:component-scan base-package="org.example.dao"/>
<!--扫根包,不推荐--> <context:component-scan base-package="org.example"/>
-
测试
@Test public void test_insert(){ //创建并启动容器 ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); //取出userscontroller对象 UsersController usersController = (UsersController) ac.getBean("usersController"); //插入 usersController.insert(new Users(10001,"user1",22)); }
多人开发问题
配置文件拆分
因为可能有多个pojo类,多人操作可能乱
-
按层拆:
-
controller
-
service
-
dao/mapper
-
-
按功能拆
-
users
-
books
-
配置文件整合
-
单个导入
在resource下创建total.xml把所拆分的xml进来
<import resource="applicationContext_mapper.xml"/> <import resource="applicationContext_service.xml"/> <import resource="applicationContext_controller.xml"/>
-
批量导入
<import resource="applicationContext_*.xml"/>
AOP 面向切面编程
是一种编程范式
用于将横切关注点从核心业务逻辑中分离出来
并以模块化的方式进行管理
Spring AOP通过代理机制实现切面的织入,
可以在运行时动态地将切面应用到目标对象中。
开发者可以使用注解或XML配置文件来定义切面和切点,以及指定通知的类型和执行顺序
切面
公共的 ,通用的 ,重复的功能
面向切面就是把切面编程提取出来,单独开发.实现业务代码,切面代码分离
在需要调用的方法中通过动态代理的方式织入
如日志记录、性能统计、安全控制、事务处理…
连接点
目标方法
目标方法中要实现目标方法的功能和切面功能
代理的方法
切入点
切入点(pointcut)指定切入位置
多个连接点构成切入点
切入点可以是一个目标方法,可以是一个类中所有方法,可以是某个包下所有类中方法
可用@Before(value=“execution(修饰符 返回类型 包.方法(参数))”)来切入
@Aspect public class MyAspect{ @Before(value="execution(* *...s.SomeService.*(..))) public void myAspect(){ sout("前置日志") } }
把myAspect方法切入到返回值任意,任意包中s.SomeService包下的任意方法中
通知
通知(advice)指定切入时机
- 时机: 前/后/出错时/环绕
目标对象
被操作者
bookserviceimpl,produceserviceimpl…
AOP框架项目
业务: 图书购买
切面: 事务
-
业务,切面紧耦合
public class BookServiceImpl { public void buy() { try { System.out.println("事务开启..."); System.out.println("图书购买业务功能..."); System.out.println("事务提交..."); } catch (Exception e) {System.out.println("事务回滚..."); } } }
-
使用子类代理拆分业务,切面
-
BookServiceImpl
public class BookServiceImpl { //在父类中只有图书馆里业务 public void buy() {System.out.println("图书购买...");} }
-
SubBookServiceImpl (子类)
public class SubBookServiceImpl extends BookServiceImpl{ @Override public void buy() { try { //事务开启切面 System.out.println("事务开启..."); //主业务实现 super.buy(); //事务提交切面 System.out.println("事务提交..."); } catch (Exception e) {System.out.println("事务回滚...");} } }
-
-
使用静态代理拆分业务,切面
业务和业务接口已拆分;切面和业务紧耦合
-
service
public interface Service { //规定业务功能 void buy(); }
-
…serviceimpl(不同对象service实现类)
public class BookServiceImpl implements Service { @Override public void buy() {System.out.println("图书购买...");} }
public class ProductServiceImpl implements Service{ @Override public void buy() {System.out.println("商品购买...");} }
-
agent
public class Agent implements Service{ //设计成员变量的类型为接口,可以灵活切换目标对象 public Service target; //服务对象多变 //构造器注入目标对象 public Agent(Service target) {this.target = target;} @Override public void buy() { try { //切面 System.out.println("事务开启..."); //业务 target.buy(); //切面 System.out.println("事务结束..."); } catch (Exception e) {System.out.println("事务回滚..."); } } }
测试
public class Test03 { @Test public void test03(){ Service agent = new Agent(new BookServiceImpl()); agent.buy(); System.out.println(""); Service agent1 = new Agent(new ProductServiceImpl()); agent1.buy(); } }
-
-
使用静态代理拆分业务和业务接口,切面和切面接口
- 设计思路
-
项目结构
-
AOP切面接口及实现类
public interface AOP { default void before(){}; default void after(){}; default void exception(){}; }
//日志实现切面接口 public class LogAop implements AOP{ @Override public void before() { System.out.println("前置日志输出..."); } }
//事务实现aop public class TransAop implements AOP{ @Override public void before() {System.out.println("事务开启...");} @Override public void after() {System.out.println("事务提交...");} @Override public void exception() {System.out.println("事务回滚...");} }
-
service接口与实现类同上…
-
agent
public class Agent implements Service { //传入目标对象,切面对象 Service target; AOP aop; //构造函数传入目标对象,切面对象 public Agent(Service target,AOP aop) { this.target = target; this.aop = aop; } @Override public void buy() { try { //切面 aop.before(); //业务 target.buy(); //切面 aop.after(); } catch (Exception e) { //切面 aop.exception(); } } }
-
测试
可以通过agent构造方法传入不同实现类来实现切面和业务灵活变换
也可以通过嵌套来实现多切面调用
极其灵活!!!
嵌套传参,实现多切面调用
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
-
使用动态代理对上个版本优化
-
项目结构
-
aop,service同上
-
ProxyFactory
代理过程,通过getProxy()方法创建代理
getProxy()方法通过newProxyInstance(ClassLoader l,Class<?>[] i,InvocationHandler h)方法创建代理
参数InvocationHandler h通过创建InvocationHandler实现类并重写invoke(Object proxy, Method method, Object[] args) throws Throwable来实现代理要做的事
public class ProxyFactory { public static Object getProxy(Object target, AOP aop) { return Proxy.newProxyInstance( //类加载器 target.getClass().getClassLoader(), //目标类接口 target.getClass().getInterfaces(), //InvocationHandler代理功能实现 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { //切面 aop.before(); //业务 result = method.invoke(target, args); //切面 aop.after(); } catch (IllegalAccessException e) { //切面 aop.exception(); } return result; } } ); } }
-
测试
-
Spring支持的aop实现
spring原生常用通知:
-
Before
通知在目标方法被调用前调用
-
After
通知在目标方法被调用后调用
-
Throws
通知在目标方法抛出异常时调用
-
Around
通知拦截目标对象方法时调用
案例
-
Service接口
package org.example.service; //业务接口 public interface Service { //规定业务功能 void buy(String name); //新增有参有返回值方法 default String show(String name) {return null;}; }
-
BookServiceImpl实现接口
package org.example.service; public class BookServiceImpl implements Service { @Override public void buy(String name) { System.out.println("图书购买"+name); } @Override public String show(String name) { System.out.println("购买图书"+name); return "..."; } }
-
TransAop
package org.example.aop; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; //事务实现aop的MethodBeforeAdvice接口 //在目标方法被调用前调用 public class TransAop implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { //打印当前时间 SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd"); System.out.println("\n[系统日志]"+sf.format(new Date())+" "+method.getName()+ Arrays.toString(args)); } }
-
applicationContext.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"> <!--创建业务/目标对象--> <bean id="bookserviceTarget" class="org.example.service.BookServiceImpl"/> <!--创建切面对象--> <bean id="transaop" class="org.example.aop.TransAop"/> <!--绑定业务和切面--> <bean id="bookservice" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--配置业务接口--> <property name="interfaces" value="org.example.service.Service"/> <!--配置切面--> <property name="interceptorNames"> <list> <value>transaop</value> </list> </property> <!--织入--> <property name="target" ref="bookserviceTarget"/> </bean> </beans>
bookservice属于org.springframework.aop.framework.ProxyFactoryBean类
ProxyFactoryBean是底层封装好的类
需要配置业务接口,配置切面和织入
- 配置业务接口
<property name="interfaces" value="实际业务的接口">
- 配置切面
<property name="interceptorNames"> <list> <value>实际切面类1</value> <value>实际切面类2</value> </list> </property>
- 织入
<property name="target" ref="实际业务类"/>
-
MyTest测试
package org.example.test; import org.example.service.BookServiceImpl; import org.example.service.Service; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test() { ApplicationContext ac=new ClassPathXmlApplicationContext ("applicationContext.xml"); Service proxy= (Service) ac.getBean("bookservice"); proxy.buy("朝花夕拾"); } }
结果:
[系统日志]2024-03-06 buy[朝花夕拾]
图书购买朝花夕拾proxy是jdk动态代理类型
AspectJ框架
是一个优秀的面向切面的框架,提供了大量切面实现
切入点表达式
execution( 访问权限 方法返回值 方法名(参数) 异常类型)
简化后execution( 方法返回值 方法声明(参数) )
-
符号
1* :通配符,表任意字符
2… :表任意参数,或者本路径及其所有子路径
- execution(public * *(…)) :任意公共方法
- execution(* set*(…)) :任何一个以set开头的方法
- execution(* org.example.service.* .*(…)) :任意返回值类型,在org.example.service包下的任意类中任意方法的任意参数
- execution(* org.example…* .*(…)) :任意返回值类型,在org.example包及其子包下的任意类中任意方法的任意参数
- execution(* *…service. *. *(…)) :service前可以有任意子包
- execution(* *.service. *. *(…)) :service前只可以有一个子包
常用通知类型
前置环绕@Before
拦截目标方法,执行切面方法后再执行目标方法
在切面方法中不可以获得目标方法返回值,只能得到目标方法签名如访问权限,返回值,方法名,方法参数
切面规范
-
访问权限:public
-
方法返回值:void
-
方法名自定义
-
方法参数:空或JoinPoint类型
可以通过jp.getSignature获得目标方法签名
jp.getArgs()获得目标方法参数
@Before(value = "execution(public boolean org.example.demo02.BookServiceImpl.buy(String))") public void MyBefore(JoinPoint jp){ System.out.println("切面中前置通知功能开启..."); System.out.println("目标方法签名: "+jp.getSignature()); System.out.println("目标方法参数: "+ Arrays.toString(jp.getArgs())); }
-
使用@Before注解来声明切入时机和切入点
- 参数value:指定切入点表达式
创建步骤
-
创建业务/目标接口及实现类
Service接口
package org.example.demo01; //业务接口 public interface Service { //规定业务功能 boolean buy(String name); }
目标实现类BookServiceImpl
package org.example.demo01; //目标对象:业务功能具体实现 //业务和业务接口分离 public class BookServiceImpl implements Service { @Override public boolean buy(String name) { System.out.println("购买图书"+name); return true; } }
-
创建切面类,实现切面方法
切面MyAspect
package org.example.demo01; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; //切面类,包含各种切面方法 @Aspect //交给aspectj框架识别管理 public class MyAspect { //所有切面功能都是由切面方法实现的 //可以将各种切面方法定义在此切面类中 //public String show(String name) //前置切面方法,用注解指定切面方法前切位置 @Before(value = "execution(public boolean org.example.demo01.BookServiceImpl.buy(String))") public void MyBefore(){ System.out.println("切面中前置通知功能开启..."); } }
@Before的value指定在切入点前执行
-
在applicationContext.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" 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/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--创建业务对象--> <bean id="bookservice" class="org.example.demo01.BookServiceImpl"/> <!--创建切面对象--> <bean id="myaspect" class="org.example.demo01.MyAspect"/> <!--绑定--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
< aop:aspectj-autoproxy>< /aop:aspectj-autoproxy>
简简单单实现切面和具体业务的绑定
-
测试
@Test public void test(){ ApplicationContext ac=new ClassPathXmlApplicationContext("demo01/applicationContext.xml"); //取出代理对象 Service service =(Service) ac.getBean("bookservice"); service.buy("king"); }
< aop:aspectj-autoproxy>< /aop:aspectj-autoproxy>
默认jdk动态代理,代理对象用接口接口可指向所有实现类
< aop:aspectj-autoproxy proxy-target-class=“true”>< /aop:aspectj-autoproxy>
设置为cglib子类代理,代理对象可用接口和实现类
- 最好用接口
-
用注解绑定
-
在目标实现类上加@Service 或@Conponent
-
在切面类上加@Conponent
-
applicationContext.xml
<!--基于注解访问要包扫描--> <context:component-scan base-package="org.example.demo02"></context:component-scan> <!--绑定--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
测试
@Test public void test02(){ ApplicationContext ac=new ClassPathXmlApplicationContext("demo02/applicationContext.xml"); //取出代理对象 org.example.demo02.Service service = (org.example.demo02.Service) ac.getBean("bookServiceImpl"); service.buy("king"); }
-
后置环绕@AfterReturning
在目标方法执行后再执行切面方法
可以接收目标方法返回值,可改变引用类型返回值
切面规范
-
访问权限:public
-
方法返回值:void
-
方法名自定义
-
方法参数:目标方法返回值,若目标方法无返回值则无参
如果目标方法返回值是8种基本类型或String ==> 后置环绕不可改变返回值
如果目标方法返回值是引用类型 ==> 后置环绕可改变返回值
-
使用@AfterRturning注解表明后置通知
- 参数value:指定切入点表达式
- 参数returning:指定目标方法返回值名称,名称必须与切面方法参数名称一致
创建步骤
-
接口
public interface Service { //规定业务功能 Book buy(String name,int price); }
-
实现类
@org.springframework.stereotype.Service public class BookServiceImpl implements Service { @Override public Book buy(String name,int price) { System.out.println("购买图书"+name); Book book=new Book(name,price); return book; } }
-
接口
//切面类,包含各种切面方法 @Component //交给spring容器创建对象 @Aspect //交给aspectj框架识别管理 public class MyAspect { //所有切面功能都是由切面方法实现的 //可以将各种切面方法定义在此切面类中 @AfterReturning(value = "execution( * org.example.demo03.BookServiceImpl.buy(..))",returning = "arg") public void MyBefore(Object arg){ System.out.println("切面中后置通知功能开启..."); if(arg!=null){ if(arg instanceof Book){ Book book=(Book) arg; book.setName("《后来的后来》"); } } } }
-
applicationContext.xml
<!--基于注解访问要包扫描--> <context:component-scan base-package="org.example.demo03"></context:component-scan> <!--绑定--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
测试
@Test public void test03(){ ApplicationContext ac=new ClassPathXmlApplicationContext("demo03/applicationContext.xml"); //代理对象 Service service=(Service)ac.getBean("bookServiceImpl"); Book returning= service.buy("《后来》",100); System.out.println(returning); }
购买图书《后来》
切面中后置通知功能开启…
Book{name=‘《后来的后来》’, price=100}
环绕通知@Around
拦截目标方法,在目标方法前后增强功能的通知
以目标方法为参数,返回值就是目标方法返回值
也就是说可轻易改变目标方法返回值
切面规范
- 访问权限public
- 返回值就是目标方法返回值
- 方法名称自定义
- 参数为目标方法
- 回避异常
- 使用@Around注解声明是环绕通知
- 参数value:指定切入点表达式
创建步骤
-
接口
public interface Service { //规定业务功能 Book buy(String name,int price); }
-
实现类
@org.springframework.stereotype.Service public class BookServiceImpl implements Service { @Override public Book buy(String name,int price) { System.out.println("购买图书"+name); Book book=new Book(name,price); return book; } }
-
切面
//切面类,包含各种切面方法 @Component //交给spring容器创建对象 @Aspect //交给aspectj框架识别管理 public class MyAspect { //所有切面功能都是由切面方法实现的 //可以将各种切面方法定义在此切面类中 @Around(value = "execution( * org.example.demo04.BookServiceImpl.buy(..))") public Object MyBefore(ProceedingJoinPoint pjp) throws Throwable //回避异常 { //前切功能实现 System.out.println("环绕通知前置功能开启..."); //目标方法调用 Book book= (Book) pjp.proceed(pjp.getArgs()); book.setPrice(666); //后切功能实现 System.out.println("环绕通知后置功能开启..."); return book; } }
-
applicationContext.xml
<!--基于注解访问要包扫描--> <context:component-scan base-package="org.example.demo04"></context:component-scan> <!--绑定--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
测试
@Test public void test04(){ ApplicationContext ac=new ClassPathXmlApplicationContext("demo04/applicationContext.xml"); Service service = (Service) ac.getBean("bookServiceImpl"); Book returning= service.buy("《环绕》",233); System.out.println(returning); }
目标方法返回值被更改
最终通知@After
无论目标方法是否正常执行,最终代码都会被执行
善后工作,类似资源释放
切面规范
-
访问权限:public
-
方法返回值:void
-
方法名自定义
-
方法参数:空或JoinPoint类型
-
使用@After注解表明是最终通知
参数value:指定切入点表达式
创建步骤
-
接口
public interface Service { //规定业务功能 Book buy(String name,int price); }
-
实现类
@org.springframework.stereotype.Service public class BookServiceImpl implements Service { @Override public Book buy(String name,int price) { System.out.println("购买图书"+name); Book book=new Book(name,price); System.out.println(1/0); return book; } }
增加除0异常
-
切面
@After(value = "execution( * org.example.demo05.BookServiceImpl.buy(..))") public void MyBefore() { System.out.println("最终通知功能开启..."); }
-
applicationContext.xml
<!--基于注解访问要包扫描--> <context:component-scan base-package="org.example.demo05"></context:component-scan> <!--绑定--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
测试
@Test public void test05(){ ApplicationContext ac=new ClassPathXmlApplicationContext("demo05/applicationContext.xml"); Service service = (Service) ac.getBean("bookServiceImpl"); Book returning= service.buy("《最终》",233); System.out.println(returning); }
即使有除零异常也正常执行
定义切入点@Pointcut
如果把前置,后置,环绕,最终都加上.最后顺序如下
如果多个切面切到同一个切入点,可以使用别名简化
使用@Pointcut注解创建一个空方法,此方法名称就是别名
@Pointcut(value = "execution( * org.example.demo06.BookServiceImpl.buy(..))")
public void MyCut(){}
创建空方法后,其他切面切入点只需要输入空方法名
@After(value = "MyCut()")
public void MyAfter()
{
System.out.println("最终通知功能开启...");
}
Spring集成MyBatis
SM整合步骤
-
建表
-
创项目
-
添加Mybatis-config.xml Mybatis核心配置文件
-
拷贝jdbc.properties属性文件到resources目录
jdbc.properties
通常用于存储与数据库连接相关的配置信息,包括数据库的URL,用户名,密码等jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///mydatabase jdbc.username=myusername jdbc.password=mypassword
-
添加测试类
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Spring_MyBatis</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring_MyBatis</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--aspectj-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--spring事务-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mysql驱动-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.2</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
<!--mybatis-spring集成-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--阿里的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<!--包括目录下的.properties和.xml文件都会被扫描到-->
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
添加模板
打开:设置–>文件和代码模板–> + -->添加Mybatis-config.xml模板
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--类型别名-->
<typeAliases>
<package name="org.example.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:///!!!数据库名"/>
<property name="username" value="root"/>
<property name="password" value="1234567890"/>
</dataSource>
</environment>
</environments>
<!--映射名称空间:为了将映射文件与对应的接口或类进行关联,以便 MyBatis 在运行时能够正确地找到和执行相应的 SQL。-->
<mappers>
<!--包扫描-->
<package name="org.example.mapper"/>
</mappers>
</configuration>
添加XXXMapper.xml模板
<!--多条件查询-->
<select id="selectBySomething" resultType="s">
<!--通过name,和manager查询-->
select * from s where name like #{name} and manager=#{manager}
</select>
<!--插入-->
<insert id="add" useGeneratedKeys="true" keyColumn="id">
insert into s (name,manager,score,c,t)
values (#{name},#{manager},#{score},#{c},#{t})
</insert>
<!--修改全部-->
<update id="updateAll">
update s set
name=#{name},
manager=#{manager},
score=#{score},
c=#{c},
t=#{t}
where id=#{id}
</update>
<!--删除对应id的记录-->
<delete id="deleteById">
delete from s where id=#{id}
</delete>
Mybatis-config.xml
被注释部分被spring的applicationContext_mapper.xml接管
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--灰色部分被spring的applicationContext_mapper.xml接管-->
<!-- <!–读取属性文件中数据库配置–>-->
<!-- <properties resource="db.properties"></properties>-->
<!--设置日志输出语句,显示响应操作的sql语句-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- <!–类型别名–>-->
<!-- <typeAliases>-->
<!-- <package name="org.example.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:///ssm"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="1234567890"/>-->
<!-- </dataSource>-->
<!-- </environment>-->
<!-- </environments>-->
<!--映射名称空间:为了将映射文件与对应的接口或类进行关联,以便 MyBatis 在运行时能够正确地找到和执行相应的 SQL。-->
<!-- <mappers>-->
<!-- <!–包扫描–>-->
<!-- <package name="org.example.mapper"/>-->
<!-- </mappers>-->
</configuration>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///ssm
jdbc.username=root
jdbc.password=1234567890
applicationContext_mapper.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"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--读取属性文件jdbc.properties-->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--创建数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置SqlSessionFactoryBean类-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"></property><!--引用上方dataSource-->
<!--配置Mybatis核心配置文件-->
<!--spring的核心配置文件还不能完全接管mybatis核心配置文件功能-->
<property name="configLocation" value="mybatis-config.xml"></property>
<!--注册实体类别名-->
<property name="typeAliasesPackage" value="org.example.pojo"></property>
</bean>
<!--注册mapper.xml文件-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.example.mapper"></property>
</bean>
</beans>
applicationContext_service.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"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--sm是基于注解的开发,要添加包扫描-->
<context:component-scan base-package="org.example.service.impl"></context:component-scan>
<!--事务处理-->
</beans>
实体类
创建uid,uname,upass组成的javabean
Mapper
UMapper.xml
<!--名称空间-->
<mapper namespace="org.example.mapper.UMapper">
<insert id="insert" >
insert into users
values (#{uid},#{uname},#{upass})
</insert>
</mapper>
UMapper接口
public interface UMapper {
void insert(Users users);
}
Service
UService接口
public interface UService {
void insert(Users users);
}
UService实现类
@Service //交给spring创建对象
public class UServiceImpl implements UService {
//在spring的applicationContext_mapper.xml中已注册mapper.xml文件
//@Autowired交给spring获取UMapper.xml中的具体功能
@Autowired
UMapper uMapper;
@Override
public void insert(Users users) {
uMapper.insert(users);
}
}
测试
@Test
public void Utest() {
//创建容器并启动
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext_service.xml");
//取出UsersServiceImpl
UService uService = (UService) ac.getBean("UServiceImpl");
uService.insert(new Users(100,"diva","123"));
}
事务
所有事务必须添加到业务逻辑层
- 基于注解的事务管理
- 基于AspectJ的aop配置管理(声明式事务管理)
基于注解的事务管理
在ServiceImpl类上加注解@Transactional
类中所以方法都必须按照注解的设定
-
在application_service.xml中
-
添加事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--因为事务必须关联数据库,所以要配置数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
添加注解驱动
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
-
-
在AServiceImpl类上加注解@Transactional
@Transactional(propagation = Propagation.REQUIRED)
在内部制造错误(1/0)测试
@Transactional中参数noRollbackForClassName控制出错不回滚错误类型
如果noRollbackForClassName = “ArithmeticException”
那么算数错误ArithmeticException不回滚
-
测试
@Test public void Atest() { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_service.xml"); AService aService= (AService) ac.getBean("AServiceImpl"); aService.save(new Accounts(202,"lily","账户安全")); }
出错回滚,不执行
@Transactional
-
propagation:事务传播特性
-
noRollbackForClassName :指定发生何种异常不回滚,使用异常名称
noRollbackForClassName = “ArithmeticException”
-
noRollbackFor:指定发生何种异常不回滚,使用异常类型
noRollbackFor = ArithmeticException.class
-
rollbackForClassName:指定发生何种异常必须回滚,使用异常名称
-
rollbackFor:指定发生何种异常必须回滚,使用异常类型
-
timeout:链接超时设置
默认值-1永不超时
-
readOnly:允许增删改
默认是false,如果是查询操作必须是true
-
isolation:数据库的隔离级别
-
isolation.DEFAULT:使用数据库自己的隔离级别
-
未提交读(read uncommitted):允许脏读,可能读取到其他会话中未提交事务修改的数据
-
提交读(read committed):只能读已提交的数据.oracle
-
可重复读(repeated read):务可以多次读取相同的数据,并且在事务执行期间,其他事务对这些数据进行更新也不会影响到当前事务mysql
-
串行读(serializable):完全串行化读.每次读都要获得表级共享锁,读写相互阻塞
-
声明式事务
在配置文件中添加配置,整个项目都遵循设定
但若某个实力类增加了@Transactional,则注解优先级更高
要求项目中方法有命名规范
规定不同方法事务
<!--配置事务切面-->
<tx:advice id="myadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*select*" read-only="true"/><!--查找read-only="true"-->
<tx:method name="*find*" read-only="true"/>
<tx:method name="*search*" read-only="true"/>
<tx:method name="*get*" read-only="true"/>
<tx:method name="*insert*" propagation="REQUIRED"/><!--增加propagation="REQUIRED"-->
<tx:method name="*add*" propagation="REQUIRED"/>
<tx:method name="*save*" propagation="REQUIRED"/>
<tx:method name="*update*" propagation="REQUIRED"/>
<tx:method name="*change*" propagation="REQUIRED"/>
<tx:method name="*modify*" propagation="REQUIRED"/>
<tx:method name="*delete*" propagation="REQUIRED"/><!--删-->
<tx:method name="*remove*" propagation="REQUIRED"/>
<tx:method name="*drop*" propagation="REQUIRED"/>
<tx:method name="*clear*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
若增加<tx:method name=“save” propagation=“REQUIRED” no-rollback-for=“ArithmeticException”/>
no-rollback-for指定算数异常(ArithmeticException)不回滚
指定事务作用层并绑定
<!--绑定切面和切入点-->
<aop:config>
<aop:pointcut id="mycut" expression="execution(* org.example.service.*.*(..))"/>
<aop:advisor advice-ref="myadvice" pointcut-ref="mycut"></aop:advisor>
</aop:config>
测试
@Test
public void testTrans(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_trans.xml");
UService uService= (UService) ac.getBean("UServiceImpl");
uService.insert(new Users(102,"jack","123"));
}
事务管理器
用来生成相应技术的连接+执行语句对象
技术 连接对象 提交 回滚 jdbc Connection connection.commit() connection.rollback() Mybatis SqlSession sqlSession.commit() sqlSession.rollback() Hibernate Session session.commit() session.rollback() 如果使用Mybatis则必须使用DataSourceTransactionManager类完成
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--因为事务必须关联数据库,所以要配置数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
事务的传播特性
(propagation)多个事务之间的合并互斥等都可以通过设置事务的传播特性解决
- PROPAGATION_REQUIRED:必被包含事务(增删改查用)
- PROPAGATION_REQUIRED_NEW:自己新开事务,不管之前是否有事务
- PROPAGATION_SUPPORTS:支持事务,若外部方法有事务则支持事务,若没有不单开事务
- PROPAGATION_NEVER:不能运行在事务中(不能在内部),否则抛异常
- PROPAGATION_NOT_SUPPORTED:不支持事务,允许在非事务的环境
- PROPAGATION_MANDATORY:必须包在事务中,否则抛异常
- PROPAGATION_NESTED:嵌套事务
AServiceImpl
public class AServiceImpl implements AService{
@Autowired
AMapper aMapper;
@Override
public void save(Accounts accounts) {
aMapper.save(accounts);
System.out.println("账户增加成功");
System.out.println(1/0);
}
}
UServiceImpl
public class UServiceImpl implements UService{
@Autowired
UMapper uMapper;
@Autowired
AService aService;
@Override
public void insert(Users users) {
uMapper.insert(users);
System.out.println("用户增加成功");
//调用账户增加操作
aService.save(new Accounts(204,"jack","账户安全"));
}
}
UServiceImpl包含AServiceImpl
UServiceImpl | AServiceImpl(抛异常) | 结果 | |
---|---|---|---|
1 | 无事务 | 无事务 | u=ok,a=ok |
2 | 无事务 | REQUIRED | u=ok,a=no |
3 | REQUIRED | 无事务 | u=no,a=no |
4 | REQUIRED | REQUIRED | u=no,a=no |
5 | REQUIRED | NOT_SUPPORTED | u=no,a=ok |
6 | REQUIRED | SUPPORTED | u=no,a=no |
7 | REQUIRED | REQUIRED_NEW | u=no,a=no |
8 | REQUIRED | NEVER | u=no,a异常 |
| 回滚 |
| --------- | ---------- | ------------------- | --------------------- |
| jdbc | Connection | connection.commit() | connection.rollback() |
| Mybatis | SqlSession | sqlSession.commit() | sqlSession.rollback() |
| Hibernate | Session | session.commit() | session.rollback() |如果使用Mybatis则必须使用DataSourceTransactionManager类完成
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--因为事务必须关联数据库,所以要配置数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
事务的传播特性
(propagation)多个事务之间的合并互斥等都可以通过设置事务的传播特性解决
- PROPAGATION_REQUIRED:必被包含事务(增删改查用)
- PROPAGATION_REQUIRED_NEW:自己新开事务,不管之前是否有事务
- PROPAGATION_SUPPORTS:支持事务,若外部方法有事务则支持事务,若没有不单开事务
- PROPAGATION_NEVER:不能运行在事务中(不能在内部),否则抛异常
- PROPAGATION_NOT_SUPPORTED:不支持事务,允许在非事务的环境
- PROPAGATION_MANDATORY:必须包在事务中,否则抛异常
- PROPAGATION_NESTED:嵌套事务
AServiceImpl
public class AServiceImpl implements AService{
@Autowired
AMapper aMapper;
@Override
public void save(Accounts accounts) {
aMapper.save(accounts);
System.out.println("账户增加成功");
System.out.println(1/0);
}
}
UServiceImpl
public class UServiceImpl implements UService{
@Autowired
UMapper uMapper;
@Autowired
AService aService;
@Override
public void insert(Users users) {
uMapper.insert(users);
System.out.println("用户增加成功");
//调用账户增加操作
aService.save(new Accounts(204,"jack","账户安全"));
}
}
UServiceImpl包含AServiceImpl
UServiceImpl | AServiceImpl(抛异常) | 结果 | |
---|---|---|---|
1 | 无事务 | 无事务 | u=ok,a=ok |
2 | 无事务 | REQUIRED | u=ok,a=no |
3 | REQUIRED | 无事务 | u=no,a=no |
4 | REQUIRED | REQUIRED | u=no,a=no |
5 | REQUIRED | NOT_SUPPORTED | u=no,a=ok |
6 | REQUIRED | SUPPORTED | u=no,a=no |
7 | REQUIRED | REQUIRED_NEW | u=no,a=no |
8 | REQUIRED | NEVER | u=no,a异常 |