@service注解_玩转Spring:纯手写实现Spring事务注解

我们在用Spring框架的时候,在service层完全不用担心事物的控制,一个注解就搞定一切,这个Transaction注解这么厉害,到底是怎么实现的呢,这篇博文来手写一个跟Spring的Transaction事务控制一样的注解。当然只是简单的实现事务的自动提交,回滚功能。

一、Spring事务注解的原理分析

  1. 这里能按自己的分析理解一下Transaction的实现原理,后面再根据这个原理来手写实现。
  2. 我们知道,jdbc操作数据库是经过、创建连接、开启事务、执行语句、提交事务这几个步骤来操作的,有异常的话就回滚事务。在我们编写代码过程中,往往是直接将这几个步骤耦合在代码里,这样子的话代码太过冗余且难以维护。此时在Spring中只要在Service层加上@Transaction注解就自动帮我们完成啦。
  3. 不管再怎么简单,肯定都经历过开启事务、提交事务、有异常回滚事务的操作。所以Spring在逻辑执行到service的具体方法时,肯定先开启事务,然后执行完后提交事务,有异常回滚事务,那么什么技术可以不侵入代码可以织入这些操作呢,很容易我们就想到了Spring AOP技术,通过SpringAOP技术在逻辑执行到service的具体方法的时候,先检查方法是否有@Transaction注解,如果有就开启事务,在执行完后就提交事务,有异常就回滚事务,这里我们当然想到了AOP的环绕通知和异常通知。

二、手写实现事务注解

1、实现基础

这里直接借助SpringAOP的技术来实现,可能有人会问,这个不就还是用Spring来实现了吗,为什么不是从0开始?我天,要是从0开始,自己先实现Spring的IOC,实现Spring的AOP,不要问我用什么技术实现IOC,AOP,IOC当然是直接读取配置文件或者注解,然后用反射技术来实现,这个以后我会手写IOC的,AOP的话就用动态代理的技术来实现,所以如果我真的要从0开始实现,那么不是简单的一篇博文可以实现的,最起码得写成一本书。

2、项目结构

这里用maven来搭建项目,然后引入c3p0和mysql-connector-java。项目结构如下:

565047ab4c564df78bf577e23db97970

文件作用

572dfa71d041487da47a8a7cd110aeeb

3、pom.xml

4.0.0cn.myforever  my-spring-transaction  0.0.1-SNAPSHOTorg.springframework            spring-core            3.0.6.RELEASEorg.springframework            spring-context            3.0.6.RELEASEorg.springframework            spring-aop            3.0.6.RELEASEorg.springframework            spring-orm            3.0.6.RELEASEorg.aspectj            aspectjrt            1.6.1aspectj            aspectjweaver            1.5.3cglib            cglib            2.1_2com.mchange            c3p0            0.9.5.2mysql            mysql-connector-java            5.1.37

引入Spring和数据库操作的依赖,这用的是MySQL数据库

4、Spring.xml

5、DAO操作类MyDao.java

@Repositorypublic class MyDao {    @Autowired    private JdbcTemplate jdbcTemplate;    public void add(String name,String age) {        String sql = "INSERT INTO user(username, age) VALUES(?,?);";        int updateResult = jdbcTemplate.update(sql, name, age);        System.out.println("updateResult:" + updateResult);    }}

这里首先得在MySQL数据库中建立user表,就两个字段

6、Service层MyService.java

@Servicepublic class MyService {    @Autowired    private MyTransaction myTransaction;    @Autowired    private MyDao myDao;    public void add() {        try {            myTransaction.begin();            myDao.add("1", "1");            int i =1/0;            myDao.add("2", "2");            myTransaction.commit();        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();            myTransaction.rollback();        }    }    @ExtTransaction    public void add1() {        myDao.add("1", "1");        int i =1/0;        myDao.add("2", "2");    }}

这里有两个方法,一个是没有加注解的方法,一个是使用了自定义注解的方法,两个方法实现的功能都一样,插入数据1,1后会遇到空指针异常,后面再插入2,2。若是调用第一个方法,我们是手动开启事务,然后执行成功后会提交事务,跑出异常后会回滚事务,第二个方法就直接@ExtTransaction注解。若是测试发现两个方法都不能插入数据,则表示自定义注解成功。

7、事务操作类MyTransaction.java

@Component@Scope("prototype") //每个事物都是新的实例,解决线程安全问题public class MyTransaction {    @Autowired    private DataSourceTransactionManager dataSourceTransactionManager;    //这里定义全局的,由于是多实例,所以这里没有关系    private TransactionStatus transactionStatus;    //开启事物    public void begin() {        System.out.println("开启事务");        transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());    }    //提交事务    public void commit() {        System.out.println("提交事务");        if(transactionStatus!=null) {            dataSourceTransactionManager.commit(transactionStatus);        }    }    //回滚事务    public void rollback() {        System.out.println("回滚事务");        if(transactionStatus!=null) {            System.out.println("1");            dataSourceTransactionManager.rollback(transactionStatus);        }    }}

@Component表示会自动实例化Bean放入Spring的上下文中;@Scope(“prototype”) 表示多例,这样子就表示每次操作都实例化新的对象,不会有线程安全问题。

8、自定义注解ExtTransaction.java

@Target({ElementType.METHOD})//只能用于方法@Retention(RetentionPolicy.RUNTIME)public @interface ExtTransaction {}

@Target({ElementType.METHOD})表示该注解只能用在方法上;@Retention(RetentionPolicy.RUNTIME)表示该注解在运行期有效。

9、切面类TransactionAop.java

@Component@Aspectpublic class TransactionAop {    @Autowired    private MyTransaction myTransaction;    @AfterThrowing("execution(* cn.myforever.service.*.*(..))")    public void afterThrowing() {        System.out.println("异常通知");        myTransaction.rollback();    }    @Around("execution(* cn.myforever.service.*.*(..))")    public void around(ProceedingJoinPoint pjp) throws Throwable {        System.out.println("坏绕通知前");        //判断方法上是否有ExtTransaction注解        MethodSignature ms = (MethodSignature) pjp.getSignature();        System.out.println(ms.getName());        ExtTransaction extTransaction = ms.getMethod().getDeclaredAnnotation(ExtTransaction.class);        System.out.println(extTransaction);        if(extTransaction!=null) {            //开启事务            System.out.println("----开启事务");            myTransaction.begin();        }        pjp.proceed();        System.out.println("环绕通知之后");        if(extTransaction!=null) {            //开启事务            System.out.println("----提交事务");            myTransaction.commit();        }    }}

这里自动注入了MyTransaction对象,因为是多例的,所以每次都会是新的对象,用了环绕通知和异常通知,环绕通知里,若是检测到方法上有@ExtTransaction注解,则会开启事务,在方法执行完后会提交事务,然后开启异常通知,有异常就回滚事务。若是需要了解自定义注解的使用,请参考博文Java注解详解。

10、启动类AppMySpringTransaction.java

public class AppMySpringTransaction {    public static void main(String[] args) {        ClassPathXmlApplicationContext app =new ClassPathXmlApplicationContext("spring.xml");        MyService myService =(MyService) app.getBean("myService");        //myService.add();        myService.add1();    }}

11、启动测试

启动测试后会输入如下日志

f14afcef1d754ca18ded4c62f49e409b

跟预期一模一样,先插入第一条记录,然后抛出异常,异常通知接收到后,回滚事务,然后我们去检查数据库,会发现一条记录也没有,表示事务控制成功。

结语

上面只是简单地实现了自定义事务,大家可以跟多的测试下不用事务的情况。还是蛮简单的,话说SpringAOP太强大了。

2e91a3e17af049178637e2e1e7e965a1
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值