AOP应用——事务管理

来源:互联网 发布:易语言游戏多开器源码 编辑:程序博客网 时间:2024/06/06 19:35

相信大家对Spring的事务管理并不陌生,再推荐一篇AOP切入点表达式的文章,我把重点放在AOP实现上。在项目开发过程中通常用Hibernate或MyBatis(iBatis)作为持久层,因此Demo基于Spring+MyBatis实现转账业务。时间很宝贵,先上代码。
spring-mybatis.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:tx="http://www.springframework.org/schema/tx" 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/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">    <!--service扫描目录配置-->    <!-- name-generator:解决类名相同时ioc容器有beanid相同的问题-->    <context:component-scan base-package="db.mybatis" name-generator="extend.CustomBeanNameGenerator">        <context:include-filter type="aspectj" expression="db.mybatis.service..*"/>    </context:component-scan>    <context:property-placeholder location="jdbc.properties"/>    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">        <property name="user" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>        <property name="jdbcUrl" value="${jdbc.url}"/>    </bean>    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">        <property name="dataSource" ref="dataSource"/>        <property name="mapperLocations" value="classpath:db/mybatis/mappers/**/*.xml"/>    </bean>    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">        <constructor-arg ref="sqlSessionFactory"/>    </bean>    <!-- dao扫描配置-->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="basePackage" value="db.mybatis.dao"/>        <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>    </bean>    <!--事务管理-->    <bean id="dbTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <constructor-arg name="dataSource" ref="dataSource"/>    </bean>    <aop:config proxy-target-class="true">        <aop:advisor advice-ref="transactionInterceptor" pointcut="execution(* db.mybatis.service..*Impl.*(..))"/>    </aop:config>    <tx:advice id="transactionInterceptor" transaction-manager="dbTransactionManager">        <tx:attributes>            <tx:method name="add*" propagation="REQUIRED"/>            <tx:method name="insert*" propagation="REQUIRED"/>            <tx:method name="save*" propagation="REQUIRED"/>            <tx:method name="update*" propagation="REQUIRED"/>            <tx:method name="modify*" propagation="REQUIRED"/>            <tx:method name="edit*" propagation="REQUIRED"/>            <tx:method name="delete*" propagation="REQUIRED"/>            <tx:method name="remove*" propagation="REQUIRED"/>            <tx:method name="get*" read-only="true" propagation="SUPPORTS"/>            <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>            <tx:method name="load*" read-only="true" propagation="SUPPORTS"/>            <tx:method name="search*" read-only="true" propagation="SUPPORTS"/>            <tx:method name="*" propagation="SUPPORTS"/>        </tx:attributes>    </tx:advice></beans>

项目目录结构如下
项目目录结构
这一部分要完成的任务就是上面那些了,我们开始动手吧。
1、开发所需jar包
建议用maven或Gradle(eclipse暂不支持)管理。如果用maven管理,只要添加以下依赖。

<dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>3.4</version>        </dependency>        <dependency>            <groupId>log4j</groupId>            <artifactId>log4j</artifactId>            <version>1.2.17</version>        </dependency>                     <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.12</version>            <scope>test</scope>        </dependency>        <!-- mysql驱动 -->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>5.1.6</version>        </dependency>                      <!-- spring -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-core</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-expression</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jdbc</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-tx</artifactId>            <version>4.2.0.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aspects</artifactId>            <version>4.2.1.RELEASE</version>        </dependency>        <!-- spring end -->        <!-- c3p0 -->        <dependency>            <groupId>c3p0</groupId>            <artifactId>c3p0</artifactId>            <version>0.9.1.2</version>        </dependency>

2、配置spring-mybatis.xml
配置完之后和最开始放上去是一样的,这里主要对一些配置做说明:
(1)<context:component-scan>里的name-generator属性
默认情况下IOC容器是根据类名来命名的,不同模块相似的功能很多类名是一样的,这时候beanid不唯一,项目不能正常启动。
配置的类代码如下:

 public class CustomBeanNameGenerator implements BeanNameGenerator {    @Override    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {        return definition.getBeanClassName().replaceAll("\\.", "").replace("Impl","");    }}

(2)<context:include-filter>
其type类型有五种,建议使用aspejct和regex。这样你不需要在每个类加类似@Service这样的注解;在不同的上下文中扫描目录明确。
(3) <tx:method>
推荐一篇文章,请点击<tx:method>标签。
3、开始开发
dao层的xml和接口

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="db.mybatis.dao.AccountDao">    <resultMap id="BaseResultMap" type="db.mybatis.model.Account">        <id column="id" property="id"/>        <result column="user" property="user"/>        <result column="money" property="money"/>    </resultMap>    <select id="getRecords" resultMap="BaseResultMap">    SELECT id,user,money FROM account    </select>    <select id="getMoneyByUser" resultType="double">        SELECT money FROM account where user = #{user}    </select>    <update id="updateMoneyByUser">        update account set money=${money}  where user = #{user}    </update></mapper>
public interface AccountDao {    List<Account> getRecords();    double getMoneyByUser(String user);    int updateMoneyByUser(@Param("money")double money,@Param("user")String user);}

service的接口和实现类

public interface AccountService {    /**     * 查询所有的人员记录     * @return     */    List<Account> getRecords();    /**     * 转账     * @param user1 转钱的人     * @param user2 收钱的人     * @param money 转出去的钱     * @return     */    int transAccount(String user1,String user2,double money);}
public class AccountServiceImpl implements AccountService {    @Resource    private AccountDao accountDao;    @Override    public List<Account> getRecords() {        return accountDao.getRecords();    }    @Override    public int transAccount(String user1, String user2, double money) {        int result = 0;        double money1 = accountDao.getMoneyByUser(user1);        if (money1 > money) {            result += accountDao.updateMoneyByUser(money1 - money, user1);            double money2 = accountDao.getMoneyByUser(user2);            result += accountDao.updateMoneyByUser(money2 + money, user2);        } else {            System.out.println(user1 + "的余额不足");        }        return result;    }}

测试类

public class UnitTestBase {    private ClassPathXmlApplicationContext context;    private String springXmlPath;    public UnitTestBase(String springXmlPath) {        this.springXmlPath = springXmlPath;    }    @Before    public void before() {        if (StringUtils.isEmpty(springXmlPath)) {            springXmlPath = "classpath:applicationContext.xml";        }        try {            context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\\s]+]"));            context.start();        } catch (BeansException e) {            e.printStackTrace();        }    }    @After    public void after() {        if (context != null)            context.destroy();    }    protected <T extends Object> T getBean(String beanId) {        try {            return (T) context.getBean(beanId);        } catch (BeansException e) {            e.printStackTrace();            return null;        }    }    protected <T extends Object> T getBean(Class<T> clazz) {        try {            return context.getBean(clazz);        } catch (BeansException e) {            e.printStackTrace();            return null;        }    }}
public class TestDB extends UnitTestBase {    public TestDB() {        super("classpath*:spring-mybatis.xml");    }    @Test    public void testDB() throws Exception {        AccountService accountService = getBean("dbmybatisserviceimplAccountService");        System.out.println("转账前:");        List<Account> records = accountService.getRecords();        printCollection(records);        int transCount = accountService.transAccount("张三", "李四", 200);        System.out.println("转账后:");        records = accountService.getRecords();        printCollection(records);        System.out.println("更新记录总数:" + transCount);    }    private void printCollection(Collection<?> collection) {        Iterator<?> iterator = collection.iterator();        while (iterator.hasNext()) {           System.out.println(iterator.next().toString());        }    }}

第一次执行结果如下:
第一次执行结果
现在将AccountServiceImpl中transAccount方法改为

 @Override    public int transAccount(String user1, String user2, double money) {        int result = 0;            double money1 = accountDao.getMoneyByUser(user1);            if (money1 > money) {                result += accountDao.updateMoneyByUser(money1 - money, user1);                result += 1 / 0;                double money2 = accountDao.getMoneyByUser(user2);                result += accountDao.updateMoneyByUser(money2 + money, user2);            } else {                System.out.println(user1 + "的余额不足");            }        return result;    }

再把测试方法改为

AccountService accountService = getBean(AccountService.class);        System.out.println("转账前:");        List<Account> records = accountService.getRecords();        printCollection(records);        int transCount = 0;        try {            transCount = accountService.transAccount("张三", "李四", 200);        } catch (Exception e) {            System.out.println("转账失败:"+e.getClass().getName()+"    "+e.getCause());        }        System.out.println("转账后:");        records = accountService.getRecords();        printCollection(records);        System.out.println("更新记录总数:" + transCount);

再测试下我们的程序,结果如下:
第二次运行结果
这时候张三转出去了200,但李四没收到200
我们在spring-mybatis.xml配置文件中<tx:attributes>加上<tx:method name="transAccount" propagation="REQUIRED"/>
再测试下我们的程序,结果如下:
第三次运行结果
AOP通过配置XML在事务管理的应用到此结束。
4、事务管理注解实现
在spring-mybatis.xml文件中添加标签<tx:annotation-driven transaction-manager="dbTransactionManager"/>开启注解事务,只需在需要加事务的类或方法上加注解@Transactional即可。
说明:
注解跟AOP配置文件实现可共存,也可只使用一个;
建议只使用一个,而且使用注解,因为在开发过程中aspectj配置很容易无效,而注解的代理模式(默认模式)不会出现这个问题。关于这个问题请阅读,我测试只用一个文件还是遇到了这样的问题。

1 0
原创粉丝点击