1.需要导入的jar包
-
必须的jar包
commons-logging-1.1.3.jar spring-beans-4.0.0.RELEASE.jar spring-context-4.0.0.RELEASE.jar spring-core-4.0.0.RELEASE.jar spring-expression-4.0.0.RELEASE.jar
-
通过注解方式配置bean需要的jar包
spring-aop-4.0.0.RELEASE.jar
-
连接Mysql数据库需要导入的jar包
c3p0-0.9.1.2.jar mysql-connector-java-5.1.37-bin.jar
-
AOP、配置事务需要的jar包
com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aspects-4.0.0.RELEASE.jar
-
配置JdbcTemplate需要的jar包
spring-jdbc-4.0.0.RELEASE.jar spring-orm-4.0.0.RELEASE.jar spring-tx-4.0.0.RELEASE.jar
2.创建IOC容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
- beans.xml的Spring的配置文件需要放到源码包(如src)下
3.bean的配置
-
set注入(属性注入)
-
通过这中方式配置bean,IOC容器首先通过类的无参构造器创建对象,然后通过set方法将属性注入到对象中
//通过property标签配置bean,我们也称为set注入 <bean id="book" class="com.atguigu.spring.bean.Book"> <property name="id" value="1"></property> <property name="title" value="金瓶梅"></property> <property name="author" value="兰陵笑笑生"></property> <property name="price" value="2.5"></property> <property name="sales" value="100000"></property> </bean>
- 通过id属性给每一个bean起一个名字,id属性值不能重复,在IOC容器中必须要保证是唯一的,class属性用来设置Book类的全类名
- name属性值是Book类中的属性,不能任意指定
-
构造器注入
-
通过这种方式配置bean,IOC容器根据类的构造器创建对象
// 通过constructor-arg标签配置bean,我们称构造器注入 <bean id="book2" class="com.atguigu.spring.bean.Book"> <constructor-arg value="2"></constructor-arg> <constructor-arg value="三国演义"></constructor-arg> <constructor-arg value="罗贯中"></constructor-arg> <constructor-arg value="28.00"></constructor-arg> <constructor-arg value="100"></constructor-arg> </bean>
-
通过这种方式配置必备要保证Book类中有对应的包含5个参数的构造器
-
引入外部bean
-
引用其他的bean
<bean id="cartItem" class="com.atguigu.spring.bean.CartItem"> ref属性:用来引用在IOC容器中配置的其他的bean,其属性值是其他bean的id属性值 <property name="book" ref="book"> </property> <property name="count" value="10"></property> </ bean>
-
通过ref属性来引入其他的bean,ref的属性值是在IOC容器中配置的其他bean的id属性值
-
引入外部属性文件(通常用来配置数据源)
-
包含了连接数据库有关信息的配置文件db.properties
jdbc.user=root jdbc.password=root jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql:///test jdbc.initPoolSize=5 jdbc.maxPoolSize=10 jdbc.minPoolSize=2
在spring的配置文件中引入上面的文件
//引入外部属性文件
<context:property-placeholder location="classpath:db.properties"/>
<!-- 获取数据库连接 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
- 获取bean的方式
1.通过id属性值获取
2.通过类型获取
(1) 通过这种方式获取bean如果IOC中有多个会抛异常 - bean的作用域
-
默认情况下,IOC容器一创建就会创建IOC容器中配置的bean的实例,而且是单例的,任何时候获取得到的都是同一个对象
-
我们可以通过bean的scope属性来设置bean时多例的,这是每次获取bean时获取的是一个新的对象,而且IOC容器一创建不会创建IOC容器中配置的bean的实例,只有每次调用getBean()时才会创建对象
bean的作用域 <!-- singleton:默认,IOC容器一创建就创建该对象,而且是一个单例的 --> <!-- prototype:IOC容器创建时不会创建该对象,每次获取bean时才会创建对象,而且每次获取都会创建一个新的对象,是多例的 --> <bean id="student" class="com.atguigu.spring.bean.Student" scope="prototype"> <property name="id" value="1"></property> <property name="name" value="苍老师"></property> </bean>
-
4.通过注解的方式配置bean
@Component
用来标识一个普通组件
@Repository
用来标识一个持久化层的组件
@Service
用来标识一个业务逻辑层的组件
@Controller
用来标识一个表示层的控制器组件
-
在Spring的配置文件中配置自动扫描的包
<!-- base-package属性:用来设置要扫描的包及其子包,如果要扫描多个包中间使用逗号分隔 --> <!-- resource-pattern属性:用来精确设置要扫描的子包 --> <context:component-scan base-package="com.atguigu.spring.annotation"></context:component-scan>
通过在类上添加以上注解及配置自动扫描的包之后,被标识的类就会自动被IOC容器管理,在IOC容器中的bean的id属性值默认是类的首字母小写
我们也可以通过value属性值指定bean的id属性值
例如:@Repository(value=“bookDao”)或@Repository(“bookDao”)
@Autowired
通过该注解将某个类的属性进行自动注入
自动注入的规则:
1)首先检查要注入的属性的类型
2)根据属性的类型进行自动注入
3)如果该属性的类型在IOC容器中匹配到多个,以属性名作为bean的id属性值进行匹配
4)通过@Qualifier注解来指定要匹配的bean的id属性值
如:@Qualifier(value=“bookDao”)或@Qualifier(“bookDao”)
将IOC容器中id属性值是bookDao的bean装配进来
如果属性最终不能自动装配,默认会抛异常,我们可以通过requireds属性来设置某个属性不是必须的
如:@Autowired(required=false)
通过@Scope(value=“prototype”)来设置某个bean时多例的
在Spring的配置文件中配置要扫描那些组件
<!-- 自定义要扫描的组件的前提是use-default-filters的属性值改为false -->
<context:component-scan base-package="com.atguigu.spring.annotation" use-default-filters="false">
type属性值是annotation时,expression的属性值是注解的全类名
type属性值是assignable时,expression的属性值是类或接口的全类名
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
- 在Spring的配置文件中配置不扫描那些组件
<context:component-scan base-package="com.atguigu.spring.annotation">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="assignable" expression="com.atguigu.spring.annotation.service.impl.UserServiceImpl"/>
<context:exclude-filter type="assignable" expression="com.atguigu.spring.annotation.handler.UserHandler"/>
</context:component-scan>
5.AOP
@Aspect
添加到类上
用来声明一个类是一个切面类,同时还需要通过@Component注解将该切面类添加到IOC容器中
@Pointcut
添加到方法上
用来声明一个切入点表达式
@Before
添加到方法上
用来声明前置通知,在方法执行之前调用
@After
添加到方法上
用来声明后置通知,在方法调用完成之后执行(不管是否发生异常)
@AfterReturnning
添加到方法上
用来声明一个返回通知,在方法执行结束得到返回值时调用
@AfterThrowing
添加到方法上
用来声明一个异常通知,当方法方式异常时调用
@Around
添加到方法上
用来声明一个环绕通知,相当于动态代理的全过程
@Order(value=1)
添加到类上
用来声明切面的优先级
value值的范围的Integer类型的范围,值越小优先级越高
具体实现代码
创建一个切面类: 该类除了使用@Aspect声明为一个切面类之外还需要通过@Component将它加入到IOC容器中
*
* @author HanYanBing
* */
@Order(-1)//通过@Order注解设置切面的优先级,值越小优先级越高,值是Integer的范围
@Aspect
@Component
public class LoggingAspect {
//声明一个切入点表达式
@Pointcut(value = "execution(* com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))")
public void pointCut(){}
/*
* 1.@Before(value="execution(public int com.atguigu.spring.aop.ArithmeticCalculateImpl.add(int, int))")
* --只有调用ArithmeticCalculateImpl中的add方法时才会调用前置通知的方法
* 2.@Before(value="execution(public int com.atguigu.spring.aop.ArithmeticCalculateImpl.*(int, int))")
* --调用ArithmeticCalculateImpl中的所有方法时调用前置通知的方法
* 3.@Before(value="execution(public int com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))")
* --调用ArithmeticCalculateImpl中的所有方法时(不考虑参数的个数及类型)调用前置通知的方法
* ★4.@Before(value="execution(* com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))")
* --调用ArithmeticCalculateImpl中的所有方法时(不考虑权限修饰符和返回值类型及参数的个数及类型)调用前置通知的方法
* 5.@Before(value="execution(* *.*(..))")
* --调用所有类中的所有方法时都会调用前置通知的方法
*/
// 前置通知:在方法调用前执行
@Before(value = "execution(* com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint) {
// 获取方法名
String name = joinPoint.getSignature().getName();
// 获取传入方法中的参数
Object[] args = joinPoint.getArgs();
System.out.println("[★★★Logging]:The method " + name + " begins with " + Arrays.asList(args));
}
// 后置通知:在方法调用后执行(不管是否发生异常)
@After("execution(* com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))")
public void afterMethod(JoinPoint joinPoint) {
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("[★★★Logging]:The method " + name + " ends");
}
// 返回通知:在方法执行完之后调用
@AfterReturning(value = "execution(* com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))", returning = "result")
public void afterReturn(JoinPoint joinPoint, Object result) {
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("[★★★Logging]:The method " + name + " returns " + result);
}
// 异常通知:在方法执行出现异常时调用
@AfterThrowing(value = "execution(* com.atguigu.spring.aop.ArithmeticCalculateImpl.*(..))", throwing = "e")
public void throwMethod(JoinPoint joinPoint, Throwable e) {
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("[★★★Logging]:The method "+name+" occurs exception "+e);
}
//环绕通知:相当于动态代理的全过程
@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp){
//获取方法名
String name = pjp.getSignature().getName();
//获取方法中传入的参数
Object[] args = pjp.getArgs();
//前置通知
System.out.println("[★★★★★Logging]:The method " + name + " begins with " + Arrays.asList(args));
Object result = null;
try {
//调用方法
result = pjp.proceed();
//返回通知
System.out.println("[★★★★★Logging]:The method " + name + " returns " + result);
} catch (Throwable e) {
//异常通知
System.out.println("[★★★★★Logging]:The method "+name+" occurs exception "+e);
// e.printStackTrace();
} finally {
//后置通知
System.out.println("[★★★★★Logging]:The method " + name + " ends");
}
return result;
}
}
- 通过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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 配置数学计算的实现类的bean -->
<bean id="arithmeticCalculate" class="com.atguigu.spring.aop.xml.ArithmeticCalculateImpl"></bean>
<!-- 配置切面bean -->
<bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut expression="execution(* com.atguigu.spring.aop.xml.ArithmeticCalculateImpl.*(..))"
id="poinCut"/>
<!-- 配置切面-->
<aop:aspect ref="loggingAspect">
<!-- 配置前置通知 -->
<aop:before method="beforeMethod" pointcut-ref="poinCut"/>
<!-- 返回通知 -->
<aop:after-returning method="afterReturn" pointcut-ref="poinCut" returning="result"/>
<!-- 异常通知 -->
<aop:after-throwing method="throwMethod" pointcut-ref="poinCut" throwing="e"/>
<!-- 后置通知 -->
<aop:after method="afterMethod" pointcut-ref="poinCut"/>
</aop:aspect>
</aop:config>
</beans>
6.配置JdbcTemplate
在Spring中配置JdbcTemplate
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.atguigu.spring.jdbc"></context:component-scan>
<!-- 导入连接数据的有关信息的外部文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置NamedParameterJdbcTemplate -->
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
</beans>
- JdbcTemplate中封装了增删改、批量更新、获取一个对象、获取一个集合、获取一个单一值的方法
public class JdbcTemplateTest {
//创建IOC容器对象
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans-jdbc.xml");
//获取JdbcTemplate对象
JdbcTemplate jdbcTemplate = (JdbcTemplate) ioc.getBean("jdbcTemplate");
//获取NamedParameterJdbcTemplate对象
NamedParameterJdbcTemplate npjt = (NamedParameterJdbcTemplate) ioc.getBean("namedParameterJdbcTemplate");
@Test
public void testDataSource() throws SQLException {
DataSource dataSource = ioc.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}
//通用的增删改
@Test
public void testUpdate(){
String sql = "insert into employees(last_name,email,salary,dept_id) values(?,?,?,?)";
jdbcTemplate.update(sql, "马蓉","mr@sz.com",25000,6);
}
//批量更新
@Test
public void testBatchUpdate(){
String sql = "insert into employees(last_name,email,salary,dept_id) values(?,?,?,?)";
//创建一个集合
List<Object[]> batchArgs = new ArrayList<>();
batchArgs.add(new Object[]{"王宝强","sg@ts.com",100,5});
batchArgs.add(new Object[]{"宋喆","sz@sjj.com",1000,6});
batchArgs.add(new Object[]{"白百何","bbh@yf.com",1000,6});
batchArgs.add(new Object[]{"文章","wz@myl.com",10000,6});
jdbcTemplate.batchUpdate(sql, batchArgs);
}
//获取一个对象的方法
/**
* 1.调用的不是queryForObject(String sql, Class<T> requiredType, Object... args)这个方法,而是
* queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
* -RowMapper使用的实现类是BeanPropertyRowMapper(Class<T>),它就相当于Dbutils中的BeanHandler
* 2.不支持级联属性的查询
*/
@Test
public void testGetBean(){
String sql = "select id,last_name lastName,email,salary,dept_id from employees where id = ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 4);
System.out.println(employee);
}
//获取一个集合
@Test
public void testGetBeanList(){
String sql = "select id,last_name lastName,email,salary from employees where id > ?";
RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
List<Employee> list = jdbcTemplate.query(sql, rowMapper,3);
System.out.println(list);
}
//获取一个单一的值
@Test
public void testGetSingelValue(){
String sql = "select count(*) from employees";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(integer);
}
//使用具名参数的形式向数据库中插入一条数据
//可以通过具名参数给占位符起一个名字,这时我们填充占位符时只需要根据具名参数的名字填充占位符即可,不需要在意它们的顺序
@Test
public void testNamedPrameterJdbcTemplate(){
String sql = "insert into employees(last_name,email,salary,dept_id) values(:ln,:em,:sy,:dpt)";
//创建一个Map
Map<String , Object> parmMap = new HashMap<>();
parmMap.put("ln", "甘露露");
parmMap.put("sy", 500);
parmMap.put("em", "gll@sll.com");
parmMap.put("dpt", 6);
npjt.update(sql, parmMap);
}
//测试SqlParameterSource
/*
* 使用SqlParameterSource时:
* 1.具名参数的值要与对象的属性值保持一致
* 2.SqlParameterSource的实现类使用BeanPropertySqlParameterSource
*/
@Test
public void testSqlParameterSource(){
String sql = "insert into departments(name) values(:name)";
Department department = new Department();
department.setName("财务部");
SqlParameterSource sqlSource = new BeanPropertySqlParameterSource(department);
npjt.update(sql, sqlSource);
}
@Test
public void testDepartmentDao(){
DepartmentDao departmentDao = (DepartmentDao) ioc.getBean("departmentDao");
Department dept = new Department();
dept.setName("公关部");
departmentDao.insertDept(dept );
}
7.给方法添加事务
配置事务管理器
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务注解起作用 -->
<!-- transaction-manager默认值是transactionManager,如果事务管理器的id属性值为transactionManager,transaction-manager属性可以省略 -->
<tx:annotation-driven />
通过@Transactional注解添加事务
~~~~~~~~如果@Transactional添加到类上
~~~~~~~~~~~~~~类中所有的方法都添加了事务
- 如果@Transactional只添加到了方法上
- 只有这一个方法添加了事务
事务中的一些属性
- 只有这一个方法添加了事务
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
/**
* 事务的传播行为:一个方法运行在一个添加了事务的方法中,当前方法是开启一个新事务还是使用原来的事务
* --通过propagation设置事务的传播行为:Propagation.REQUIRED,默认,使用原来的事务
* Propagation.REQUIRES_NEW,开启一个新事务
* 事务的隔离级别:通过isolation设置事务的隔离级别
* -Mysql默认的隔离级别是Isolation.REPEATABLE_READ;Oracle默认的隔离级别是Isolation.READ_COMMITTED
* -常用的隔离级别:Isolation.READ_COMMITTED 读已提交
* 通过rollbackFor、rollbackForClassName和noRollbackFor、noRollbackForClassName设置出现那些异常回滚和不回滚
* 通过readOnly=true设置该操作是一个只读的,一般查询的方法中会设置该属性
* timeout属性:用来设置事务的超时时间,单位是秒
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,
noRollbackForClassName={"ArithmeticException"},timeout=1,readOnly=true)
@Override
public void purchase(int userId, String isbn) {
//1.获取图书的价格
double bookPrice = bookShopDao.getBookPrice(isbn);
// System.out.println(bookPrice);
// double bookPrice2 = bookShopDao.getBookPrice(isbn);
// System.out.println(bookPrice2);
//2.更新图书的库存
bookShopDao.updateBookStock(isbn);
// int a = 10/0;
// //3.更新用户的余额
bookShopDao.updateAccountBanlance(userId, bookPrice);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 通过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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 设置自动扫描的包 -->
<context:component-scan base-package="com.atguigu.spring.transaction.xml"></context:component-scan>
<!-- 导入连接数据的有关信息的外部文件 -->
<context:property-placeholder location="classpath:db2.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
<property name="minPoolSize" value="${jdbc.minPoolSize}"></property>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
</bean>
<!-- 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务 -->
<tx:advice id="txAdvice">
<!-- 配置要添加事务的方法 -->
<tx:attributes>
<tx:method name="purchase" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!-- 配置AOP-->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.atguigu.spring.transaction.xml.BookShopServiceImpl.*(..))"
id="txPointCut"/>
<!-- 配置切入点与事务之间的关联关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>