深入理解Spring事务和Spring AOP

什么是AOP编程

  1. Aspect Oriented Programming 面向切面编程
  2. 主要应用场景是日志记录,权限控制,性能监控,事务处理,异常处理等
  3. 主要意图是将日志记录,权限控制,性能监控,事务处理,异常处理等代码从业务逻辑代码中抽取出来,降低耦合性并解决代码复用的问题
  4. 底层实现原理是代理设计模式,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能.假设把应用程序想成一个立体结构的话,OOP的利刃是纵向切入系统,把系统划分为很多个模块(如: 用户模块,文章模块等等),而AOP的利刃是横向切入系统,提取各个模块可能都要重复操作的部分(如: 权限检查,日志记录等等).由此可见,AOP是OOP的一个有效补充

名词解释

  1. 关注点: 重复代码(如打印日志的代码)
  2. 切面/切面类: 关注点形成的类,将重复代码抽取出来,在运行的时候在业务方法上动态植入
  3. 切入点: 执行目标对象方法,动态植入切面代码(可以通过切入点表达式,指定拦截哪些类的哪些方法;给指定的类在运行的时候植入切面类代码)

AOP底层实现原理

代理的分类:

静态代理和动态代理

静态代理和动态代理的区别:

静态代理是静态定义代理类(需要手写代理类);动态代理是动态生成代理类(虚拟生成代理类)

动态代理分为:

jdk动态代理和cglib动态代理

jdk动态代理和cglib的区别:

jdk是基于反射机制实现的;
cglib是基于ASM字节码包装的一个类库(性能比反射要高一些)(ASM是一个java字节码操控框架,它能被用来动态生成类或者增强既有类的功能.ASM可以直接产生二进制class文件,也可以在类被加载进java虚拟机之前动态改变类的行为.)

  1. AOP实现原理: 静态代理和动态代理

  2. 什么是静态代理: 由程序员创建或工具生成代理类的源码,再编译代理类.所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了.

    //接口
    public interface IUserDao {
        void save();
    }
    
    //被代理类
    public class UserDaoImpl implements IUserDao {
    
        @Override
        public void save() {
            System.out.println("假装把数据入库了...");
        }
    }
    
    //代理类
    public class UserDaoProxy implements IUserDao {
        private IUserDao target;
    
        public UserDaoProxy(IUserDao userDao) {
            this.target = userDao;
        }
    
        @Override
        public void save() {
            System.out.println("开启事务...");
            target.save();
            System.out.println("提交事务...");
        }
    }
    
  3. 什么是动态代理: 代理类不需要实现接口,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

  • jdk动态代理(原理是根据类加载器和接口创建代理类,此代理类是接口的实现类,所以必须使用接口,面向接口生成代理,接口位于java.lang.reflect包下)

    public class InvocationHandlerImpl implements InvocationHandler{
    
        // 这其实是业务实现类对象,用来调用具体的业务方法
        private Object target;
    
        // 通过构造函数传入目标对象
        public InvocationHandlerImpl(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Object result = null;
            System.out.println("调用开始处理---");
            result = method.invoke(target, args);
            System.out.println("调用结束处理---");
            return result;
    
        }
    
        public static void main(String[] args){
            // 被代理对象
            IUserDao userDao = new UserDaoImpl();
            InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
            ClassLoader loader = userDao.getClass().getClassLoader();
            Class<?>[] interfaces = userDao.getClass().getInterfaces();
            // 主要装载器、一组接口及调用处理动态代理实例
            IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
            newProxyInstance.save();
    
        }
    
    }
    
  • cglib动态代理(原理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理)

    public class CglibProxy implements MethodInterceptor {
    
        private Object targetObject;
        // 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
        public Object getInstance(Object target) {
            // 设置需要创建子类的类
            this.targetObject = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("开启事务..");
            Object result = proxy.invoke(targetObject, args);
            System.out.println("关闭事务..");
            // 返回代理对象
            return result;
        }
        
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
            UserDaoImpl userDao = (UserDaoImpl) cglibProxy.getInstance(new UserDaoImpl());
            userDao.save();
        }
    }
    
    
  • jdk动态代理和cglib动态代理的区别

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    Spring中:

    如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP ;
    如果目标对象实现了接口,可以强制使用CGLIB实现AOP ;
    如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换 ;
    JDK动态代理只能对实现了接口的类生成代理,而不能针对类 ;
    CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ;
    因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

AOP编程使用

  1. 注解版本实现AOP
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 开启事物注解权限

    @Component //交给Spring来处理
    @Aspect    //指定一个类为切面类
    public class AopLog {
        // 前置通知
        @Before("execution(* com.pliu.service.UserService.add(..))")
        public void begin() {
            System.out.println("前置通知");
        }
    
        // 后置通知
        @After("execution(* com.pliu.service.UserService.add(..))")
        public void commit() {
            System.out.println("后置通知");
        }
    
        // 运行通知
        @AfterReturning("execution(* com.pliu.service.UserService.add(..))")
        public void returning() {
            System.out.println("运行通知");
        }
    
        // 异常通知
        @AfterThrowing("execution(* com.pliu.service.UserService.add(..))")
        public void afterThrowing() {
            System.out.println("异常通知");
        }
    
        // 环绕通知
        @Around("execution(* com.pliu.service.UserService.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕通知开始");
            proceedingJoinPoint.proceed();
            System.out.println("环绕通知结束");
        }
    
    }
    
    
  1. xml版本实现AOP(不好意思,感觉麻烦懒得写,如有必要还请百度吧)

Spring事务使用

  1. 事务基本特性

    ⑴ 原子性(Atomicity)
      原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

    ⑵ 一致性(Consistency)
     一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
      拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

    ⑶ 隔离性(Isolation)
      隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
      即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

    ⑷ 持久性(Durability)
      持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
    例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

  1. 事务控制分类
  • 编程式事务: 手动事务(自己去begin,commit,rollback),灵活但繁琐

  • 声明式事务: 自动事务,原理是使用编程式事务+反射机制进行包装.分注解版本和xml版本

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容