Spring—Aop面向切面编程

Spring—Aop面向切面编程

一、动态代理

1.实现方式(面试重点)

Ⅰ、jdk动态代理

  • 使用jdk中的Proxy,Mathod,InvocationHander创建代理对象
  • jdk动态代理要求目标类必须实现接口

Ⅱ、cglib动态代理

  • 第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类,子类就是代理对象
  • 要求目标类不能是final的,方法也不能是final的

2.动态代理的作用(面试重点)

  1. 在目标类源代码不改变的情况下, 增加动能
  2. 减少代码的重复
  3. 专注业务逻辑代码
  4. 解耦合,让业务功能和日志,事务非业务功能分离

3.AOP

  • 面向切面编程
  • 基于动态代理的,可以使用jdk,cglib两种代理方式
  • AOP是动态代理的规范化,把动态代理的实现步骤,方式都定义好,让开发人员用一种统一的方式,使用动态代理

二、AOP概述

A:切面,给目标类增加的功能,就是切面

O:面向

P:编程

1.怎么理解面向切面编程(面试重点)

  1. 需要在分析项目功能时,找出切面
  2. 合理安排切面的执行时间(在目标前还是目标后)
  3. 合理安排切面的质执行位置,在那个类,那个方法增加增强功能

2.术语

  1. Aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能(非业务功能)

    ​ 常见的切面功能有日志,事务,统计信息,参数检查,权限验证

  2. JoinPoint:连接点,连接业务方法和切面的位置,就某类中的业务方法

  3. Pointcut:切入点,指多个连接点方法的集合。多个方法

  4. 目标对象:给那个类的方法增加功能,这个类就是目标对象

  5. Advice:通知,通知表示切面功能更执行的时间

3.切面三要素

  1. 切面功能的代码,切面干什么
  2. 切面的执行位置,使用Pointcut表示切面执行的位置
  3. 切面的执行时间,使用Advice表示时间,在目标方法之前还是目标方法之后

三、AOP的实现

AOP是一个规范,是动态的一个规范化,一个标准

1.AOP的技术实现框架

Ⅰ、spring

  • spring内部实现了AOP规范,能做AOP的工作
  • spring主要在事务处理时使用AOP
  • 项目开发中很少使用spring的AOP实现,因为spring的AOP比较笨重

Ⅱ、aspectJ

一个开源的专门做AOP的框架,spring框架中集成了aspectJ框架,通过spring就能使用aspectJ的功能

实现方法:

  1. 使用xml的配置文件:配置全局事务
  2. 使用注解,项目中要做AOP功能,一般使用注解,aspectJ有5个3注解

2.aspectJ的使用

  1. 切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)

    在aspectJ框架中使用注解表示的,也可以使用xml配置文件中的标签

    • @Before
    • @AfterReturning
    • @Around
    • @AfterThrowing
    • @After

3.使用aspectj框架实现aop

  • 使用aop:目的是给已经存在的一些类和方法,增加额外的功能。前提是不改变原来的类的代码

Ⅰ、使用abjectj实现aop的步骤

  1. 新建maven项目

  2. aspectj依赖,spring依赖,junit单元测试

  3. 创建目标类:接口和实现类

    要做的是给类中的方法增加功能

  4. 创建切面类:普通类

    1. 在类的上面加入@Aspect

    2. 在类定义方法,方法就是切面要执行的功能代码

      在方法的上面加入aspect中的通知注解,例如@Before,有需要指定的切入点表达式execution()

  5. 创建spring的配置文件:声明对象,把对象交给容器统一管理,

    声明对象,可以使用注解或者xml配置文件

    1. 声明目标对象

    2. 声明切面对象

    3. 声明abjectj框架中的自动代理生成器标签

      自动代理生成器:用来完成代理对象的自动创建功能

  6. 创建测试类,从spring容器中获取目标对象(实际就是代理对象),通过代理执行方法,实现aop的功能增强

Ⅱ、前置通知

1)@Aspect
  • @Aspect 是aspectj框架中的注解

  • 切面类,是用来给业务方法增加功能的类,在这个类中有切面类的功能代码

  • 位置:在类定义的上面

  • 定义方法,方法是实现切面的功能

    • 公共方法 public
    • 方法没有返回值
    • 方法名称自定义
    • 方法可以有参数,也可以没有,如果有参数,参数不是自定义的,有几个参数类型可以使用
    @Aspect
    public class MyAspect {
          
          
        ...............
    }
    
2)@Before:前置通知注解
  • @Before:前置通知注解

  • 属性:value,是切入点表达式,表示切面的功能执行的位置

  • 位置:在方法的上面

  • 特点

    • 在目标方法之前执行的
    • 不会改变目标方法的执行结果
    • 不会影响目标方法的执行
  • 一个业务方法可以同时加入好几个通知

  • 使用方法自己定义

    @Before(value = "execution(public void nuc.gjq.nuc01.SomeServiceImpl.doSome(String,int))")
    @Before(value = "execution(void nuc.gjq.nuc01.SomeServiceImpl.doSome(String,int))")
    @Before(value = "execution(void *..SomeServiceImpl.doSome(String,int))")
    @Before(value = "execution(* *..SomeServiceImpl.doSome(..))")
    @Before(value = "execution(* *..SomeServiceImpl.do*(..))")
    @Before(value = "execution(* do*(..))")
    public void myBefore(){
          
          
    }
    
3)声明自动代理生成器
  • 使用aspectj框架内部功能,创建目标对象的代理对象

  • 创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建代理对象,所以目标对象就是被修改后的代理对象

  • aop:aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象

Ⅲ、指定通知方法中的参数(JoinPoint)

  • JoinPoint:业务方法,要加入切面功能的业务方法

  • 作用:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参

  • 如果切面功能中需要用到方法的信息,就加入JoinPoint

  • JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数

    @Before(value = "execution(* do*(..))")
        public void myBefore(JoinPoint jp){
          
          
            //获取方法的完整定义
            System.out.println("方法的签名(定义):" + jp.getSignature());
            System.out.println("方法的名称:" + jp.getSignature().getName());
            //获取方法的实参
            Object args[] = jp.getArgs();
            for (Object arg : args) {
          
          
                System.out.println("参数:" + arg);
            }
            System.out.println("6·=====前置通知,切面功能:在目标方法之前输出执行时间:"+ new Date());
        }
    

Ⅳ、异常通知(@AfterThrowing)

  • 公共方法 public

  • 方法没有返回值

  • 方法名称自定义

  • 方法有一个参数Exception,如果还有是JoiPoint

  • 属性:

    1. value:切入点表达式
    2. throwing:自定义的变量,表示目标方法执行时是不是有异常
  • 特点:

    1. 在目标方法抛出异常时执行

    2. 可以做异常的监控程序,监控目标方法执行时是不是有异常

      如果有异常,可以发送邮件,或者短信进行通知

    	@AfterThrowing(value = "execution(* *..SomeServiceImpl.do*(..))",throwing = "ex")
        public void myAfterThrowing(Exception ex){
          
          
            System.out.println("异常通知:发生异常时执行:"+ex.getMessage());
            //发送邮件或短信通知开发人员
        }
    
    执行类似于
    	try{
          
          
            SomeServiceImpl.do*(..)
        }catch(Exception e){
          
          
            myAfterThrowing(e)
        }
    
  • ex.getMessage():获取异常的信息

Ⅴ、最终通知(@After)

  • 公共方法 public

  • 方法没有返回值

  • 方法名称自定义

  • 方法没有参数,如果有是JoiPoint

  • 属性:value 切入点表达式

  • 位置:方法上面

  • 特点:

    1. 总是会执行
    2. 在目标方法之后执行
    	@After(value = "execution(* *..SomeServiceImpl.do*(..))")
        public void myAfter(){
          
          
            System.out.println("执行最终通知,总是会被执行的代码");
            //一般做资源清除工作,清理缓存
        }
    
    执行类似于
    	 try{
          
          
            SomeServiceImpl.do*(..)
        }catch(Exception e){
          
          
           
        }finally{
          
          
        	myAfter()
        }
    

Ⅵ、定义切入点(@Pointcut)

  • 定义和管理切入点,如果项目中有多个切入点表达式是重复的,可以复用的,可以使用@Pointcut

  • 属性:value 切入点表达式

  • 位置:在自定义方法上面

  • 特点:

    1. 当使用@Pointcut定义在一个方法上面。此时这个方法的名称就是切入点表达式的别名
    2. 其他的通知中,value属性就可以使用这个方法名称,代替切入点表达式
  • 私有方法,没有返回值,没有参数

     	@Pointcut(value = "execution(* *..SomeServiceImpl.doOther(..))")
        private void apt(){
          
          
            //无需代码
        }
    	@Before(value = "apt")
    	public void myBefore(){
          
          
            ...............
        }
    

猜你喜欢

转载自blog.csdn.net/qq_45414532/article/details/115253861
今日推荐