AOP的基本认识

Aspect Oriented Programming,面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

AOP的实现原理

Spring内部是使用动态代理的方式实现AOP

  • JDK动态代理:只能对实现接口的类产生代理

    • 产生代理对象的方法:Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

      • Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,所以JDK动态代理只能对实现接口的类适用
  • Cglib动态代理:可以对没有实现接口的类产生代理
    • 继承这个类,生成该类的子类对象

      • 所以这个类不能是final修饰的(无法继承)
  • Spring实现AOP时,如果这个类实现了接口,默认使用JDK动态代理;如果这个类没实现接口,就使用Cglib产生代理

AOP的核心概念

  • 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
  • joinpoint:连接点,可以被拦截的点
    • Spring中只支持方法类型的连接点,所以在Spring中连接点指的就是可以被拦截到的方法
  • pointcut:切入点,对连接点进行拦截的定义,也就是真正被拦截的点
  • advice:方法层面的增强(也叫通知),就是指拦截到连接点之后要执行的代码

    • 前置通知
    • 后置通知
    • 环绕通知
    • 异常通知
    • 最终通知
  • introduction:引介,指的是类层面的增强 (用的比较少)
    • 在不修改代码的前提下,引介可以在运行期为类动态地添加一些方法或属性
  • target:目标对象,需要被代理的那个类,也就是需要被增强的那个类
  • proxy:代理对象,就是目标类被增强了之后就产生了一个结果代理对象
  • weave:织入,将通知(advice)应用到目标对象(target)并导致代理对象创建的过程
  • aspect:切面,是对横切关注点的抽象,其实就是切入点和通知的组合

AOP的简单使用

Spring有两套AOP开发的方式,一套是传统的Spring的AOP开发方式,十分繁琐,现在已经被弃用;目前Spring进行AOP开发使用的都是基于AspectJ的方式

XML方式

简单实现

  1. 导入相应jar,除spring开发的基础jar外,还应额外导入几个jar
    1. com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
    2. com.springsource.org.aopalliance-1.0.0.jar
    3. spring-aop-4.2.4.RELEASE.jar
    4. spring-aspects-4.2.4.RELEASE.jar  
  2. 编写目标类、切面类
    1. 切面类

      1. package com.qf.aop.demo;
      2.  
      3. public class MyAspectJ {
      4. public void advice() {
      5. System.out.println("========通知========");
      6. }
      7. }
    2. 目标类  
      1. package com.qf.aop.demo;
      2.  
      3. public interface TestDao {
      4. void add();
      5. void delete();
      6. void update();
      7. void query();
      8. }
      1. package com.qf.aop.demo;
      2.  
      3. public class TestDaoImpl implements TestDao {
      4.  
      5. @Override
      6. public void add() {
      7. System.out.println("add()");
      8. }
      9.  
      10. @Override
      11. public void delete() {
      12. System.out.println("delete()");
      13. }
      14.  
      15. @Override
      16. public void update() {
      17. System.out.println("update()");
      18. }
      19.  
      20. @Override
      21. public void query() {
      22. System.out.println("query()");
      23. }
      24.  
      25. }
  3. 配置文件编写
    1. 引入aop的约束

      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <beans xmlns="http://www.springframework.org/schema/beans"
      3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4. xmlns:aop="http://www.springframework.org/schema/aop"
      5. xsi:schemaLocation="
      6. http://www.springframework.org/schema/beans
      7. http://www.springframework.org/schema/beans/spring-beans.xsd
      8. http://www.springframework.org/schema/aop
      9. http://www.springframework.org/schema/aop/spring-aop.xsd">
      10.  
      11. </beans>
    2. 具体aop配置
      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <beans xmlns="http://www.springframework.org/schema/beans"
      3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4. xmlns:aop="http://www.springframework.org/schema/aop"
      5. xsi:schemaLocation="
      6. http://www.springframework.org/schema/beans
      7. http://www.springframework.org/schema/beans/spring-beans.xsd
      8. http://www.springframework.org/schema/aop
      9. http://www.springframework.org/schema/aop/spring-aop.xsd">
      10. <!-- 配置spring管理需要被增强的目标对象 -->
      11. <bean id="testDao" class="com.qf.aop.demo.TestDaoImpl"></bean>
      12. <!-- 配置spring管理切面对象 -->
      13. <bean id="aspect" class="com.qf.aop.demo.MyAspectJ"></bean>
      14. <!-- 配置aop,实现对目标类产生代理 -->
      15. <aop:config>
      16. <!-- expression:具体哪个类的哪个方法需要增强 -->
      17. <aop:pointcut expression="execution(* com.qf.aop.demo.TestDaoImpl.delete(..))" id="pointcut01"/>
      18. <!-- 配置切面 -->
      19. <aop:aspect ref="aspect">
      20. <!-- method:选择使用切面中的哪一个增强 -->
      21. <aop:after method="advice" pointcut-ref="pointcut01"/>
      22. </aop:aspect>
      23. </aop:config>
      24. </beans>
  4. 测试
    1. 测试类

      1. package com.qf.aop.demo;
      2.  
      3. import javax.annotation.Resource;
      4.  
      5. import org.junit.Test;
      6. import org.junit.runner.RunWith;
      7. import org.springframework.test.context.ContextConfiguration;
      8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
      9.  
      10. @RunWith(SpringJUnit4ClassRunner.class)
      11. @ContextConfiguration("classpath:applicationContext.xml")
      12. public class TestDemo {
      13. @Resource(name="testDao")
      14. private TestDao testDao;
      15.  
      16. @Test
      17. public void test() {
      18. testDao.add();
      19. testDao.delete();
      20. testDao.update();
      21. testDao.query();
      22. }
      23. }
    2. 测试结果
      1. add()
      2. delete()
      3. ========通知========
      4. update()
      5. query()

通知类型

  1. 前置通知:在目标方法之前进行操作

    • 切面的增强方法中可以获得连接点信息

      • 切面中的增强方法

        1. public void advice(JoinPoint joinPoint) {
        2. System.out.println("========通知========"+joinPoint);
        3. }
      • 配置文件
        1. <aop:before method="advice" pointcut-ref="pointcut01"/>
  2. 后置通知:在目标方法之后进行操作
    • 可以获得目标方法的返回值

      • 目标方法

        1. @Override
        2. public String delete() {
        3. System.out.println("delete()");
        4. return "testResult";
        5. }
      • 切面中的增强方法
        1. public void advice(Object obj) {
        2. System.out.println("========通知========"+obj);
        3. }
      • 配置文件,returning属性值和增强方法中的参数名称必须一致 
        1. <aop:after-returning method="advice" pointcut-ref="pointcut01" returning="obj"/>
  3. 环绕通知:在目标方法之前和之后都进行操作
    • 切面中的增强方法

      1. public Object advice(ProceedingJoinPoint pjp) throws Throwable {
      2. System.out.println("========环绕前通知========");
      3. Object object = pjp.proceed();
      4. System.out.println("========环绕后通知========");
      5. return object;
      6. }
    • 配置文件  
      1. <aop:around method="advice" pointcut-ref="pointcut01" />
  4. 异常通知:在目标方法出现异常时,进行操作
    • 可以获取异常信息

      • 目标方法中设置一个异常

        1. public String delete() {
        2. System.out.println("delete()");
        3. int a = 1/0;
        4. return "testResult";
        5. }
      • 切面中的增强方法
        1. public void advice(Throwable e) {
        2. System.out.println("======异常通知=="+e.getMessage());
        3. }
      • 配置文件  
        1. <aop:after-throwing method="advice" pointcut-ref="pointcut01" throwing="e"/>
  5. 最终通知:无论代码是否有异常,都会操作
    1. <aop:after method="advice" pointcut-ref="pointcut01" />  

切入表达式

  • 基于execution函数
  • 语法:[访问修饰符] 返回值 包名.类名.方法名(方法参数)
    • *:通配符,* com.qf.test.*.*(..)表示com.qf.test包下的所有类的所有方法
    • +:* com.qf.test.TestDao+.add(..)表示TestDao类和其子类下的add方法 
    • ..:* com.qf..*.*(..)表示com.qf包以及其子包下的所有类的所有方法

注解方式

简单实现

  1. 导入jar包,和XML方式的jar一致
  2. 编写目标类和切面类
    • 编写目标类

      1. package com.qf.aop.demo;
      2.  
      3. public class TestDaoImpl implements TestDao {
      4.  
      5. @Override
      6. public void add() {
      7. System.out.println("add()");
      8. }
      9.  
      10. @Override
      11. public String delete() {
      12. System.out.println("delete()");
      13. return "testResult";
      14. }
      15.  
      16. @Override
      17. public void update() {
      18. System.out.println("update()");
      19. }
      20.  
      21. @Override
      22. public void query() {
      23. System.out.println("query()");
      24. }
      25.  
      26. }
    • 编写切面类
      1. package com.qf.aop.demo;
      2.  
      3. import org.aspectj.lang.annotation.Aspect;
      4. import org.aspectj.lang.annotation.Before;
      5. import org.aspectj.lang.annotation.Pointcut;
      6.  
      7. @Aspect
      8. public class MyAspectJ {
      9. //方式一:
      10. /*@Before(value="execution(* com.qf.aop.demo.TestDaoImpl.query(..))")
      11. public void advice() {
      12. System.out.println("======before通知==");
      13. }*/
      14.  
      15. //方式二:
      16. @Pointcut(value="execution(* com.qf.aop.demo.TestDaoImpl.query(..))")
      17. private void pointcut() {};
      18. @Before("MyAspectJ.pointcut()")
      19. public void advice() {
      20. System.out.println("======before通知==");
      21. }
      22. }

          

  3. 编写配置文件
    • 配置Spring管理bean,开启AOP注解

      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <beans xmlns="http://www.springframework.org/schema/beans"
      3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4. xmlns:aop="http://www.springframework.org/schema/aop"
      5. xsi:schemaLocation="
      6. http://www.springframework.org/schema/beans
      7. http://www.springframework.org/schema/beans/spring-beans.xsd
      8. http://www.springframework.org/schema/aop
      9. http://www.springframework.org/schema/aop/spring-aop.xsd">
      10. <!-- 配置spring管理需要被增强的目标对象 -->
      11. <bean id="testDao" class="com.qf.aop.demo.TestDaoImpl"></bean>
      12. <!-- 配置spring管理切面对象 -->
      13. <bean id="aspect" class="com.qf.aop.demo.MyAspectJ"></bean>
      14.  
      15. <!-- 配置打开aop注解 -->
      16. <aop:aspectj-autoproxy/>
      17. </beans>

        

  4. 测试
    • 测试类

      1. package com.qf.aop.demo;
      2.  
      3. import javax.annotation.Resource;
      4.  
      5. import org.junit.Test;
      6. import org.junit.runner.RunWith;
      7. import org.springframework.test.context.ContextConfiguration;
      8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
      9.  
      10. @RunWith(SpringJUnit4ClassRunner.class)
      11. @ContextConfiguration("classpath:applicationContext.xml")
      12. public class TestDemo {
      13. @Resource(name="testDao")
      14. private TestDao testDao;
      15.  
      16. @Test
      17. public void test() {
      18. testDao.add();
      19. testDao.delete();
      20. testDao.update();
      21. testDao.query();
      22. }
      23. }
    • 测试结果
      1. add()
      2. delete()
      3. update()
      4. ======before通知==
      5. query()

        

        

三、spring的AOP的更多相关文章

  1. 深入浅出学习Spring框架(三):AOP 详解

    AOP的英文解释——AOPAspect Oriented Programming面向切面编程.主要目的是通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. 在反 ...

  2. Spring AOP源码分析(三)创建AOP代理

    摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.获取增强器 1. 普通增强器的获取 2. 增加同步实例化增强 ...

  3. 10 Spring框架 AOP (三) Spring对AspectJ的整合

    上两节我们讲了Spring对AOP的实现,但是在我们的开发中我们不太使用Spring自身的对AOP的实现,而是使用AspectJ,AspectJ是一个面向切面的框架,它扩展了Java语言.Aspect ...

  4. spring 学习(三):aop 学习

    spring 学习(三):aop 学习 aop 概念 1 aop:面向切面(方面)编程,扩展功能不修改源代码实现 2 AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码 3 aop底层使用动态代 ...

  5. 死磕Spring之AOP篇 - Spring AOP自动代理(三)创建代理对象

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  6. spring学习三:Spring的Aop、代理

    ref:https://mp.weixin.qq.com/s/J77asUvw8FcnF-6YlX6AAw AOP相关术语:    Joinpoint(连接点):类里面可以被增强的方法,这些方法称为连 ...

  7. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  8. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  9. spring的AOP

    最近公司项目中需要添加一个日志记录功能,就是可以清楚的看到谁在什么时间做了什么事情,因为项目已经运行很长时间,这个最初没有开来进来,所以就用spring的面向切面编程来实现这个功能.在做的时候对spr ...

随机推荐

  1. 一、表单和ajax中的post请求&&后台获取数据方法

    一.表单和ajax中的post请求&&后台获取数据方法 最近要做后台数据接收,因为前台传来的数据太过于混乱,所以总结了一下前台数据post请求方法,顺便写了下相对应的后台接收方法. 前 ...

  2. Springmvc上传过程中遇到的错误

    问题1: org.springframework.web.util.NestedServletException: Handler processing failed; nested exceptio ...

  3. 【学术篇】bzoj2440 [中山市选2011]完全平方数

    -题目の传送门- 题目大意: 找到第k个无平方因子数. 看到数据范围很大, 我们要采用比\(O(n)\)还要小的做法. 考虑如果前\(x\)个数中有\(k-1\)个无平方因子数, 而前\(x+1\)个 ...

  4. CS184.1X 计算机图形学导论(第四讲)

    一.齐次变换 1.平移变换 变换矩阵不能包含X,Y,Z等坐标变量 如果x坐标向右平移了5个单位长度,则x~=x+5.在变换矩阵中表示的时候添加一个w坐标变量.通过加入一个w坐标,可以实现平移变换 1& ...

  5. java 反转数组

    package java03; public class Demo05ArrayReversr { public static void main(String[] args) { int[] arr ...

  6. selenium原理(以百度搜索为例)

    1.首先导入 Selenium(webdriver)相关模块2.调用 Selenium 的浏览器驱动,获取浏览器句柄(driver)并启动浏览器.3.通过句柄访问百度 URL.4.通过句柄操作页面元素 ...

  7. 51nod 1253:Kundu and Tree(组合数学)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1253 所有的三元组的可能情况数有ans0=C(n,3).然后 ...

  8. linux的块设备层

    ll_rw_block()是块设备驱动层,向上提供按block读写块设备到某个内存地址的(是以page为目标单位)方法. bread()是块设备缓冲层,磁盘上的block用页缓存.先从这个缓存里找,找 ...

  9. iOS 常用代码之 UICollectionView

    记一下 不用每次都从0开始写,人生苦短 ,省点时间给自己 之前必须完成相关注册: . cell . 头部和尾部 [self.hotAndHistoryCollectionV registerNib:[ ...

  10. manjaro linux java环境配置

    大佬博客 yaourt jdk 不管怎么装就是会出错 #报错信息 Error: dl failure on line 597 Error: failed /usr/lib/jvm/java-12-op ...