spring
一、Spring简介
1、spring是什么
Spring是分层的java se/ee应用full-stack轻量级开源框架,以IOC(Inverse Of Control 控制反转 )和AOP(Aspect Oriented Programming 面向切面编程)为内核。
提供了展现层SpringMVC和持久层Spring JDBCTemplate以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的java ee企业应用开源框架。
2、spring发展历程
1997年,IBM提出了EJB的思想
1998年,SUN公司制定开发标准规范EJB1.0
1999年,EJB1.1发布(Enterprise JavaBean)
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布
Rod Johnson,spring之父,Expert One-to-One Design and Development(2002),阐述了J2EE使用EJB开发设计的有点及解决方案。
Expert One-to-One J2EE Development without EJB(2004) 阐述了J2EE不使用EJB的解决方案。(Spring雏形)
3、spring的优势
1.方便解耦,简化开发
通过spring提供的IOC容器,可以将对象间的以来关系交由spring进行控制,避免硬编码所造成的过度耦合,用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
1.耦合是什么?
耦合就是程序间的依赖关系。包括类之间的耦合和方法间的耦合。
2.解耦是什么?
解耦就是降低程序间的以来关系。
在实际开发中,我们应该做到编译器不依赖,运行时才依赖。
3.解耦的思路?
首先我们使用反射来创建对象,避免使用new关键字。
其次我们通过读取配置文件来获取要创建的对象的全限定类名。
2.AOP编程的支持
通过Spring的AOP功能,方便进行面向切面编程,许多不需要传统OOP(object oriented programming)实现的功能可以通过AOP轻松实现。
1.什么是aop?
aop就是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
aop是oop的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生范型。
通过aop可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度变低,提高程序的可重用性。
简单的来说就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上对已有方法进行增强。
2.aop的作用及优势?
在程序运行期间,不修改源码对已有方法进行增强。
减少重复代码 提高开发效率 维护方便
3.aop的实现方式:使用动态代理
3.声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器以来的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5.方便集成各种优秀框架
spring对各种优秀框架都能进行集成使用。
6.降低了JavaEE API的使用难度
spring对于JavaEE进行了薄薄的封装,使用这些api的难度大大降低。
7.java源码是经典的学习范例。
spring源码中使用的算法和设计模式都是极为巧妙的。
4、spring的体系结构
二、Spring快速入门
1、spring程序开发步骤
1.导入Spring开发的基本包坐标
2.编写Dao接口和实现类
3.创建Spirng核心配置文件
4.在Spring配置文件中配置要产生实例对象的全限定类名
5.使用Spring的API获取Bean实例
2、spring对bean的管理细节
1.spring创建bean的三种方式
第一种方式:使用默认构造函数创建
在spring的配置文件中使用bean标签,配置id和class属性以后,且没有其他属性和标签时,
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
<bean id="accountService" class = "com.xzy.service.impl.AccountService"></bean>
默认构造函数如下:
public AccountServiceImpl(){
System.our.println("对象创建了!");
}
第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器中)
假如我们想要使用的类,是存在其他jar包中,这里我们无法修改源码,只能使用这种方法。
<bean id="instanceFactory" class="com.xzy.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
第三种方法:使用静态工厂中的静态方法创建对象(使用某个勒种的静态方法创建对象,并存入spring容器)
<bean id="accountService" class="com.xzy.factory.StaticFactory" factory-method="getAccountService"></bean>
2.bean对象的作用范围
bean标签的scope属性用来指定bean的作用范围
singleton: 单例(也是默认值)
prototype: 多例
request: 作用于web应用的请求范围
session: 作用于web应用的会话范围
global-session: 作用于集群环境的会话范围,如果不是集群环境,那么就是session
3.bean对象的生命周期
单例对象: 一般使用sintleton方式,也就是即时加载
出生:当容器创建时对象出生
活着:只要容器还在,那么单例对象就一直活着
死亡:容器消失,单例对象死亡
多例对象:一般使用prototype方式,也就是延时加载
出生:只有使用对象时对象才出生
活着:对象在使用过程中就一直活着
死亡:由jvm中垃圾回收机制回收
3、spring中的依赖注入(DI IOC)
1.注入的数据
第一种:基本类型和String
<bean id="accountService2" class="com.xzy.service.impl.AccountServiceImpl2">
<property name="name" value="test2"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
第二种:其他bean,例如在配置文件中或者注解配置过的bean
<bean id="accountService" class="com.xzy.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
第三种:复杂类型或集合类型等
<bean id="accountService3" class="com.xzy.service.impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</array>
</property>
<property name="myList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="mySet">
<set>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testa" value="aaa"></entry>
<entry key="testb" value="bbb"></entry>
<entry key="testc" value="ccc"></entry>
</map>
</property>
<property name="myPropers">
<props>
<prop key="testA">aaa</prop>
<prop key="testB">bbb</prop>
<prop key="testC">ccc</prop>
</props>
</property>
</bean>
2.注入的方式
//如果是经常变化的数据,那么并不适用注入的方式
第一种:使用构造方法注入
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name,Integer age,Date birthday){
this.name = name;
this.age = age;
this.birthday = birthday;
}
<!--
构造函数的注入:
标签中属性的具体解释:
type:用以指定要注入数据的数据类型,该数据类型也是构造函数中某个或某些参数得类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值
=================以上三个用于指定构造函数中那个参数赋值==================================
value:用于提供基本类型和String类型的数据
ref:用于引用其他bean类型的数据.指的就是spring的容器中出现的bean对象
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功.
缺点:改变了bean对象的实例化方式,使我们在创建对象时,如果我们不需要该对象,必须自己提供数据.
-->
<bean id="accountService" class="com.xzy.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
第二种:使用set方法注入(更常用的方式)
//如果是经常变化的数据,并不适用于注入的方式
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
<!--
使用set方法注入:使用标签 property
标签的属性:
name:用以指定注入时所调用的set方法名称
value和ref和上述一样
优势:创建对象时没有明确的限制,可以直接使用默认构造函数
缺点:如果某个成员必须有值,则set方法无法保证能一定注入
-->
<bean id="accountService2" class="com.xzy.service.impl.AccountServiceImpl2">
<property name="name" value="test2"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
第三种:使用注解方式注入
在使用这种方式前需要导入相应的xml约束空间,然后开启包扫描
<context:component-scan base-package="com.xzy"></context:component-scan>
用于创建对象的,作用就是把当前类通过反射创建一个对象,然后存入到spring容器中
这里的value是用于指定类的id,如果不写默认为当前类名
@Component
@Controller
@Service
@Repository
用于注入数据的
@Autowired 自动按照类型注入(前提是有唯一一个bean和它匹配)如果没有或者类型多于一个,那么就会报错。
@Qualifier 在按照类注入的基础之上再按照名称注入,在单独给类成员注入时不能使用,但是在给方法单数注入时可以使用。
@Resource 直接按照bean的id注入 使用name属性指定bean的id
以上三个注入都只能注入bean类型的数据,而基本类型和string类型无法使用上述注解实现
另外,集合类型的注入只能使用xml来实现。
@Value 用以注入基本类型和string类型的值,可以使用spirng中的spel表达式
用于改变作用范围的
@Scope 用于指定bean的作用范围,如果不写默认为singleton
和生命周期相关的
@Predestroy 用于指定销毁方法
@PostConstruct 用以指定初始化方法
其他
@Bean 把当前方法的返回值作为bean对象存入到spring容器中
4、spring Aop实现
1.动态代理实现
public static void main(String[] args) {
//匿名内部类访问外部成员方法时需要使用final修饰
final Producer producer = new Producer();
/**
* 动态代理:
* 特点:字节码随用随创建,在不修改源码的基础上对方法进行增强
* 分类:基于接口的动态代理和基于子类的动态代理
*
* 基于接口的动态代理: 涉及的类Proxy,提供者:jdk官方
* 如何创建代理对象:使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:
* ClassLoader 用于加载代理对象字节码的,和被代理对象使用相同的类加载器
* Class[] 字节码数组 它是让代理对象和被代理对象有相同方法
* InvocationHandler 它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类.
*/
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* @description: 执行被代理对象的任何接口方法都会经过该方法
* @Param proxy 代理对象的引用
* @Param method 表示当前执行的方法
* @Param args 当前执行方法所需的参数
* @return: java.lang.Object 和被代理对象方法有相同的返回值
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的方法
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())){
returnValue=method.invoke(producer,money*0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f);
}
public static void main(String[] args) {
//匿名内部类访问外部成员方法时需要使用final修饰
final Producer producer = new Producer();
/**
* 动态代理:
* 特点:字节码随用随创建,在不修改源码的基础上对方法进行增强
* 分类:基于接口的动态代理和基于子类的动态代理
*
* 基于子类的动态代理: 涉及的类Enhancer,提供者:第三方cglib库
* 如何创建代理对象:使用Enhancer类中的create方法
* 创建代理对象的要求:被代理类不能是最终类
* create方法的参数:
* Class 字节码 用以指定被代理对象的字节码
* Callback 用于提供增强的代码 我们一般用的是该接口的实现类MethodInterceptor
*/
Producer cglibProducer = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* @description: 执行此对象的任何方法都会经过该方法
* @Param obj 和invoke中的方法中的proxy是一样的
* @Param method
* @Param args
* @Param proxy 当前执行方法的代理对象
* @return: java.lang.Object
*/
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//提供增强的方法
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())){
returnValue=method.invoke(producer,money*0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(12000f);
}
2.aop相关术语
1.Joinpoint 连接点 所谓连接点就是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
通俗的来说,这里的连接点指的就是业务层的所有方法,包括被增强的方法和没有被增强的方法。
2.Pointcut 切入点 所谓切入点是指我们要对哪些joinpoint进行拦截的定义。
通俗来说,这里的切入点指的就是业务层被增强的那些方法。
3.Advice 通知/增强 所谓通知就是指拦截到joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
4.Introduction 引介 引介是一种特殊的通知在不修改代码的前提下,introduction可以在运行期间为类动态地添加一些方法或者Field。
5.Target 目标对象 也就是代理的目标对象。
6.Weaving 织入 是指把增强应用到目标对象来创谄媚新的代理对象的过程。
spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。
7.Proxy 代理 一个雷被aop织入增强后,就产生一个结果代理类。
8.Aspect 切面 是切入点和通知的结合。
3.学习spring aop我们必须明确的事情
a 开发阶段(我们做的)
编写核心业务代码:大部分程序员来做,要求熟悉业务需求。
把公用代码抽取出来,制作成通知。aop编程人员来做,一般是开发阶段最后实现。
在配置文件中,声明切入点与通知间的关系,及切面,一般是aop编程人员来做。
b 运行阶段(spring框架帮我们完成的)
spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创谄媚目标对象的代理对象,根据通知类型,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
4.关于代理的选择
在spring中,框架会根据目标类是否实现了接口来决定采取哪种动态代理的方式。
5.基于xml实现aop
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置springioc 把service对象配置进来-->
<bean id="accountService" class="com.service.impl.AccountServiceImpl"></bean>
<!--
spring中基于xml的aop配置步骤:
1.把通知bean也交给spring来管理
2.使用aop:config标签标示开始aop的配置
3.使用aop:aspect标签表示开始配置切面
id 是给切面提供一个唯一标识
ref 是指定通知类bean的id
4.在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在事例方法在切入点方法执行之前执行,所以是前置通知
pointcut用以指定切入点表达式,该表达式的含义指的是对业务层中哪些方法进行增强
切入点表达式的写法:
关键字:execution
表达式: 访问修饰符 包名.包名.包名...类名.方法名(参数列表)
标准写法:public com.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略
返回值可以使用通配符,表示任意返回值
* com.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包,但是几级包需要写几个*
包名可以使用..表示当前包及其子包
类名和方法名都可以使用*来通配
方法参数列表:基本数据类型直接写名称 int 引用数据类型写包名.类名的方式 java.lang.String
全通配写法: * *..*.*(..)
实际开发中,切入点表达式一般切到业务层实现类下的所有方法
* com.xzy.service.impl.*.*(..)
-->
<bean id="logger" class="com.utils.Logger"></bean>
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<!--
前置通知 在切入点方法执行之前执行
后置通知 在切入点方法正常执行之后执行
异常通知 在切入点方法执行产生异常之后执行
最终通知 无论切入点方法是否正常执行都在其后面执行
后置通知和异常通知两个只能执行其中的一个.
-->
<aop:before method="printLog" pointcut="execution(public void com.service.impl.AccountServiceImpl.saveAccount())"></aop:before>
<aop:after-returning method="afterLog" pointcut="execution(public void com.service.impl.AccountServiceImpl.saveAccount())"></aop:after-returning>
<aop:after-throwing method="exceptionLog" pointcut="execution(public void com.service.impl.AccountServiceImpl.saveAccount())"></aop:after-throwing>
<aop:after method="finalLog" pointcut-ref="pt1"></aop:after>
<!--配置切入点表达式,id指定表达式的唯一表示 expression用以指定表达式内容-->
<aop:pointcut id="pt1" expression="execution(* com.service.impl.*.*(..))"/>
<!--配置环绕通知-->
</aop:aspect>
</aop:config>
</beans>
6.基于注解实现aop
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置要扫描的包-->
<context:component-scan base-package="com"></context:component-scan>
<!--配置spring开启注解aop的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
@Component("logger")
@Aspect//表示当前类是一个切面类
public class Logger {
/**
* @description: 用以打印日志,计划让其在切入点方法之前执行,切入点方法啊就是业务层方法
* @Param
* @return: void
*/
public void printLog(){
System.out.println("log类中的日志类开始记录日志了");
}
@Pointcut("execution(* com.service.impl.*.*(..))")
private void pt1(){}
@Before("pt1()")
public void beforeLog(){
System.out.println("前置通知");
}
@AfterReturning("pt1()")
public void afterLog(){
System.out.println("后置通知");
}
@AfterThrowing("pt1()")
public void exceptionLog(){
System.out.println("异常通知");
}
@After(("pt1()"))
public void finalLog(){
System.out.println("最终通知");
}
/**
* @description: 环绕通知
* 会出现问题:当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
* 分析:通过对比动态代理中的环绕通知代码分析,动态代理中的环绕通知有明确的切入点方法调用,而我们的代码中没有.
* 解决:spring框架为我们提供了一个接口:ProceedingJoinPoint,该接口有一个方法proceed(),此方法就相当于明确调用切入点方法.
* 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架回味我们提供该接口的实现供我们使用.
* @Param
* @return: void
*/
//@Around("pt1()")
public Object aroundLog(ProceedingJoinPoint pjp){
//明确调用切入点方法
Object rtValue = null;
try{
Object[] args = pjp.getArgs();
System.out.println("环绕通知 前置");//这里就成前置通知
pjp.proceed(args);
System.out.println("环绕通知 后置");//这里就成了后置通知
return rtValue;
}catch(Throwable t){
System.out.println("环绕通知 异常");//这里就成了异常通知
throw new RuntimeException("运行时异常");
}finally {
System.out.println("环绕通知 最终");//这里就成了最终通知
}
}
}
5.spring的事务控制
关于spring事务控制我们要明确的:
1.JavaEE体系进行分层开发,spring提供了分层设计业务层的事务处理解决方案。
2.spirng框架为我们提供了一组事务控制的接口。
3.spring的事务控制都是基于aop的,可以通过编程的方式实现,也可以使用配置的方式实现,我们希望通过配置的方式实现。
基于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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置业务层-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!-- 配置账户的持久层-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
<!-- spring中基于XML的声明式事务控制配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性
是在事务的通知tx:advice标签的内部
-->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务的属性
isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 配置aop-->
<aop:config>
<!-- 配置切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
<!--建立切入点表达式和事务通知的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
</aop:config>
</beans>
基于注解的事务控制
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启spring对注解事务的支持-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
然后在需要事务支持的地方加@Transaction注解即可