美文网首页springboot Java学习笔记程序员
03_Spring_注解AOP编程&jdbcTempla

03_Spring_注解AOP编程&jdbcTempla

作者: 明天你好向前奔跑 | 来源:发表于2017-07-13 23:04 被阅读218次

    一、@Aspectj注解配置切面编程

    1.1. 搭建环境

    1. Spring基本包(4+2: beans,context,core,expression + logging+log4j)
    2. aop编程导入aop相关的包(2+2 : aop联盟,aspectj,aop联盟整合包,aspectj整合包)
    3. Spring-Junit测试:test
    
    --------------------------------------------------------------------------
    4. 导入log4j.properties日志配置文件,导入含有beans,context,aop约束的applicationContext.xml
    

    1.2. 使用注解完成aop编程

    1. 确定目标对象,注册bean
    2. 编写增强类,注册bean
    3. 使用注解配置切面和切入点
    4. 测试
    
    
    目标对象:UserServiceImpl / ProductService
    
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        @Override
        // 使用Spring的AOP编程增强
        public void save() {
            System.out.println("用户注册.........");
        }
    }
    
    //没有接口的目标对象bean
    @Service
    public class ProductService {
    
        //模拟添加商品..对该方法进行增强
        public void save() {
            System.out.println("ProductService.......添加商品...........");
        }
    }
    
    并在两个目标对象上使用@Service完成了bean的注解
    ---------------------------------------------------------------------------
    
    编写增强类,注册bean(MyAspect):
    
    //通知/增强类,对切入点进行增强
    @Component //注册bean,将增强类交给Spring管理
    @Aspect //将该类标识为切面类(该类里面有增强方法),相当于<aop:aspect ref="myAspect">
    public class MyAspect {
    
        //前置增强,参数value:写切入点表达式
        //相当于<aop:befoe method="writeLog" pointcut="bean(*Service)">
        @Before("bean(*Service)")
        public void writeLog() {
            System.out.println("开始写日志了......Spring..aop开发");
        }
    }
    
    
    -----------------------------------------------------------------------------
    
    到这里,目标bean的定义,增强类的定义以及切面的配置完成,但是还需要在核心配置文件中开启注解扫描与自动代理机制。
    
    <?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.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 开启注解扫描组件 -->
        <context:component-scan base-package="com.itdream.spring.aopanno"/>
        
        <!-- 开启AspectJ注解自动代理机制:
            作用:能自动扫描带有@Aspect注解的bean,将其作为aop增强配置,有点类似于aop-config
        -->
        <aop:aspectj-autoproxy/>
    </beans>
    
    -----------------------------------------------------------------------------
    
    测试:
    @RunWith(SpringJUnit4ClassRunner.class)//Spring整合Junit
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public class SpringTest {
    
        //注入测试bean
        @Autowired
        @Qualifier("userService")
        private UserService userService;
        
        @Autowired
        private ProductService productService;
        
        @Test
        public void test() {
            userService.save();
            System.out.println("======================");
            productService.save();
        }
    }
    

    img36.png

    补充:

    我们的aop代理是使用的Spring的内部代理机制,默认是如果有接口就优先对接口代理(jdk动态代理)。
    
    问题:如果目标对象有接口,能否只对实现类代理,而不对接口进行代理?可以!
    
    img37.png
    这里要使用子类的扩展方法,而接口中没有该方法,因为代理对象与目标对象不能互转,所以不能调用。
    这时可以使用类代理(cglib动态代理),只需要设置 proxy-target-class = true 
    
    img39.png

    1.3. 使用@Pointcut 定义切入点

    问题:如果直接在通知注解中写切入点表达式,会发生重复编写,后期不便于维护

    解决:

    在实际开发中,切入点都是单独定义维护的,如:
    * 使用xml定义切入点<aop:pointcut>
    * 使用注解单独定义切入点@Pointcut
    
    语法要求:
        切点方法:private void 无参数方法,方法名为切点名
    
    =============================================================================
    
    //通知/增强类,对切入点进行增强
    @Component
    @Aspect
    public class MyAspect {
    
        // 定义切入点,id/name就是方法名
        // 优点:方便统一维护
        @Pointcut("bean(*Service)") // 定义切入点表达式
        private void myPointcut() {
        }
    
        // @Before("bean(*Service)")
        // 建立切入点与通知之前的关联,完整类名.方法名
        // @Before("com.itdream.spring.aopanno.MyAspect.myPointcut()")
        @Before("myPointcut()") // 因为在类的内部,可简写
        public void writeLog() {
            System.out.println("开始写日志了......Spring..aop开发");
        }
    }
    

    测试结果:

    img40.png

    另外,一个通知方法也可以对多个切入点进行增强:

    img41.png

    二、Spring JdbcTemplate模板类

    2.1 概述

    Spring中有一系列XxxTemplate类,这些类主要是用于简化xxx的编程。

    Spring JdbcTemplate 是一个模板工具类,简化Jdbc编程 (类似 Apache DbUtils )

    img42.png
    JdbcTemplate 简化 jdbc编程
    HibernateTemplate 极大的简化 Hibernate编程
    

    2.2 JdbcTemplate快速入门-代码

    目标:使用Jdbc Template操作数据库。

    1. 搭建环境
        1. 4+2个Spring基本jar包:beans,core,context,expression+logging,log4j.
        
        2. 导入JDBC模板开发包(2个):spring-jdbc.jar , spring-tx.jar ,有耦合性,因此操作jdbc需要导入tx,操作事务需要导入jdbc
        
        3. 导入测试包:spring-test.jar
    
        4. 数据库驱动
        
        5. 导入log4j.properties配置文件和核心配置文件applicationContext.xml
    
    2. 代码
        1. 编写Service层、dao层操作数据库
    
    
    ===========================================================================
    
    dao层:
    
    public class UserDAO {
    
        //使用jdbc Template模板类操作数据库
        public void create() {
            //创建数据源(连接数据库),内置连接池,不建议生产环境下使用
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/spring");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
            
            //创建JdbcTemplate模板对象,简化JDBC代码
            JdbcTemplate jdbcTemplate = new JdbcTemplate();
            //设置数据源(连接池)
            jdbcTemplate.setDataSource(dataSource);
            jdbcTemplate.execute("create table user001 (name varchar(20))");
        }
    }
    
    service层:
    public class UserService {
        //业务层,调用dao层
        public void create() {
            UserDAO userDAO = new UserDAO();
            userDAO.create();
        }
    }
    

    以上是使用JdbcTemplate操作数据库的基本方式。

    2.3. JdbcTemplate的XML配置方式

    目标:将数据源和jdbcTemplate都交给Spring来管理。

    这里还是使用Spring的内置的连接池。

    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">
    
        <!--配置Spring内置数据源 -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/spring" />
            <property name="username" value="root" />
            <property name="password" value="root" />
        </bean>
    
        <!-- 注册JdbcTemplate模板类 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource" />
        </bean>
        
        <!-- 注册UserDAO对象 -->
        <bean id="userDAO" class="com.itdream.dao.UserDAO">
            <property name="jdbcTemplate" ref="jdbcTemplate"/>
        </bean>
        
        <!-- 注册UserService -->
        <bean id="userService" class="com.itdream.service.UserService">
            <property name="userDAO" ref="userDAO"/>
        </bean>
    </beans>
    
    注意:
        UserDAO中提供JdbcTemplate的声明以及setter方法,以供属性注入。
        UserService中提供UserDAO的声明以及setter方法,以供UserDAO注入。
        
    测试:
    @RunWith(SpringJUnit4ClassRunner.class)//整合Spring与Junit
    @ContextConfiguration(locations="classpath:applicationContext.xml")//为Spring容器指定配置文件
    public class SpringTest {
        
        @Autowired
        private UserService userService;
    
        @Test
        public void test() {
            userService.create();
            System.out.println("建表成功");
        }
    }
    

    2.4 使用JdbcDaoSupport简化开发

    img43.png
    上面我们使用xml配置,将数据源,模板类,UserDAO以及UserService交给了Spring管理,但是配置文件中写的代码有点多。
    
    Spring提供了JdbcDaoSupport来方便Dao中注入Jdbc Template.
    
    我们只需要继承JdbcDaoSupport,将这个dao类注册成bean,注入dataSource数据源即可。
    
    它会调用父类JdbcDaoSupport的setDataSource方法,将注入的DataSource传入,返回一个JdbcTemplate
    
    因此,我们在dao类中,直接获取父类配置了注入数据源的JdbcTemplate使用即可
    
    
    ============================================================================
    
    userDAO:(之前定义用于注入的JdbcTemplate代码就节省了)
    
    public class UserDAO extends JdbcDaoSupport {
    
        // Spring注入的DataSource会调用父类JdbcDaoSupport中的setDataSource方法
        // 该方法会return new JdbcTemplate(dataSource);这个dataSource就是配置文件中注入的dataSource\
        // 因此父类中就有了一个配置了注入数据源的JdbcTemplate,我们直接get父类的JdbcTemplate模板类使用即可
    
        // 使用jdbc Template模板类操作数据库
        public void create() {
            getJdbcTemplate().execute("create table user002 (name varchar(20))");
        }
    }
    
    
    UserService:
    
    public class UserService {
    
        private UserDAO userDAO;
    
        public void setUserDAO(UserDAO userDAO) {
            this.userDAO = userDAO;
        }
    
        // 业务层,调用dao层
        public void create() {
            userDAO.create();
        }
    }
    
    --------------------------------------------------------------------------
    配置文件applicationContext.xml:(不需要注册JdbcTemplate,直接向DAO中注入数据源dataSource即可)
    
    <?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">
    
        <!--配置Spring内置数据源 -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost:3306/spring" />
            <property name="username" value="root" />
            <property name="password" value="root" />
        </bean>
    
        <!-- 注册UserDAO对象 -->
        <bean id="userDAO" class="com.itdream.dao.UserDAO">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 注册UserService -->
        <bean id="userService" class="com.itdream.service.UserService">
            <property name="userDAO" ref="userDAO"/>
        </bean>
    </beans>
    

    测试:

    img44.png

    2.4 配置自定义连接池

    2.4.1 DBCP连接池的配置
    1.导入DBCP连接池所需的jar包(tomcat内置了apache dbcp的jar包)
        commons-pool-1.5.6.jar  commons-dbcp-1.4.jar
    
    2. 配置DBCP连接池(根据setter方法确定name)
    
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/spring" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>
    
    DBCP连接池的配置与Spring内置连接池的配置几乎一模一样,修改一下class即可。
    
    2.4.2 C3P0连接池的配置
    1.导入C3P0连接池所需的jar包
        com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
    
    2. 配置DBCP连接池(根据setter方法确定name)
    
    <!-- 配置C3P0数据源(连接池) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring" />
        <property name="user" value="root" />
        <property name="password" value="root" />
    </bean>
    

    2.5. 外部属性文件的引入配置

    上面我们进行了数据源(连接池)在Spring中的配置,我们有时候需要更换连接池,而配置文件的代码很多,修改起来不方便,因此将它的参数提取出来放在一个配置文件中。

    模拟需求:
    现在数据源的相关参数配置,是测试环境下的。
    现在,要将工程搭建在正式的服务器上,因为测试环境和正式环境的数据库肯定不是一个。
    所以肯定首先要更改数据源相关的配置。将数据源相关配置参数,外置。
    
    目的:可以将xml配置中可能要经常修改内容,抽取到一个properties文件 
    应用:使用properties文件配置参数,如数据库连接参数等。
    
    ------------------------------------------------------------------------------
    
    1. 从applicationContext.xml中抽取经常修改的变量
    例如:
        jdbc.dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
        jdbc.driverClass=com.mysql.jdbc.Driver
        jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring
        jdbc.username=root
        jdbc.password=root
    
    2. 在applicationContext.xml中读取抽取出去的变量
     
    首先要使用context:property-placeholder标签引入外界配置文件。
    
        1. 因此首先要导入,context有关的schema约束。
        2. 引入外界配置文件设置
        3. 引入变量
    
    <!--引入头约束 -->
    <?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 http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 引入外部配置文件 -->
        <context:property-placeholder location="classpath:db.properties"/>
        <!-- 配置C3P0数据源(连接池) -->
        <bean id="dataSource" class="${jdbc.dataSource}">
            <property name="driverClass" value="${jdbc.driverClass}" />
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
        ............................
    </beans>
    

    2.6. 使用Jdbc Template进行CRUD操作

    ProductDAO :
    
    //使用JDBC Template进行CRUD操作
    public class ProductDAO extends JdbcDaoSupport {
    
        // 继承了JdbcDaoSupport类,不需注入JdbcTemplate,只需要注入DataSource连接池即可
        // DAO中注入连接池DataSource,Spring会找到父类中的setDataSource,从而创建一个配置了注入连接池的JdbcTemplate
        // 因此在这里,我们可以直接调用父类的getJdbcTemplate()方法即可
    
        // 增加商品
        public void save(Product product) {
            String sql = "insert into product values(null,?,?)";
            getJdbcTemplate().update(sql, product.getPname(), product.getPrice());
        }
    
        // 删除商品
        public void delete(Product product) {
            String sql = "delete from product where pid=?";
            getJdbcTemplate().update(sql, product.getPid());
        }
    
        // 修改商品
        public void update(Product product) {
            String sql = "update product set price=? where pid=?";
            getJdbcTemplate().update(sql, product.getPrice(), product.getPid());
        }
    
        // 投影查询
        public Object findNameById(int pid) {
            String sql = "select pname from product where pid=?";
            return getJdbcTemplate().queryForObject(sql, new SingleColumnRowMapper<>(), pid);
        }
    
        // 投影查询
        public Object findColumnsById(int pid) {
            String sql = "select pname,price from product where pid=?";
            return getJdbcTemplate().queryForObject(sql, new ColumnMapRowMapper(), pid);
        }
    
        // 查询单个商品
        public Product query(int pid) {
            String sql = "select * from product where pid=?";
            return getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<>(Product.class), pid);
        }
    
        // 查询多商品
        public List<Product> queryProducts(String pname) {
            String sql = "select * from product where pname like ?";
            return getJdbcTemplate().query(sql, new BeanPropertyRowMapper(Product.class), "%" + pname + "%");
        }
        
        //查询所有商品
        public List<Product> findAll() {
            String sql = "select * from product";
            return getJdbcTemplate().query(sql, new BeanPropertyRowMapper(Product.class));
        }
    }
    
    ---------------------------------------------------------------------------
    
    配置文件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: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 http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 引入外部配置文件 -->
        <context:property-placeholder location="classpath:db.properties" />
        <!-- 配置C3P0数据源(连接池) -->
        <bean id="dataSource" class="${jdbc.dataSource}">
            <property name="driverClass" value="${jdbc.driverClass}" />
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- 注册productDAO对象 -->
        <bean id="productDAO" class="com.itdream.dao.ProductDAO">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- 注册Product的bean对象 -->
        <bean id="product" class="com.itdream.domain.Product">
            <property name="pid" value="1"/>
            <property name="pname" value="Iphone"/>
            <property name="price" value="6999D"/>
        </bean>
    </beans>
    
    ---------------------------------------------------------------------------
    
    测试:
    @RunWith(SpringJUnit4ClassRunner.class) // 整合Spring,否则不能注入测试bean
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class SpringTest {
    
        @Autowired
        private ProductDAO productDAO;
    
        @Autowired
        private Product product;
    
        @Test
        public void test() {
            // productDAO.save(product);
            // productDAO.delete(product);
            // product.setPid(2);
            // product.setPrice(2999D);
            // productDAO.update(product);
            
            System.out.println(productDAO.findNameById(2));
            System.out.println(productDAO.findColumnsById(2));
            System.out.println(productDAO.query(2));
            System.out.println(productDAO.queryProducts("one"));
            System.out.println(productDAO.findAll());
        }
    }
    
    =============================================================================
    

    总结:
    • 增删改的操作使用update
    • 简单查询使用```queryForObject````查询
      • 封装单个对象使用BeanPropertyRowMapper封装
      • 投影查询对象属性使用ColumnMapRowMapper
    • 复杂的查询使用query查询
      • 封装使用new BeanPropertyRowMapper封装

    三、 Spring的事务管理机制【了解】

    Spring事务管理高层抽象主要包括3个接口,Spring的事务主要是由他们共同完成的:

    • PlatformTransactionManager:事务管理器—主要用于平台相关事务的管理
    • TransactionDefinition: 事务定义信息(隔离、传播、超时、只读)—通过配置如何进行事务管理。
    • TransactionStatus:事务具体运行状态—事务管理过程中,每个时间点事务的状态信息。

    3.1. PlatformTransactionManager事务管理器

    该接口提供三个方法:

    • commit : 提交事务
    • rollback : 回滚事务
    • getTransaction : 获取事务状态

    Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现:

    img45.png
    • DataSourceTransactionManager针对JdbcTemplate、MyBatis事务控制 ,使用Connection(连接)进行事务控制 :

      • connection.setAutoCommit(false):开启事务
      • connection.rollback():回滚事物
      • connection.commit():提交事物
    • HibernateTransactionManager针对Hibernate框架进行事务管理, 使用Session的Transaction相关操作进行事务控制 :

      • session.beginTransaction():开启事务
      • session.getTransaction.commit:提交事务
      • session.getTransaction.rollback:回滚事务

    根据选择和使用的持久层技术,来选择对应的事务管理器。

    3.2. TransactionDefinition事务定义信息

    该接口主要提供的方法:

    • getIsolationLevel:隔离级别获取
    • getPropagationBehavior:传播行为获取
    • getTimeout:获取超时时间(事务的有效期)
    • isReadOnly: 是否只读。事务管理器能够根据这个返回值进行优化。
      • 保存、更新、删除—对数据进行操作-设置为false(默认值),变成可读写的,
      • 查询-设置这个属性为true,只能读不能写)

    这些事务的定义信息,都可以在配置文件中配置和定制。

    3.2.1. IsolationLevel事务的隔离级别
    img46.png

    Spring使用默认的defalut:根据数据库的默认隔离级别确定

    * MySQL的默认隔离级别是Repeatable_Read,可重复读
    * Oracle的默认隔离级别是Read_Commit,读已提交
    
    3.2.2. 事务的传播行为Transaction Propagation Behavior

    事务传播行为用于解决两个被事务管理的方法互相调用问题

    img47.png

    业务层两个方法面临的事务问题:有些时候需要处于同一个事务,有些时候不能在同一个事务 !

    事务的传播行为的7种类型:

    img48.png

    有关事务传播行为的详细解释:

    PROPAGATION_REQUIRED(默认值)
    A中有事务,使用A中的事务.如果A中没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
    PROPAGATION_SUPPORTS 
    A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.
    PROPAGATION_MANDATORY
    A中有事务,使用A中的事务.如果A没有事务.抛出异常.
    PROPAGATION_REQUIRES_NEW
    A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)
    PROPAGATION_NOT_SUPPORTED
    A中有事务,将A中的事务挂起.
    PROPAGATION_NEVER
    A中有事务,抛出异常.
    PROPAGATION_NESTED
    嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚
    

    为方便记忆,将Spring的事务传播行为分为三大类:

    **PROPAGATION_REQUIRED(默认值)**、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
    支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。
    事务默认传播行为 REQUIRED。最常用的。
    
    
    **PROPAGATION_REQUIRES_NEW**、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER 
    不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
    常用的事务传播行为:PROPAGATION_REQUIRES_NEW
    
    
    **PROPAGATION_NESTED**
    嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点
    
    
    面试题: REQUIRED、REQUIRES_NEW、NESTED 区分?
        REQUIRED 一个事务(默认,推荐)
        REQUIRES_NEW 两个事务 
        NESTED 一个事务,事务可以设置保存点,回滚到保存点 ,选择提交或者回滚
    

    3.3. TransactionStatus 事务状态

    • flush(),给hibernate使用,底层发出sql的
    • hasSavepoint():判断是否有保留点
    • isCompleted():判断事务是否结束
    • isNewTransaction():判断当前事务是否是新开的一个事务。
    • isRollbackOnly():判断事务是否只能回滚
    • setRollbackOnly():设置事务是否回滚

    数据库操作中,如果只是回滚,后面不操作,数据库在关闭连接的时候,自动发出了commit。commit结束事务。


    三个事务超级接口对象之间的关系:

    1. 用户管理事务,需要先配置TransactionDefinition(事务定义信息、事务的管理方案);
    2. 然后根据TransactionDefinition,通过TransactionManager(事务管理器)进行事务管理;
    3. 事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。

    3.4. Spring事务管理两种方式

    • 编程式的事务管理(有侵入性,很少用)
    • 使用XML或注解配置声明式事务【开发中使用】
      • Spring的声明事务通过AOP实现(环绕通知)
    3.4.1 使用XML配置声明式事务

    示例:

    需求:
        银行转账:转出、转入,使用Spring的声明式事务
    
    -------------------------------------------------------------------------
    
    步骤:
    1. 搭建环境
        1. 导入jar包
            Spring基本包:4+2 : beans,context,core,expression.  +  apache logging,log4j
    
            spring测试包: test
    
            spring事务包: tx,jdbc(jdbc与tx有耦合,使用其中一个包必须导入另一个)
    
            spring的事务依赖AOP(2+2):apache-aop联盟,aspect.  spring对两个包的整合包
    
            数据库驱动:mysql-converter
    
            c3p0连接池的jar包
        --------------------------------------------------------------------
    
        2. 导入log4j.properties配置文件,applicationContext.xml配置文件
        3. 完成数据库建表以及模拟的两个用户
        4. 根据表编写实体类User
    
    
    2. 完成功能
        1. 编写UserDAO的转入转出数据库操作(使用JdbcTemplate)
        2. 编写UserService完成业务逻辑操作
        3. 完成事务增强
            1. 确定目标类,注册bean
            2. 注册增强类
            3. 配置切面和切入点(切入点和通知之间的关联)
    
    
    3. 测试
    
    
    ===========================================================================
    

    1. 搭建环境:

    jar包:

    img49.png
        
        创建数据库表user:
        CREATE DATABASE spring;
        
        CREATE TABLE USER(
            uid INTEGER PRIMARY KEY AUTO_INCREMENT,
            username VARCHAR(20),
            acount DOUBLE
        );
        
        INSERT INTO USER VALUES(NULL,'tom',1000),(NULL,'jack',1000);
    
    
        创建实体类User:
        //User的实体类
        public class User {
        
            private Integer uid;
            private String username;
            private Double acount;
        
            public Integer getUid() {
                return uid;
            }
        
            public void setUid(Integer uid) {
                this.uid = uid;
            }
        
            public String getUsername() {
                return username;
            }
        
            public void setUsername(String username) {
                this.username = username;
            }
        
            public Double getAcount() {
                return acount;
            }
        
            public void setAcount(Double acount) {
                this.acount = acount;
            }
        
            @Override
            public String toString() {
                return "User [uid=" + uid + ", username=" + username + ", acount=" + acount + "]";
            }
        }
    
        
        导入log4j.properties,applicationContext.xml核心配置文件。
    
    

    2. 业务实现:

    
        UserDAO:
    
        //模拟银行转账
        public class UserDAO extends JdbcDaoSupport {
        
            // 注入dataSource父类会自动创建一个配置了注入数据源的JdbcTemplate
        
            // 转出
            public void out(String outname, Double money) {
                String sql = "update user set acount=acount-? where username=?";
                getJdbcTemplate().update(sql, money, outname);
            }
        
            // 转入
            public void in(String inname, Double money) {
                String sql = "update user set acount=acount+? where username=?";
                getJdbcTemplate().update(sql, money, inname);
            }
        }
    
        UserService:
    
        public class UserService {
        
            // 注入userDAO
            private UserDAO userDAO;
        
            // 提供setter方法
            public void setUserDAO(UserDAO userDAO) {
                this.userDAO = userDAO;
            }
        
            public void transfer(String outname, String inname, Double money) {
                //开启事务
                //转出
                userDAO.out(outname, money);
                
                //制造异常
                //int i = 1/0;
                //转入
                userDAO.in(inname, money);
                //关闭事务
            }
        }
    
        ---------------------------------------------------------------------------
    
        配置applicationContext.xml完成Spring的声明式事务:
    
        头约束:
            因为JdbcTemplate和事务管理器都需要导入数据源,因此配置了c3p0连接池,抽取了变量另外配置。
            为了能引入外部配置,使用context..placeholder,需要引入context头约束。
    
            因为要使用事务,因此需要导入aop和tx的头约束(spring的事务依赖aop的环绕通知完成)
    
        
            
        外部数据源的配置(db.properties):
            jdbc.dataSourceClass=com.mchange.v2.c3p0.ComboPooledDataSource
            jdbc.driverClassName=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/spring
            jdbc.username=root
            jdbc.password=root
    
    
        核心配置文件(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: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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
        
        
            <!-- 引入外部配置文件(首先引入Context头约束) -->
            <context:property-placeholder location="db.properties"/>
            
            <!-- 配置C3P0数据源,使用${key}引入外部配置文件中的变量 -->
            <bean id="dataSource" class="${jdbc.dataSourceClass}">
                <property name="driverClass" value="${jdbc.driverClassName}"/>
                <property name="jdbcUrl" value="${jdbc.url}"/>
                <property name="user" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </bean>
            
            <!-- 注册UserDAO -->
            <bean id="userDAO" class="com.itdream.dao.UserDAO">
                <!-- 注入数据源,获取配置了注入数据源的JdbcTemplate对象 -->
                <property name="dataSource" ref="dataSource"/>
            </bean>
            
            <!-- 注册UserService,也是目标bean -->
            <bean id="userService" class="com.itdream.service.UserService">
                <property name="userDAO" ref="userDAO"/>
            </bean>
            
            <!-- Spring的事务管理 -->
            <!-- 配置一个事务管理器,给通知advice进行注入使用 -->
            <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <!-- 因为不同的持久层,需要开启事务的连接不同,因此需要注入数据源(连接池)
                    事实上,DataSourceTransactionManager中有setDataSource()方法供注入
                 -->
                <property name="dataSource" ref="dataSource"/>
            </bean>
            
            <!-- 增强bean -->
            <!-- Spring提供的事务增强通知类,Spring为了简化配置,我们只需要使用tx:advice标签就相当于将该增强类注册了。
                    tx:advice等价于<bean id="txAdvcie" class="....MethodInterceptor..">
             -->
             <!-- trasaction-manager相当于:<property name="" 
              -->
            <tx:advice id="txAdvice" transaction-manager="transactionManager">
                <!-- 配置Spring的事务属性 -->  
                <tx:attributes>
                    <!-- 对指定方法设置事务属性
                    isolation:事务的隔离级别,不写,默认default,根据数据库的默认隔离级别
                    timeout:超时时间,默认-1,根据数据库的默认超时时间
                    propagation:事务传播状态,默认required,一个事务,一般不改
                    read-only:是否只读,默认false,可读写.增删改数据库时使用默认,查询时使用true
                    no-rollback-for:遇到什么异常不回滚,其他的都会滚
                    rollback-for:遇到什么异常就回滚,剩下的不回滚
                    一般就设置一下read-only
                    -->
                    <tx:method name="transfer" 
                    isolation="DEFAULT" timeout="-1"
                    propagation="REQUIRED" read-only="false"
                    no-rollback-for="" rollback-for=""
                    />
                </tx:attributes>
            </tx:advice>
            
            <!-- 配置切入点与切面,建立切入点与通知的关联 -->
            <aop:config>
                <!-- 定义切入点,即哪些方法需要被事务增强器进行增强 -->
                <aop:pointcut expression="execution(* com.itdream.service.UserService.transfer(..))" id="myPointcut"/>
                <!-- 定义切面,这里使用的旧的aop,不是新的aspectJ的aop 
                    只支持一对一的切入点与通知的关联
                -->
                <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
            </aop:config>
        </beans>
    
    
        ---------------------------------------------------------------------------
    
        测试:
        @RunWith(SpringJUnit4ClassRunner.class)//整合Spring进行测试
        @ContextConfiguration(locations="classpath:applicationContext.xml")//为Spring容器指定配置文件
        public class SpringTest {
            
            @Autowired
            //注入测试bean
            private UserService userService;
        
            @Test
            public void testTransfer() {
                //tom向jack转账1000元
                userService.transfer("tom", "jack", 100D);
                System.out.println("转账成功.............");
            }
        }
    

    3.4.2 使用注解配置声明式事务

    步骤:

    1. 确定目标类,方法(要被增强的方法,即切入点),注册bean
    2. 确定增强类(这里要完成事务管理),Spring已经帮我们实现了
    3. 配置切面关系(注解实现),在需要管理事务的方法或者类上面 添加@Transactional 注解  
    4. 配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)
    
    -----------------------------------------------------------------------------
    
    1. @Service将目标类 UserService注册成bean
    2. Spring已经帮我们实现了事务管理的增强类,无需手动实现并注册了
    3. 配置切面关系,@Transactional写在需要增强的方法或类上
    
    @Service("userService")
    public class UserService {
    
        // 注入userDAO
        @Autowired
        @Qualifier("userDAO")
        private UserDAO userDAO;
    
        //在需要管理事务的方法或类上面用@Transactional管理
        @Transactional
        public void transfer(String outname, String inname, Double money) {
            //转出
            userDAO.out(outname, money);
            
            //制造异常
            //int i = 1/0;
            //转入
            userDAO.in(inname, money);
        }
    }
    
    ----------------------------------------------------------------------------
    
    需要将UserDAO也注册成bean,将DataSource数据源传入,调用父类setDoataSource方法,创建一个JdbcTemplate,
    这样UserDAO就可以直接获取父类的JdbcTemplate使用了。
    
    但是,如果将@Autowired直接注解声明的对象上,Spring会自动创建一个setDataSource()方法,与父类的final冲突。
    因此自己定义一个set方法接收DataSource,调用父类的setDSataSource方法。
    
    @Repository("userDAO") // 将其注册成bean
    public class UserDAO extends JdbcDaoSupport {
    
        //使用注解@Autowired注入DataSource数据源会默认生成setter方法,setDataSource(Datasource ds)注入
        //但是父类的setDataSource是用final修饰的,不能重写,而且这样也无法将dataSource传入到setDataSource方法中
        //这样就无法生成配置了注入数据源的JdbcTemplate,因此我们随便写一个set方法,参数类型写DataSource调用父类的setter方法
        //@Autowired
        //private DataSource dataSource;
        
        @Autowired
        public void setSuperDataSource(DataSource dataSource) {
            super.setDataSource(dataSource);
        }
        
    
        // 转出
        public void out(String outname, Double money) {
            String sql = "update user set acount=acount-? where username=?";
            getJdbcTemplate().update(sql, money, outname);
        }
    
        // 转入
        public void in(String inname, Double money) {
            String sql = "update user set acount=acount+? where username=?";
            getJdbcTemplate().update(sql, money, inname);
        }
    }
    
    4. 配置注解驱动事务管理器
    
    <?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
    
    
        <!-- 开启注解扫描组件 -->
        <context:component-scan base-package="com.itdream" />
    
        <!-- 引入外部配置文件(首先引入Context头约束) -->
        <context:property-placeholder location="db.properties" />
    
        <!-- 配置C3P0数据源,使用${key}引入外部配置文件中的变量 -->
        <bean id="dataSource" class="${jdbc.dataSourceClass}">
            <property name="driverClass" value="${jdbc.driverClassName}" />
            <property name="jdbcUrl" value="${jdbc.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- 配置具体的事务驱动管理器:jdbc。
            因为不同的持久层方案,需要用到不同的连接来管理事务,因此需要传入数据源(如果是hibernate就传入sessionFactory) 
        -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 注册事务注解驱动 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>
        
    
    -------------------------------------------------------------------------
    
    1. 事务的相关策略:
        isolation,read-only...可以直接在@Transactional后配置
    
    2. 如果在类上标识@Transactional,那么会对该类中的所有public方法都包装事务,等同于所有的public方法上面
    都添加了@Transactional。
    如果某个方法需要单独的事务定义,需要在方法上加@Transactional来覆盖类上的标注声明。
    
    img50.png

    xml和注解的选择:

    XML方式,集中式维护。使用XML进行事务管理 属性集中配置,便于管理和维护

    注解方式,使用@Transactional注解进行事务管理,配置太分散

    因此优选使用XML进行事务管理。


    事务的策略通配符设置:

    img51.png

    xml方式与注解方式管理事务小结:

    img52.png img53.png

    今日总结:

    以后直接使用即可。

    1. 注解AOP编程:

    1. 确定目标类bean
    2. 确定增强(通知)类,bean
    3. 使用@Aspect定义切面,使用@Pointcut定义切入点。
    在对应的增强方法上使用@Before,@Around等完成切入点与通知的关联
    
    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: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.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 开启注解扫描组件 -->
        <context:component-scan base-package="com.itdream.spring.aopanno"/>
    
        <!-- 开启AspectJ注解自动代理机制:
            作用:能自动扫描带有@Aspect注解的bean,将其作为aop增强配置,有点类似于aop-config
        -->
        <aop:aspectj-autoproxy/>
    </beans>
    
    这里如果想使用cglib的代理,还可以在 <aop:aspectj-autoproxy/>内设置proxy-target-class="true"
    

    2. JdbcTemplate

    为简化数据库操作,Spring提供了JdbcTemplate模板类,为了简化JdbcTemplate的开发,Spring提供了JdbcDaoSupport。
    Dao层继承该类,传入对应数据源就可以直接获取对应的Template操作数据库。
    JdbcTemplate/HibernateTemplate...
    
    ---------------------------------------------------------------------------
    1. db.properties外部配置文件
            jdbc.dataSource=com.mchange.v2.c3p0.ComboPooledDataSource
            jdbc.driverClass=com.mysql.jdbc.Driver
            jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring
            jdbc.username=root
            jdbc.password=root
    
        2. 在applicationContext.xml中读取抽取出去的变量
         
        首先要使用context:property-placeholder标签引入外界配置文件。
    
            1. 因此首先要导入,context有关的schema约束。
            2. 引入外界配置文件设置
            3. 引入变量
    
        <!--引入头约束 -->
        <?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 http://www.springframework.org/schema/context/spring-context.xsd">
        
            <!-- 引入外部配置文件 -->
            <context:property-placeholder location="classpath:db.properties"/>
            <!-- 配置C3P0数据源(连接池) -->
            <bean id="dataSource" class="${jdbc.dataSource}">
                <property name="driverClass" value="${jdbc.driverClass}" />
                <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
                <property name="user" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </bean>
            ............................
        </beans>
    

    3. 声明式事务管理

    两种方式:

    • XML配置进行事务管理
    • 注解配置事务管理

    注解配置进行事务管理:

    1. 将目标类定义成bean即可.增强类我们不需自己实现。
    2. 在需要进行事务管理的方法/类上使用@Transactional(这里可以配置事务策略)
    3. 配置注解事务管理器
    
    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: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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">
    
    
        <!-- 开启注解扫描组件 -->
        <context:component-scan base-package="com.itdream" />
    
        <!-- 引入外部配置文件(首先引入Context头约束) -->
        <context:property-placeholder location="db.properties" />
    
        <!-- 配置C3P0数据源,使用${key}引入外部配置文件中的变量 -->
        <bean id="dataSource" class="${jdbc.dataSourceClass}">
            <property name="driverClass" value="${jdbc.driverClassName}" />
            <property name="jdbcUrl" value="${jdbc.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- 配置具体的事务驱动管理器:jdbc。
            因为不同的持久层方案,需要用到不同的连接来管理事务,因此需要传入数据源(如果是hibernate就传入sessionFactory) 
        -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!-- 注册事务注解驱动 -->
        <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>
    

    XML配置事务管理:【推荐】

    1. 确定目标类,注册bean 
    2. 确定增强类,注册bean(Spring已经实现,只需注册该则增强类即可)
    3. 配置事务管理器(Spring实现的增强类需要注入事务管理器)
    4. 配置切面(切入点与通知的关联)
    
    
    外部配置文件db.properties:
    jdbc.dataSourceClass=com.mchange.v2.c3p0.ComboPooledDataSource
    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/spring
    jdbc.username=root
    jdbc.password=root
        
    
    applicationContext.xml配置文件最终版:
    
    <!-- 引入外部配置文件(首先引入Context头约束) -->
    <?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    
        <!-- 引入外界配置属性 -->
        <context:property-placeholder location="db.properties"/>
    
        <!-- 配置数据源(连接池),根据选择的不同连接池确定具体的class以及name -->
        <bean id="dataSource" class="${jdbc.dataSourceClass}">
            <property name="driverClass" value="${jdbc.driverClassName}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
        
        <!-- 事务管理 -->
        <!-- 1.注册增强类,2.注入事务管理器,3.确定事务策略 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transfer"/>
                <tx:method name="save*"/>
                <tx:method name="delete*"/>
                <tx:method name="update*"/>
                <tx:method name="find*" read-only="true"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 注册事务管理器,注入数据源,提供连接管理事务 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 配置切面(切入点与通知的关系) -->
        <aop:config>
            <!-- 定义切入点 -->
            <aop:pointcut expression="execution(* com.itdream.service.UserService.transfer(..))" id="myPointcut"/>
            
            <!-- 配置切入点和通知的关联 -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
        </aop:config>
        
        <!-- 注册UserDAO -->
        <bean id="userDAO" class="com.itdream.dao.UserDAO">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 注册UserService-->
        <bean id="userService" class="com.itdream.service.UserService">
            <property name="userDAO" ref="userDAO"/>
        </bean>
    </beans>

    相关文章

      网友评论

        本文标题:03_Spring_注解AOP编程&jdbcTempla

        本文链接:https://www.haomeiwen.com/subject/qiwghxtx.html