对象的注入可以采用普通的Bean标签 也可以采用自动扫描包 但是要在类的前面加上注解 例如@Service
-
@Component
value属性 : 用于指定 bean 的 id 。当我们不写时,他的默认值是当前类名,且首字母小写。
-
@Controller
: 一般用于表现层的注解。 -
@Service
: 一般用于业务层的注解。 -
@Repository
: 一般用于持久层的注解。
如果需要对值初始化的话 可以直接在属性前面加@value()
bean 标签的 scope
属性就是用来指定 bean 的作用范围的
- singleton : 默认值,单例的. (bean对象默认是单例模式) 创建容器的时候就创建了对象
- prototype : 多例的. 可以在java类中直接配置 也可以在bean中配置属性 需要的时候就创建 不需要的时候java销毁
@Repository
@Scope("prototype")
public calss UserDao{
public String hello(){
return "hello";
}
}
依赖注入
依赖注入也就是说当我们在一个类中需要使用另一个类的时候 就会用到
具体的注入方法:
- 构造函数
- set方法
- 注解
- 构造函数注入的就是调用有参构造函数进行注入 使用的便签:
constructor-arg
<bean id = "accountService" class = "com.smallbeef.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name = "age" value="20"></constructor-arg>
<constructor-arg name = "birthday" ref="now"></constructor-arg>
</bean>
<!--配置一个日期对象
读取这个类名通过反射创建对象并存入spring容器中,我们可以通过id来使用它
-->
<bean id="now" class="java.util.Date"></bean>
- set注入 :这个就类似于前面的Bean中的属性赋值 就是将要注入的对象作为一个属性 在类中有set方法 在配置bean的时候就用ref进行注入 如果是普通的属性就value ,bean对象的话就用ref
<bean id = "accountService" class = "com.smallbeef.service.impl.AccountServiceImpl">
<property name="name" value="test"></property>
<property name="age" value="20"></property>
<property name="birthday" ref = "now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
- 注解
@Autowired
作用: 自动按照类型注入。
出现位置:变量和方法上都可以
当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。
在 Spring 容器查找,找到了注入成功。找不到 就报错。
当有多个类型匹配时,使用要注入的 对象变量名称 作为 bean 的 id
-
只有一个相符合的bean时,直接匹配数据类型
-
有多个相符合的bean时,先匹配数据类型,再将变量名称和bean的id进行匹配
当变量名称找不到一样的 bean 的 id 的时候,就会报错。
为解决变量名称和 bean 的 id 不匹配的情况,有了如下注解
Qualifier
。
@Qualifier
作用: 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
它在给成员变量注入时不能独立使用,必须和 @Autowire
一起使用;但是给方法参数注入时,可以独立使用
属性: value:指定 bean 的 id。
@Resource
作用: 直接按照 Bean 的 id 注入。可以独立使用(相当于Autowired + Qualifier)。它也只能注入其他 bean 类型。
属性: name:指定 bean 的 id (可不写)。
集合类型的注入只能通过 XML 来实现
Spring的纯注解配置
// 获取容器时需要使用 AnnotationApplicationContext(有 @Configuration 注解的类 .class)。
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
@Configuration
@ComponentScan("com.smallbeef")
public class JDBCConfiguration {
}
-
@Bean用来代替之前的xml中bean标签
作用: 该注解只能写在方法上,表明把当前方法的返回值作为 bean 对象存入 Spring 容器中。
属性: name:给当前
@Bean
注解方法创建的对象指定一个名称(即 bean 的 id)。 默认值是当前方法的名称细节:当我们使用注解配置方法时,如果方法有参数,Spring 框架会去容器中查找有没有相匹配的 bean 对象,查找方法和
AutoWired
一样。
/**
* 连接数据库的配置类
*/
@Configuration
@ComponentScan("com.smallbeef")
public class JDBCConfiguration {
/**
* 创建一个数据源,并存入 spring 容器中
* * @return
* */
@Bean(name="dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("root");
ds.setPassword("1234");
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql:///spring_day02");
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 创建一个 QuerryRunner对象,并且也存入 spring 容器中
* * @param dataSource
* * @return
* */
@Bean(name="dbAssit")
public DBAssit createDBAssit(DataSource dataSource) {
return new DBAssit(dataSource);
}
}
- 使用==@Import==注解导入其他的配置类
@Import 导入其他配置类
作用:用于导入其他配置类,有 @Import
注解的类就是主配置类。在引入其他配置类时,其他子配置类可以不用再写 @Configuration
注解。当然写上也没问题。
属性: value[]
:用于指定其他配置类的字节码。
举个例子:大的 SpringConfiguration
类利用 @Import
包含小的 JDBCConfiguration
配置类,这样 AnnotationConfigApplicationContext
直接加载大的配置类,就会把这些小的配置类也都加载进来
@Configuration //在 AnnotationConfigApplicationContext中做参数时可以不写该注解
@ComponentScan(basePackages = "com.smallbeef.spring")
@Import({ JdbcConfig.class,xxxxxConfig.class, xxxxConfig.class})
public class SpringConfiguration {
}
public class JdbcConfig{
}
-----------------------------------------------------
// 直接加载大配置类即可
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
-
@propertySouerce加载.pro文件配置
作用:用于加载
.properties
文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到 properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。属性:
value[]
:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
@Configuration
@ComponentScan(basePackages = "com.smallbeef.spring")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
纯注解实现Ioc
注解类
package com.swpu.configTest;
import com.sun.xml.internal.bind.v2.model.core.ID;
import com.swpu.Entity.Student;
import com.swpu.Entity.Teacher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 功能描述:
* @Author: ygq
* @Date: 2021/6/29 21:02
*/
@Configuration
@ComponentScan("com.swpu.Entity")
public class TestAnnonation {
@Bean(name = "student")
public Student GetStu(){
return new Student();
}
@Bean(name = "teacher")
public Teacher GetTea(){
return new Teacher();
}
}
实体类:
package com.swpu.Entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 功能描述:
* @Author: ygq
* @Date: 2021/6/29 20:59
*/
@Component
public class Student {
@Value("zs")
private String name;
@Value("12")
private int id;
@Autowired
private Teacher teacher;
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", id=" + id +
", teacher=" + teacher +
'}';
}
public Student(String name, int id, Teacher teacher) {
this.name = name;
this.id = id;
this.teacher = teacher;
}
}
package com.swpu.Entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
/*
* 功能描述:
* @Author: ygq
* @Date: 2021/6/29 20:59
*/
@Component
public class Teacher {
@Value("lisi")
private String name;
@Value("teacher")
private String job;
@Value("12")
private int age;
public Teacher() {
}
public void CheckWork(){
System.out.println("检查作业");
}
public Teacher(String name, String job, int age) {
this.name = name;
this.job = job;
this.age = age;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", age=" + age +
'}';
}
}
main方法
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestAnnonation.class);
Teacher teacher = (Teacher)applicationContext.getBean("teacher");
Student bean = applicationContext.getBean(Student.class);
System.out.println(teacher);
System.out.println(bean);
Ioc源码分析
引言
spring-context 会自动将 spring-core、spring-beans、spring-aop、spring-expression 这几个基础 jar 包带进来。
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
}
上面是启动一个Spring容器的语句,意思就是在类路径下寻找一个指定的xml文件,然后根据这个xml文件生成一个Ioc容器的实例
![1](https://i-blog.csdnimg.cn/blog_migrate/c4e28c2eac192d768d62a27457cc4ad0.png)
可以看到,ClassPathXmlApplicationContext 兜兜转转了好久才到 ApplicationContext 接口,同样的,我们也可以使用绿颜色的 FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext 这两个类。基于注解的话就使用后面这个类
启动过程
Ioc的启动过程
![img](https://i-blog.csdnimg.cn/blog_migrate/0e42d808a4b1676b2447775c2f33915f.png)
SpringAop
面向切面编程 降低耦合度
不改变源代码实现新功能的添加
底层是基于动态代理实现,其中有两种情况的动态代理
- 有接口的动态代理(JDK)创建实现类的代理对象
- 无接口的(CGLib)创建当前类的子类的代理对象
JDK动态代理概述
JDK实现的动态代理是有接口的,所以我们需要创建一个接口类(这个类里面的方法需要被别人去实现),一个接口实现类。然后通过Proxy类中方法创建一个新的代理对象实例。代理对象可以增强方法
Object newProxyInstance(ClassLoader loader, //类加载器 ClassLoader
Class<?>[] interfaces,//委托类实现的接口 数组,至少需要传入一个接口进去
InvocationHandler h) //调用的 InvocationHandler 实例处理接口方法 也就是实现了invocationhandler的一个类(这个类中的invoke方法就是实现了对方法的增强的)
创建代理对象的时候需要强制转换 前面的类型就是接口的类型
重要的是实现invocationhandler接口的这个类,在这个类中首先应该传递过去一个委托类对象 因为代理就是要把原来的方法进行增强 ,这里一般采用构造方法传递
传递之后,在这个类中实现下面的方法
public Object invoke(Object proxy, Method method, Object[] args)
//参数Method可以通过反射调用委托类中的方法
//args是方法的参数
通过method.invoke(obj, args);方法可以直接调用委托类中的方法(注意 这里不是接口 实现类)这个方法的返回值就是源方法的返回值,对象就是源对象 参数就是方法的参数。
具体实例
-
接口类
package com.swpu.JDKProxy; /** * 功能描述: * 接口类 包含了两个方法 * @Author: ygq * @Date: 2021/6/30 10:59 */ public interface UserDao { public int add(int a,int b); String update(String id); }
-
实现类
package com.swpu.JDKProxy; /** * 功能描述: * * @Author: ygq * @Date: 2021/6/30 11:00 */ public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
-
实现了invocationHandler接口的类
class UserDaoProxy implements InvocationHandler { //创建的是谁的代理对象 就把谁的对象传递过来 这里就是UserDaoImpl 因为是要把它的方法进行增强 // 有参构造传递 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("Before method" + method.getName() + "传递的参数" + Arrays.toString(args)); //被增强的方法执行 invoke方法返回值就是源方法的返回值 Object invoke = method.invoke(obj, args); System.out.println("增强方法之后执行" + obj); System.out.println("invoke的值是:" + invoke); return invoke; //将原来的方法的返回值返回(如果有就返回) }
-
代理类
public class JdkProxy { public static void main(String[] args) { UserDaoImpl userDao = new UserDaoImpl(); //委托类的实例对象 不是接口 接口不可以实例化 Class[] interfaces = {UserDao.class};//委托类的数组 // 将生成的代理对象强制转换成委托类 UserDao o = (UserDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); System.out.println(o.add(1, 2)); System.out.println(o.update("Hello")); } }
AOP术语
- 连接点 :类里面的哪些方法可以被增强 这些方法就是连接点
- 切入点: 实际被真正增强的方法就是切入点 要增强谁谁就是切入点
- 通知(增强):实际增强的逻辑部分就是通知
- 前置通知:方法前面要执行的
- 后置通知:方法后面要执行的
- 环绕通知:方法前后都要执行的
- 异常通知:出现异常要执行的通知
- 最终通知:类似于try catch finally中的finally 无论怎样都会执行
- 切面:是动作, 把通知应用到切面的过程
AOP操作
在Spring框架中一般基于AspectJ实现AOP操作
AspectJ是一个独立于Spring框架的AOP框架,一般两个结合进行·AOP操作
可以使用XML实现 也可以使用基于注解 大多数是使用注解
切入点表达式
- 作用:知道对哪个类的那个方法进行增强
- 语法结构:execution(【权限修饰符】【返回类型】【类全路径】【方法名称】(【参数列表】))
例如 对com.swpu.dao.UserDao中的Add方法进行增强 权限修饰符可以用*代替
execution(*com.swpu.dao.UserDao.Add(…)) 方法中(…)代表参数
对com.swpu.dao.UserDao中的所有方法进行增强
execution(❤com.swpu.dao.UserDao.*(…))
AspectJ注解方式实现AOP
进行通知的配置:
- 在Spring配置文件中,开启注解扫描
- 使用注解创建User和UserProxy
- 在增强类上面添加注解@Aspect
- 在Spring配置文件中开启生成的代理对象
实例
- Spring.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"
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-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--这个必须是偶数个-->
<bean id="user" class="com.swpu.pojo.User" scope="prototype">
<property name="name" value="zs"></property>
<property name="pwd" value="z123"></property>
<property name="id" value="1"></property>
</bean>
<!--开启注解扫描-->
<context:component-scan base-package="com.swpu.AOPTest"></context:component-scan>
<!-- 开启AspectJ生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- UserTest类 记得加注解
package com.swpu.AOPTest;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 功能描述:
* @Author: ygq
* @Date: 2021/6/30 14:58
*/
@Component
public class UserTest {
public void add(){
System.out.println("Add方法执行");
}
}
-
UserProxy类
package com.swpu.AOPTest; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 功能描述: * * @Author: ygq * @Date: 2021/6/30 14:59 */ @Component @Aspect public class UserProxy { @Before(value = "execution(* com.swpu.AOPTest.UserTest.add(..))") public void Before(){ System.out.println("Before执行"); } @After("execution(* com.swpu.AOPTest.UserTest.add(..))") public void After(){ System.out.println("After"); } @Around("execution(* com.swpu.AOPTest.UserTest.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕前"); proceedingJoinPoint.proceed(); System.out.println("环绕之后"); } }
-
main方法 这里获取的对象是原来的委托类 而不是代理类
public class Test { public static void main(String[] args) { ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml"); UserTest bean = applicationContext.getBean(UserTest.class); bean.add(); } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9lkgWt9-1625053852172)(C:\Users\25699\AppData\Roaming\Typora\typora-user-images\image-20210630153654623.png)]
SpringAOP相同的切入点抽取
相同的切入点可以抽取出来 用pointCut注解表示 写在方法上面
package com.swpu.AOPTest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 功能描述:
*
* @Author: ygq
* @Date: 2021/6/30 14:59
*/
@Component
@Aspect
@Order(1) //设置优先级 多个增强类的时候要设置优先级的时候使用这个注解
public class UserProxy {
//抽取相同的切入点
@Pointcut(value ="execution(* com.swpu.AOPTest.UserTest.add(..))" )
public void pointDemo(){}
@Before(value = "pointDemo()")
public void Before(){
System.out.println("Before执行");
}
@After(value = "pointDemo()")
public void After(){
System.out.println("After");
}
@Around(value = "pointDemo()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前");
proceedingJoinPoint.proceed();
System.out.println("环绕之后");
}
}
jdbcTemplate
什么是jdbcTemplate:Spring框架对jdbc的模板 可以很方便的实现对数据的增删改操作
准备工作:
-
导入依赖
<!-- 数据库连接--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.19</version> </dependency> <!-- 事务--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.2.4.RELEASE</version> </dependency> <!-- jdbc依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.4.RELEASE</version> </dependency> <!-- 数据库连接池 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
-
在Spring配置文件中配置好数据库连接池
<context:property-placeholder location="classpath:db.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 配置连接池属性 --> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- c3p0连接池的私有属性 --> <property name="maxPoolSize" value="30"/> <property name="minPoolSize" value="10"/> <!-- 关闭连接后不自动commit --> <property name="autoCommitOnClose" value="false"/> <!-- 获取连接超时时间 --> <property name="checkoutTimeout" value="10000"/> <!-- 当获取连接失败重试次数 --> <property name="acquireRetryAttempts" value="2"/> </bean>
-
创建jdbcTemplate对象
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
-
创建Service类,创建Dao类,在Dao类注入jdbcTemplate对象 注意SQL语句中?用来占位 前面是语句 后面就是这个语句中的参数
package com.swpu.Dao; import com.swpu.bookEn.Book; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; /** * 功能描述: * Dao * @Author: ygq * @Date: 2021/6/30 15:56 */ @Repository public class bookDaoImpl implements bookDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public void add(Book book) { jdbcTemplate.update("insert into book values(?,?,?)",book.getName(),book.getId(),book.getDetail()); } } package com.swpu.Service; import com.swpu.Dao.bookDaoImpl; import com.swpu.bookEn.Book; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 功能描述: * * @Author: ygq * @Date: 2021/6/30 15:56 */ @Service public class bookService { @Autowired private bookDaoImpl bookDaoImpl; public void add(Book book){ bookDaoImpl.add(book); } } public class jdbcTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); bookService bean = ac.getBean(bookService.class); bean.add(new Book("java",1,"testJava")); } }
Spring中的事务操作
什么是事务:事务是数据操作的最基本单元,逻辑上的一组操作,要么都成功,要么都失败
典型场景:银行转账
事务的特性(ACID)
- 原子性 :不可分割,要么成功要么失败
- 一致性 :操作前后数量不变(转账的时候前后钱不变)
- 隔离性 :多事务操作不会相互产生影响
- 持久性:修改之后会不会改变
事务操作场景搭建
-
创建数据库表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k6j0Mtx9-1625053852191)(C:\Users\25699\AppData\Roaming\Typora\typora-user-images\image-20210630164205386.png)]
-
创建Service Dao 对象和注入关系
package com.swpu.Dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* 功能描述:
*
* @Author: ygq
* @Date: 2021/6/30 16:45
*/
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private UserDao userDao;
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addMoney() {
String sql = "update account set money = money+? where username = ?";
jdbcTemplate.update(sql,100,"mary");
}
@Override
public void subMoney() {
String sql = "update account set money = money-? where username = ?";
jdbcTemplate.update(sql,100,"lucy");
}
}
package com.swpu.Service;
import com.swpu.Dao.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 功能描述:
*
* @Author: ygq
* @Date: 2021/6/30 16:44
*/
@Service
public class UserService {
@Autowired
private UserDaoImpl userDao;
public void changeMoney(){
userDao.subMoney();
userDao.addMoney();
}
}
- 出错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sZP0p09e-1625053852195)(C:\Users\25699\AppData\Roaming\Typora\typora-user-images\image-20210630165513362.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1UeAtIqm-1625053852208)(C:\Users\25699\AppData\Roaming\Typora\typora-user-images\image-20210630165524633.png)]
- 开启事务
有两种方式开启事务 编程式 声明式(常用) 事务一般在Service中
声明式事务管理:
- 注解
- xml
在Spring进行声明式事务管理 底层使用AOP原理
Spring事务管理API:
-
提供一个接口 代表事务管理器 这个接口针对不同的框架有不同的实现类
事务操作(注解声明式事务管理)
<!-- 创建事务管理器实例对象--> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
在xml中添加配置 开启事务注解 xmlns:tx=“http://www.springframework.org/schema/tx”
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/beans/spring-tx.xsd
<!-- 开启事务注解-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>
- 添加注解 可以在方法(带表该方法添加事务 加到类就是整个类的所有事务加方法)
@Service
@Transactional
public class UserService {
@Autowired
private UserDaoImpl userDao;
public void changeMoney(){
userDao.subMoney();
//模拟异常
int i = 100/0;
userDao.addMoney();
}