基于注解的AOP

 注解的AOP切入点表达式和重用以及获取连接点的信息

   1 . 准备工作

      ① 添加依赖

    在ioc所需依赖基础上再加入下面依赖即可:

    就是所实现的aop也是在ioc容器的基础上实现的。

    所以spring最重要的核心就是ioc。

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aspects</artifactId>
   <version>5.3.18</version>
</dependency>

②接口
public interface Calculator {
   int add(int i, int j);
   int sub(int i, int j);
   int mul(int i, int j);
   int div(int i, int j);
}

③实现类
@Component
public class CalculatorImpl implements Calculator {
   @Override
   public int add(int i, int j) {
       
       int result = 1+j;
       System.out.println("方法内部,result:"+result);
       return result;
  }
   @Override
   public int sub(int i, int j) {

       int result = 1-j;
       System.out.println("方法内部,result:"+result);

       return result;
  }
   @Override
   public int mul(int i, int j) {

       int result = 1*j;
       System.out.println("方法内部,result:"+result);

       return result;
  }
   @Override
   public int div(int i, int j) {

       int result = 1/j;
       System.out.println("方法内部,result:"+result);

       return result;
  }
}

     2 . 注解的AOP

img

 

 将前置通知通过切入点表达式作用到了连接点上

       切面:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 创建切面
* 如果要实现aop:把切面和目标对象交给ioc管理
* 1.
* 切面类必须通过@Asprct注解标识为一个切面
*/
//必须把切面类和目标类作为ioc容器中的组件
@Component
@Aspect//将当前组件标识为切面
public class LoggerAspect {
/**
* 切面写的是抽取横切关注点,把横切关注点封装到一个方法,然后这个方法就是一个通知
* 然后在把通知方法通过切入点表达式作用于连接点上
* @Before():在目标对象方法执行之前执行
*   需要当前的通知通过切入点execution()表达式作用于连接点
*   所以切面的通知确实通过当前切入点表达式作用到了当前连接点
*/
//设置前置通知:@Before()注解
@Before("execution(public int com.atguigu.spring.aop.annotation.CalculatorImpl.add(int,int))")
public void beforeAdviceMethod(){
   System.out.println("LoggerAspect ,前置通知");
}
}

​XML:
 <!--
   AOP的注意事项:
      切面类和目标类都需要交给IOC容器管理
      切面类必须通过@Asprct注解标识为一个切面
      在spring的配置文件中设置   <aop:aspectj-autoproxy /> 开启基于注解的aop功能
   -->
  <context:component-scan base-package="com.atguigu.spring.aop.annotation"></context:component-scan>
<!-- 开启基于注解的aop功能 -->
  <aop:aspectj-autoproxy />

测试类:
 @Test
   public void testAOPByAnnotation(){
       ApplicationContext ioc=new ClassPathXmlApplicationContext("ApplicationContext.xml");
       /**
        * 只要为目标对象创建了代理对象,每次访问他,都是通过代理对象间接访问
        * 获取代理类对象:因为跟目标对象实现的是相同的接口,可以通过向上转型,来创建接口的对象
        * 通过ioc获取某个bean的时候,不需要通过一个具体的类型获取,
        * 可以用所继承的父类或者实现的接口都可以。
        */
       Calculator bean = ioc.getBean(Calculator.class);
       bean.add(1,2);
         /**
        * LoggerAspect ,前置通知
        * 方法内部,result:3
        */
  }

 以上是写死了的所以要改进:

  切入点表达式的语法

   切面
 /**
    * 前置方法表示这个类中所有的方法设置前置通知
    * 切入点表达式:设置在标识通知的注解value属性中
    * @Before("execution(.....)")
    * 第一个*表示任意的访问修饰符和返回值类型
    * 第二个*表示类中任意的方法
    * ..任意参数列表
    * 在类的地方也可以写*,表示包下所有的类
    * ()表示的是:当前方法的参数列表,
    *   方法存在重载,括号里面为空表示的是就是无惨的方法
    *   不在乎方法的参数是什么,只要是个方法就加前置通知,就写..
    */
   //通知必须要套到当前目标对象的方法上(也就是连接点上)所以用切入点表达式
@Before("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl .*(..))")

测试
 Calculator bean = ioc.getBean(Calculator.class);
       bean.add(1,2);
       bean.div(1,2);
       /**
        * LoggerAspect ,前置通知
        * 方法内部,result:3
        * LoggerAspect ,前置通知
        * 方法内部,result:0
        */
 

  重用切入点表达式

      切面

  /**
    * 2. 重用切入点表达式
    * @Pointcut:声明一个公共的切入点表达式
    * 使用方式:
    * 例: @Before("pointCut()") :括号里写的就是当前方法的方法名
    * 声明功能的表达式
    */
   @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
   public void pointCut(){}

 //直接调用方法
   @Before("pointCut()")
  ....
   @After("pointCut()")
      ....
     

  获取连接点的信息

           在通知方法的参数位置,设置JoinPoint类型的参数,就可以获取连接点所对应方法的信息

         切面

  @Before("pointCut()")
/**
* 3.获取连接点信息
* 在通知方法的参数位置,设置JoinPoint类型的参数,就可以获取连接点所对应方法的信息
* joinPoint:帮助我们获取连接点的信息
*(切入点表达式定位的是哪个方法,当前joinPoint表示的哪一个方法的信息)
*/
public void beforeAdviceMethod(JoinPoint joinPoint){
   /**
    * 获取连接点所对应方法的签名信息
    * 签名信息:方法声明,访问修饰符,返回值类型,方法名 ,参数
    */
   Signature signature = joinPoint.getSignature();
   //获取连接点所对应方法的参数
   Object[] args = joinPoint.getArgs();
   //因为是因为是Object类型,输出的是内存地址,要加上toString()
   System.out.println("LoggerAspect ,方法:"+signature.getName()+",参数:"+      Arrays.toString(args));
}

@After("pointCut()")
public void afterAdiviceMethod(){
....
}

测试
  Calculator bean = ioc.getBean(Calculator.class);
       bean.add(1,2);
       bean.div(1,2);
       /**
        * LoggerAspect ,方法:add,参数:[1, 2]
        * 方法内部,result:3
        * LoggerAspect ,方法:div,参数:[1, 2]
        * 方法内部,result:0
        */

       其他通知

  /**
    * @After:后置通知,在目标对象方法的finally子句中执行的
    *@AfterReturning:返回通知,在目标对象方法返回值之后执行(目标对象有异常不执行)
    * 在返回通知中若要获取目标对象方法的返回值,只需要通过@AfterReturning注解的returning属性
    * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
    * @AfterThrowing:异常通知在目标对象方法的catch子句中执行
    * 在异常通知中获取目标对象的异常只需要通过@AfterThrowing注解的throwing属性
    * 就可以将通知方法的某个参数指定为接收目标对象方法出现异常的参数
    */

   @After("pointCut()")
public void afterAdiviceMethod(){
}

@AfterReturning(value = "pointCut()",returning = "result")
public void afterAdviceMethod(JoinPoint joinPoint,Object result){
   Signature signature = joinPoint.getSignature();
   System.out.println("LoggerAspect,返回通知"+"结果:"+result);
}

@AfterThrowing(value="pointCut()",throwing = "ex")
public void afterThrowingAdviceMethod(JoinPoint joinPoint, Exception ex){
   System.out.println(".."+"异常"+ex);
}
 
    环绕通知 
@Around("pointCut()")
/**
* ProceedingJoinPoint:可执行的连接点的对象
*设置环绕通知的方法,来代替目标对象方法执行,
*       所以一定要保证当前目标对象方法的返回值是什么,当前环绕通知的方法返回值也要是什么
*       (环绕通知的方法返回值一定要和目标对象方法的返回值一致)
*/
public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
   Object result=null;
   try {
       System.out.println("环绕通知-->前置通知");
       //表示目标对象方法的执行
       result = joinPoint.proceed();
       System.out.println("环绕通知-->返回通知");
  } catch (Throwable e) {
       e.printStackTrace();
       System.out.println("环绕通知-- > 异常通知");
  }finally {
       System.out.println("环绕通知-- > 后置通知");
  }
   return result;
}

   切面的优先级

       用@Order注解标识切面的优先级。

      而每一个优先级都有默认值,integer最大值。

      value值越小,优先级越低。


@Component
@Aspect
@Order(1) //优先级
public class ValidateAspect {

   @Pointcut("execution(* com.atguigu.spring.aop.annotation.CalculatorImpl.*(..))")
   public void pointCut(){}
   @Before("pointCut()")
   public void beforeMethod(){
       System.out.println("ValidateAspect-->前置通知");
  }

posted @ 2022-11-19 22:34  zjw_rp  阅读(170)  评论(0)    收藏  举报