简介
-
Spring是什么?
Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程) 为内核。提供了展现层(web层)SpringMVC、持久层(dao层)Spring JDBCTemplate、业务层(service层)事务管理等众多的企业级应用技术,还能整 合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
-
Spring之父:Rod Johnson
-
为什么使用Spring?
- 方便解耦,简化开发
- AOP编程的支持
- 声明式事务的支持
- 方便程序的测试
- 方便集成各种优秀框架
- 降低JavaEE API的使用难度
- 优秀的开源源码
-
Spring框架的体系结构
-
Spring框架重要模块
- Test : 提供了对 JUnit 和 TestNG 测试的支持。
- Core: 框架的最基础部分,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IOC 、依赖注入功能。
- Aspects:该模块为与AspectJ的集成提供支持。
- AOP :提供了面向切面的编程实现。
- JDBC:提供了JDBC的抽象层实现。
- Transactions:提供了基于AOP的事务控制实现
- ORM : 提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层。
- Web : 提供了基础的 Web 开发的上下文信息,为现有的Web框架,如JSF、Tapestry、Structs等,提供了集成
- WebMVC:即所谓的SpringMVC,提供了 Web 应用的 Model-View-Controller 全功能实现。
-
Spring框架常用模块
- Spring AOP
- Spring MVC
- Spring Test
- Spring JDBC
- Spring ORM
IOC反转控制
入门
-
IOC是什么?
IOC是指将传统的对象创建流程转变为交由框架进行创建和管理。在Spring中,对象的创建交给Spring进行配置。它包括DI(依赖注入,即向类的属性设置值)。
IoC与DI的关系:依赖注入不能单独存在,需要在IoC基础之上完成操作。
-
Spring程序开发步骤
① 导入Spring开发的基本包坐标(maven)
② 创建Bean(如Dao接口和实现类)
③ 创建Spring核心配置文件(applicationContext.xml)
④ 在Spring配置文件中进行配置(如UserDaoImpl)
⑤ 使用Spring的API获得Bean实例(getBean方法)
Spring配置文件
-
Bean标签基本配置
-
作用:用于配置对象交由Spring来创建。默认情况下它调用的是类中的无参构造函数
-
常用属性
- id:Bean实例在Spring容器中的唯一标识【基本属性】
- class:Bean的全限定名【基本属性】
- scope:指对象范围,取值如下:
- singleton:默认值,单例的,Spring容器中只有一个实例,在加载上下文时创建实例,只要容器在对象就一直存在,当销毁容器时对象被销毁
- prototype:多例的,每次获取Bean时新建对象,在获取Bean时创建新的实例,只要对象仍被使用就一直存在,当对象长时间不用时会被Java的垃圾回收器回收
- request:WEB项目中,Spring创建一个Bean的对象,将对象存入request域中
- session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
- global session:WEB项目中,应用在Portlet环境,如果没有Portlet环境那么该值相当于session
- factory-method:指定工厂方法,一般在工厂静态方法实例化方式中使用,含有该属性时class应该为工厂类的全限定名,注意工厂创建对象时仍受scope控制
- factory-bean:指定工厂Bean,一般在工厂实例方法实例化方式中使用
- init-method:指定类中的初始化方法名称,初始化方法将在对象创建后调用
- destroy-method:指定类中销毁方法名称,销毁方法将在对象销毁后调用
-
Bean实例化三种方法
- 无参构造方法实例化:class=目标类名
- 工厂静态方法实例化:class=工厂类名,factory-method=工厂方法名
- 工厂实例方法实例化:factory-bean=工厂类id,factory-method=工厂方法名
-
示例
<!-- 1.实例化方式:无参构造方法 --> <bean id="userDao" class="pers.zero.dao.impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destroy"></bean> <!-- 2.实例化方式:工厂静态方法 --> <!-- <bean id="userDao" class="pers.zero.factory.StaticFactory" factory-method="getUserDao"></bean> --> <!-- 3.实例化方式:工厂实例方法 --> <!-- <bean id="factory" class="pers.zero.factory.DynamicFactory"></bean> --> <!-- <bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean> -->
public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = (UserDao) app.getBean("userDao"); userDao.save(); }
-
-
Bean的依赖注入
-
概念:依赖注入是Spring框架核心IOC的具体实现。在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IOC解耦只是降低他们的依赖关系,但不会消除,例如service层仍会调用dao层的方法。这种service层和dao层的依赖关系,在使用Spring之后,就让Spring来维护了。简单的说,就是让框架以上下文为依据将dao层对象传入业务层,而不用我们自己去获取(如在service的方法中再次加载上下文并使用getBean获取dao对象)。
-
Bean的依赖注入方式:使用<property>标签(扩展:上下文配置时可以使用p命名空间)
-
有参构造方法
<bean id="user" class="pers.zero.service.impl.UserServiceImpl"> <constructor-arg name="userDao" ref="userDao"></constructor-arg> </bean> name:值是有参构造函数的形参名,不改变大小写,而不是类的属性名 ref:被引用Bean的id
-
set方法:
<bean id="userService" class="pers.zero.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> <property name="userName" value="小明"></property> <property name="age" value="18"></property> <property name="stringList"> <list> <value>ab</value> <value>cd</value> <value>ef</value> </list> </property> <property name="userMap"> <map> <entry key="张三" value-ref="user1"></entry> <entry key="李四" value-ref="user2"></entry> </map> </property> <property name="properties"> <props> <prop key="p1"> ppp1 </prop> <prop key="p2"> ppp2 </prop> </props> </property> </bean> <bean id="user1" class="pers.zero.entity.User"> <property name="name" value="张三"></property> <property name="addr" value="天涯"></property> </bean> <bean id="user2" class="pers.zero.entity.User"> <property name="name" value="李四"></property> <property name="addr" value="海角"></property> </bean> name:值是setXXX()方法中的XXX,第一个X要小写,而不是类的属性名 ref:被引用Bean的id(对应引用数据类型) value:属性的值(对应普通数据类型、String)
-
-
Bean的依赖注入的数据类型:
- 普通数据类型
- 引用数据类型
- 集合数据类型
-
-
引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件中通过import标签进行加载
<import resource="applicationContext-xxx.xml"/>
-
总结——Spring重点配置
Spring相关API
- ApplicationContext的实现类
- ClassPathXmlApplicationContext:从类的根目录下加载上下文时推荐使用
- FileSystemXmlApplicationContext:从磁盘路径上加载配置文件,配置文件可以在磁盘任意位置
- AnnotationConfigApplicationContext:使用注解配置容器对象时,需要使用该类来创建Spring容器
- getBean()
- public Object getBean(String name) throws BeansException:根据id获取Bean
- public <T> T getBean(Class<T> requiredType) throws BeansException:根据class获取Bean,上下文中只能有一个该类型的Bean
Spring配置数据源
-
数据源(连接池)的作用
- 数据源(连接池)是提高程序性能而出现的
- 事先实例化数据源,初始化部分数据源
- 使用连接资源时从数据源中获取
- 使用完毕后将连接资源归还给数据源
-
常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等
-
不使用Spring容器时的数据源创建方法:
-
编写数据源配置文件(驱动类、url、用户名、密码),习惯命名jdbc.properties
-
读取配置文件(以键值对方式为例)
ResourceBundle rb = ResourceBundle.getBundle("jdbc"); String driver = rb.getString("jdbc.driver");
-
创建数据源实例,配置实例,获取连接,使用,返还资源
-
-
使用Spring容器时的数据源创建方法:
-
编写数据源配置文件(驱动类、url、用户名、密码),习惯命名jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC jdbc.username=root jdbc.password=xxx
-
在Spring配置文件中引入数据源配置文件的数据
<!-- 添加命名空间与约束--> <beans xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 加载外部配置文件--> <context:property-placeholder location="jdbc.properties" />
-
在Spring配置文件中配置数据源Bean
<!-- 配置Bean--> <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}" /> </bean>
-
在代码中获取数据源Bean
-
获取连接,使用,返还资源
-
Spring注解开发
-
作用:减轻配置成本,提高开发效率
-
原始注解:主要用于替代<Bean>的配置
-
原始注解+XML配置文件开发流程
-
在对应位置添加注解
//@Component("userDao") @Repository("userDao") public class UserDaoImpl implements UserDao { ...... }
//@Component("userService") @Service("userService") @Scope("singleton") public class UserServiceImpl implements UserService { /* @Autowired //按照数据类型从容器中进行匹配,可单独使用 @Qualifier("userDao") //按照id值从容器中进行匹配,要结合@Autowired一起使用 */ @Resource(name="userDao") private UserDao userDao; //无需set @Value("${jdbc.driver}") private String driver; ...... }
-
在Spring配置文件中配置组件扫描
<context:component-scan base-package="pers.zero" />
-
通过getBean获取并使用Spring容器中的Bean
-
-
新注解:用于替代非自定义的Bean的配置、加载properties文件的配置、组件扫描的配置、引入Spring配置文件
-
原始注解+新注解开发流程
-
创建核心配置类
@Configuration //标志该类是Spring的核心配置类 @ComponentScan("pers.zero") //配置组件扫描 @Import({DataSourceConfiguration.class,}) //导入模块配置类 public class SpringConfiguration { }
-
创建模块配置类(以数据源配置类为例)【常用于配置外部类】
@PropertySource("classpath:jdbc.properties") //导入配置文件 public class DataSourceConfiguration { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("dataSource") //将方法的返回值以指定名称存入Spring容器 public DataSource getDataSource() throws PropertyVetoException { ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setDriverClass(driver); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } }
-
在自定义类中添加原始注解
-
通过getBean获取并使用Spring容器中的Bean
-
-
全注解开发总结
- 使用@ComponentScan方式设置组件扫描,能使自定义类中使用@Component、@Repository等注解注册的Bean被装入容器
- 对于外部类,需要通过@Import+@Bean才能将方法返回的Bean装入容器
-
一般而言,我们使用XML配置外部类,使用注解配置自定义类
Spring集成Junit
-
为什么要集成Junit?
如果单独使用Junit的话操作会比较繁琐,如每次测试前都要加载Spirng容器
-
集成思路
- 让SpringJunit负责创建Spring容器,但要把配置文件名告诉它
- 将需要进行测试的Bean直接在测试类中进行注入
-
集成步骤
-
导入junit与spring-test坐标
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <!-- 注意使用spring-test时junit需要4.12以上版本 --> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.5.RELEASE</version> </dependency>
-
使用@Runwith注解替换原来的运行期
-
使用@ContextConfiguration指定配置文件或配置类
-
使用配置文件或者注解注入需要测试的Bean
-
创建测试方法进行测试
-
-
示范
@RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration("classpath:applicationContext.xml") //配置文件方式 @ContextConfiguration(classes = {SpringConfiguration.class}) //配置类方式 public class SpringJunitTest { @Resource(name="userService") private UserService userService; @Autowired private DataSource dataSource; @Test public void test1() throws SQLException { userService.save(); System.out.println(dataSource.getConnection()); } }
AOP面向切面编程
入门
-
AOP是什么?
AOP是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
经典应用:事务管理、性能监视、安全检查、缓存 、日志等
-
AOP的作用与优势
- 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
- 优势:减少重复代码,提高开发效率,便于维护
-
AOP的底层实现
实际上,AOP的底层时通过Spring提供的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
-
常用的动态代理技术(Spring底层实现)
-
JDK代理:基于接口的动态代理技术
final Target target = new Target(); //目标对象 final Advice advice = new Advice(); //增强对象 //代理对象 TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance( target.getClass().getClassLoader(), //目标对象类加载器 target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组 new InvocationHandler() { //InvocationHandler实现类实例 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { advice.before(); Object ret = method.invoke(target,args); advice.after(); return ret; } } ); proxy.test();
-
cglib代理:基于父类的动态代理技术(Spring的core包中集成了cglib)
final Target target = new Target(); //目标对象 final Advice advice = new Advice(); //增强对象 //代理对象 //1.创建增强器 Enhancer enhancer = new Enhancer(); //2.设置父类(目标) enhancer.setSuperclass(target.getClass()); //3.设置回调 enhancer.setCallback( new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { advice.before(); Object ret = method.invoke(target,objects); advice.after(); return ret; } } ); //4.创建代理对象 Target proxy = (Target) enhancer.create(); proxy.test();
-
-
AOP相关术语
- Target(目标对象):被代理的目标对象
- Proxy(代理):一个类被AOP织入增强后,就产生了一个结果代理类
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情
- Aspect(切面):是切入点和通知(引介)的结合
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
-
AOP开发流程
- 编写核心业务代码(目标类的目标方法)
- 编写切面类,切面类中有通知(增强功能方法)
- 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
-
AOP技术实现的内容
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行。使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
-
AOP底层使用哪种代理方式?
在Spring中框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式
基于XML方式的AOP开发
-
快速入门
-
导入AOP相关坐标
<!-- Spring集成的第三方AOP包(官方推荐) --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.4</version> </dependency>
-
创建目标接口和目标类(内部有切点)
-
创建切面类(内部有增强方法)
-
将目标类和切面类的对象创建权交给Spring
-
在applicationContext.xml中配置织入关系
<!-- 引入命名空间与约束 --> <beans xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目标对象 --> <bean id="target" class="pers.zero.aop.Target" /> <!-- 切面对象 --> <bean id="myAspect" class="pers.zero.aop.MyAspect" /> <!-- 配置织入:告诉Spring哪些方法需要哪些增强(前置、后置...) --> <aop:config> <!-- 声明切面 --> <aop:aspect ref="myAspect"> <!-- 切面=切点+通知 --> <!-- <aop:before method="before" pointcut="execution(* pers.zero.aop.*.*(..))" /> --> <aop:pointcut id="myPointcut" expression="execution(* pers.zero.aop.*.*(..))"/> <!-- 抽取切点表达式 --> <aop:before method="before" pointcut-ref="myPointcut" /> <aop:after-returning method="afterReturnning" pointcut-ref="myPointcut" /> <aop:around method="around" pointcut-ref="myPointcut" /> <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" /> <aop:after method="after" pointcut-ref="myPointcut" /> </aop:aspect> </aop:config> </beans>
-
测试代码
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AopTest { @Autowired private TargetInterface target; //注意要使用接口,不能用private Target target; @Test public void test1(){ target.test(); } }
-
-
XML配置详解
-
切点表达式的写法:
execution([修饰符]返回值类型 包名.类名.方法(参数))
-
访问修饰符可以省略
-
返回值类型、包名、类名、方法名可以使用星号*表示任意
-
包名与类名之间一个点.表示当前包下的类,两个点…表示当前包及其子包下的类
-
参数列表可以使用两个点…表示任意个数、类型的参数列表
-
例如
execution(public void pers.zero.aop.Target.Method()) //指定类的指定方法 execution(* pers.zero.aop.Target.*(..)) //指定类的所有方法 execution(* pers.zero.aop.*.*(..)) //指定包下的所有方法 execution(* pers.zero.aop..*.*(..)) //指定包及其子包下的所有方法
-
-
通知的类型
//环绕通知需要参数 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前增强..."); Object proceed = pjp.proceed(); //切点方法 System.out.println("环绕后增强..."); return proceed; }
-
切点表达式的抽取
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式
<!-- 抽取切点表达式 --> <aop:pointcut id="myPointcut" expression="execution(* pers.zero.aop.*.*(..))"/> <aop:before method="before" pointcut-ref="myPointcut" />
-
基于注解方式的AOP开发
-
快速入门
-
创建目标接口和目标类(内部有切点)
-
创建切面类(内部有增强方法)
-
将目标类和切面类的对象创建权交给Spring
-
在切面类中使用注解配置织入关系
@Component("myAspect") @Aspect //注册为切面类 public class MyAspect { @Pointcut("execution(* pers.zero.aop_anno.*.*(..))") public void pointcut(){} //@Before("execution(* pers.zero.aop_anno.*.*(..))") @Before("pointcut()") //前置增强 public void before(){ System.out.println("前置增强..."); } @AfterReturning("MyAspect.pointcut()") public void afterReturnning(){ System.out.println("后置增强..."); } }
-
在配置文件中开启组件扫描和AOP的自动代理
<!-- 组件扫描 --> <context:component-scan base-package="pers.zero.aop_anno"/> <!-- aop自动代理 --> <aop:aspectj-autoproxy/>
-
测试
-
-
注解配置AOP详解
-
通知类型配置
-
切点表达式抽取
抽取方式是在切面中定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在增强注解中进行引用,具体如下
@Pointcut("execution(* pers.zero.aop_anno.*.*(..))") public void pointcut(){} @Before("pointcut()") //前置增强 public void before(){ System.out.println("前置增强..."); } @AfterReturning("MyAspect.pointcut()") public void afterReturnning(){ System.out.println("后置增强..."); }
-
基于Spring框架的数据库控制
Spring JdbcTemplate基本使用
-
简介
它是Spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。Spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的Jdbc Template和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等
-
JdbcTemplate开发步骤
- 导入spring-jdbc和spring-tx坐标
- 创建数据库表和实体
- 创建JdbcTemplate对象
- 执行数据库操作
-
示例
<context:property-placeholder location="jdbc.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}" /> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean>
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class JdbcTemplateTest { @Autowired DataSource dataSource; @Autowired JdbcTemplate jdbcTemplate; @Test //测试JdbcTemplate快速开发步骤 public void test1(){ int row = jdbcTemplate.update("INSERT INTO account values(?,?)","Tom",5000); System.out.println(row); } }
-
常用操作
public void testUpdate(){ jdbcTemplate.update("INSERT INTO account values(?,?)","One",5000); //增 jdbcTemplate.update("DELETE FROM account WHERE name=?","One"); //删 jdbcTemplate.update("UPDATE account SET money=? WHERE name=?",1000,"Zero"); //改 } //注意:使用BeanPropertyRowMapper的前提是 表中的字段名和实体类的成员变量名称一致!!! public void testQuery(){ //查所有对象 List<Account> accountList = jdbcTemplate.query("SELECT * FROM account",new BeanPropertyRowMapper<>(Account.class)); System.out.println(accountList); //查单个对象 Account acconut = jdbcTemplate.queryForObject("SELECT * FROM account WHERE name=?",new BeanPropertyRowMapper<>(Account.class),"Zero"); System.out.println(acconut); //查询数据条数 Long count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM account", Long.class); System.out.println(count); }
事务控制
-
编程式事务控制
-
事务隔离级别
-
事务传播行为(A业务方法调用B业务方法,若两方法都设置了隔离级别,通过事务传播行为解决冲突)(下面的省略主语是B业务方法,“当前”指的是A业务方法)
-
超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
-
是否只读:建议查询时设置为只读
-
-
基于XML的声明式事务控制(基于AOP)
-
引入tx命名空间与约束
-
配置
<!-- 切点 --> <bean id="accountService" class="pers.zero.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <!-- 通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 配置方法对应的事务属性,*是通配符 --> <tx:attributes> <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/> <!-- <tx:method name="findAll" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="true"/> --> <!-- <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/> --> </tx:attributes> </tx:advice> <!-- 织入 --> <aop:config> <!-- 表示只有一个通知 --> <aop:advisor advice-ref="txAdvice" pointcut="execution(* pers.zero.service.impl.*.*(..))"/> </aop:config> <!-- 平台管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
-
基于注解的声明式事务控制
-
配置平台管理器
<!-- 平台管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
配置事务注解驱动tx:annotation-driven
<tx:annotation-driven transaction-manager="transactionManager" />
-
在需要进行事务控制的方法上添加@Transactional注解
@Service("accountService") @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED,timeout = -1,readOnly = true) //(选用)所有方法进行事务控制 public class AccountServiceImpl implements AccountService { @Resource(name="accountDao") private AccountDao accountDao; @Override @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED,timeout = -1,readOnly = false) //冲突时以方法上的为主 public void transfer(String outMan, String inMan, double money) { //开启事务 accountDao.out(outMan,money); int i = 1/0; //模拟错误,钱转出去但未收到,需要事务回滚 accountDao.in(inMan,money); //提交事务 } }
-