SpringAOP

SpringAOP面向切面编程

大纲:

  1. 认识AOP

    1. 什么是AOP
    2. [AOP的功能](#2. AOP的功能)
    3. 如何实现AOP/AOP的底层
  2. AOP的核心概念

  3. SpringAOP的相关注解

  4. SpringAOP中的JointPoint和ProceedingJointPoint

  5. SpringAOP的切点表达式

1、认识AOP

1.什么是AOP

AOP,即面向切面编程,通过面向对象的思想,将重复的功能代码从逻辑代码中分离出来,降低业务逻辑各部分之间的耦合度,提高程序的可重用性

2. AOP的功能

主要功能

日志记录,性能统计,安全控制,事务处理,异常处理等等。

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

3.AOP的底层

1、底层实现:

  • 只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
  • Cglib代理使用字节码处理框架ASM,对代理对象类的class文件加载进来,通过修改字节码生成子类。 通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。

2、代理方式的选择

  • JDK创建对象的速度大于Cglib,这是由于Cglib创建对象时需要操作字节码。Cglib执行速度略大于JDK
  • 由于Cglib大部分类是直接对Java字节码进行操作,这样生成的类会存放在 堆区中,使用Cglib动态代理太多,容易造成堆空间不足。Spring默认使用JDK动态代理
  • JDK需要的委托类要有接口,否则就使用Cglib动态代理

2、AOP的核心概念

转载 (158条消息) Spring AOP详解_苏州-DaniR的博客-CSDN博客_springaop

请添加图片描述

切面(Aspect): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

连接点(join point):连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用异常的抛出

切点(PointCut): 表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

通知(Advice):AOP框架的增强处理。 Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作, 以及何时执行

目标对象(Target):将要被增强的对象/织入Advice的对象

织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程被称为织入

3、SpringAOP的相关注解

 @Aspect @Pointcut @Before @AfterReturning @AfterThrowing @After @Around 一共7

转载 Spring AOP常用注解_@未安的博客-CSDN博客_aop 常用注解

SpringAOP实现了AOP的思想,实际使用的话,会配合AspectJ一起使用

3.1 @Aspect (切面注解)

功能:声明当前类为切面类,该注解要放在类上

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
    String value() default "";
}

切面类一般为单例,当一个切面类为多例时,指定预处理的切入点表达式

支持指定切入点表达式或 用**@Pointcut修饰的方法名称(要求全限定方法名)**

@Scope("prototype")
@Component
@Aspect(value = "execution(top.beau.service.Impl.*.*(..)")
public class LogUtil {

    /**
     * 用于配置当前方法的前置通知
     */
    @Before("execution(top.beau.service.Impl.*.*(..)")
    public void printLog(){
        System.out.println("执行打印日志的功能");
    }
}
3.2@PointCut (切入点注解)

功能:指定切入点表达式

使用场景:当有多个通知需要执行时,同时增强规则确定的情况下,就可以将切入点表达式通用化

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
   	/**
    * 指定切入点表达式
    */
    String value() default "";
    /**
    * 指定切入点表达式的参数,可以为execution或args中的,通常情况不使用此属性也
    * 可以获得参数
    */
    String argNames() default "";
}
@Component
@Aspect(value = "execution(* top.beau.service.Impl.*.*(..)")
public class LogUtil {
    /***
     * 切入点表达式
     * value属性中使用了 &&符号,表示并且的关系;&&符号后的args和execution一样,都是切入点表达式支持的关键字,表示参数。
     *  *可以是全限定名,或者是名称。当指定参数名称时,要求与方法的形参名称相同
     *  argNames属性,是定义参数的名称,该名称必须和args关键字的名称一致
     */
    @Pointcut(value = "execution(* top.beau.service.Impl.*.*(java.lang.String))&&args(str)",argNames = "str")
    private void print(String str){

    }
}
3.3通知注解
3.3.1@Before 前置通知的注解

被@Before注解修饰的方法为 前置通知

功能:指定在切入点方法执行前执行前置通知

使用场景: 需要对切入点方法执行之前进行增强,若要获取切入点方法中的参数进行处理,可配合切点表达式参数使用

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
    /**
     * 用于指定切入点表达式,也可为表达式引用
     */
    String value();
    /**
     * 指定切入点表达式参数的名称。要求和切入点表达式中的参数名称一致
     * 通常不指定也可以获取切入点方法的参数内容
     */
    String argNames() default "";
}
/**
* 前置通知
*/
@Before(value = "print(str)",argNames = "str")
public void beforeLog(String str){
    System.out.println("执行切入点方法前记录日志"+str);
}

3.3.2@AfterReturning

功能:配置后置通知,在切入点方法正常执行后执行

基于注解配置时,Spring创建通知方法的拦截器链时,后置通知在最终通知之后,先执行@After注解修饰的方法

使用场景: 此注解是用于配置后置增强切入点方法的,被修饰的方法会在切入点方法正常执行后执行,提交事务,记录访问日志,统计方法执行效率等可以用后置通知实现

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterReturning {
    /**
     * 指定切入点表达式或表达式引用
     */
    String value() default "";
    /**
     * 等同于value
     */
    String pointcut() default "";
    /**
     * 指定切入点方法返回值的变量名称,与切入点方法返回值名称一致
     */
    String returning() default "";
    /**
     * 指定切入点表达式参数的名称。要求和切入点表达式中的参数名称一致
     * 通常不指定也可以获取切入点方法的参数内容
     */
    String argNames() default "";
}
/**
* 后置通知
*/
@AfterReturning(value = "execution(* top.beau.service.impl.*.*(..))&&args(param)",returning = "str")
public void afterReturningLog(String param,Object str){
    System.out.println("正常执行切入点方法后记录日志,切入点方法的参数
    是:"+param);
    System.out.println("正常执行切入点方法后记录日志,切入点方法的返回值
    是:"+str);
}
3.3.3@AfterThrowing

功能:配置异常通知

使用场景: 此注解修饰方法在切入点方法执行产生异常后执行

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterThrowing {
    /**
     * 指定切入点表达式或表达式引用
     */
    String value() default "";
    /**
     * 等同于value
     */
    String pointcut() default "";
    /**
     * 指定切入点方法执行产生异常时的异常对象变量名称,与切入点方法异常变量名称一致
     */
    String throwing() default "";
    /**
     * 指定切入点表达式参数的名称。要求和切入点表达式中的参数名称一致
     * 通常不指定也可以获取切入点方法的参数内容
     */
    String argNames() default "";
}
/**
* 异常通知
*/
@AfterThrowing(value = "execution(* top.beau.service.impl.*.*(..))&&args(param)",throwing = "e")
public void afterThrowingLog(String param,Throwable e){
    System.out.println("执行切入点方法产生异常后记录日志,切入点方法的参数
    是:"+param);
    System.out.println("执行切入点方法产生异常后记录日志,切入点方法的异常
    是:"+e);
}
3.3.4@After

功能:指定最终通知

使用场景: 最终通知于切入点方法执行完成之后执行,无论切入点方法是否产生异常最终通知都会执行,通常用来 做一些清理操作

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
    /**
     * 指定切入点表达式或表达式引用
     */
    String value();
    /**
     * 指定切入点表达式参数的名称。要求和切入点表达式中的参数名称一致
     * 通常不指定也可以获取切入点方法的参数内容
     */
    String argNames() default "";
}
/**
* 最终通知
*/
@After(value = "execution(* top.beau.service.impl.*.*(..))")
public void afterLog(){
    System.out.println("无论切入点方法执行是否有异常都记录日志");
}
3.3.5@Around

功能:指定环绕通知

特点:不用来指定增强方法执行时间,而通过编码方式手动控制增强方法何时执行

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Around {
    /**
     * 指定切入点表达式或表达式引用
     */
    String value();
    /**
     * 指定切入点表达式参数的名称。要求和切入点表达式中的参数名称一致
     * 通常不指定也可以获取切入点方法的参数内容
     */
    String argNames() default "";
}
/**
* 环绕通知
*/
@Around("execution(* top.beau.service.impl.*.*(..))")
public Object arountPrintLog(ProceedingJoinPoint joinPoint){
    //1.定义返回值
    Object res = null;
    try{
        //前置通知
        System.out.println("执行切入点方法前记录日志");
        //2.获取方法执行所需的参数
        Object[] args = joinPoint.getArgs();
        //3.执行切入点方法
        res = joinPoint.proceed(args);
        //后置通知
        System.out.println("正常执行切入点方法后记录日志");
    }catch (Throwable t){
        //异常通知
        System.out.println("执行切入点方法产生异常后记录日志");
    }finally {
        //最终通知
        System.out.println("无论切入点方法执行是否有异常都记录日志");
    }
    return rese;
}

4、SpringAOP中的JointPoint和ProceedingJointPoint

转载 (158条消息) SpringAOP中的JointPoint和ProceedingJoinPoint使用总结_啊~~噙!的博客-CSDN博客

4.1 Point Cut

pointcut 是一种程序结构和规则,它用于选取join point并收集这些point的上下文信息。

pointcut通常包含了一系列的Joint Point,我们可以通过pointcut来同时操作jointpoint。

JointPoint通过JpointPoint对象可以获取到下面信息

// 返回目标对象,即被代理的对象
Object getTarget();
//返回切入点的参数
Object[] getArgs();
// 返回切入点的Signature
Signature getSignature();
//返回切入的类型,比如method-call,field-get等等,感觉不重要 
 String getKind();
4.2Joint Point

作为AOP的切入点.JointPoint对象则包含了和切入相关的很多信息。比如切入点的对象,方法,属性等。我们可以通过反射的方式获取这些点的状态和信息,用于追踪tracing和记录logging应用信息。

  • JointPoint使用:

    • 切入点的方法名及其参数
    • 切入点方法标注的注解对象(通过该对象可以获取注解信息)
    • 切入点目标对象(可以通过反射获取对象的类名,属性和方法名)
  • 相关API

    1. 获取切入点所在目标对象

      Object targetObj =joinPoint.getTarget();
      // 可以发挥反射的功能获取关于类的任何信息,例如获取类名如下
      String className = joinPoint.getTarget().getClass().getName();			
      
    2. 获取切入点方法的名字 getSignature();是获取到这样的信息 :修饰符+ 包名+组件名(类名) +方法名

      String methodName = joinPoint.getSignature().getName()
      
    3. 获取方法上的注解

      1. 方法一
       Signature signature = joinPoint.getSignature();
       MethodSignature methodSignature = (MethodSignature) signature;
       Method method = methodSignature.getMethod();
       if (method != null)
       {
           xxxxxx annoObj= method.getAnnotation(xxxxxx.class);
        }
        return null;
      

      ​ 2.方法二: 上面我们已经知道了方法名和类的对象,通过反射可以获取类的内部任何信息。

       // 切面所在类
              Object target = joinPoint.getTarget();
       
              String methodName = joinPoint.getSignature().getName();
       
              Method method = null;
              for (Method m : target.getClass().getMethods()) {
                  if (m.getName().equals(methodName)) {
                      method = m;
                     //  xxxxxx annoObj= method.getAnnotation(xxxxxx.class);同上
                      break;
                  }
              }
      
    4. 获取方法的参数

      Object[] args = joinPoint.getArgs();
      
4.3 ProceedingJointPoint (只有环绕通知才能使用)

Proceedingjoinpoint 继承了 JoinPoint。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。

环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的
5、SpringAOP切点表达式

转载 Spring AOP常用注解_@未安的博客-CSDN博客_aop 常用注解

5.1切入点表达式标准格式

动作关键字(访问修饰符 返回值 包.类/接口名.方法名(参数)异常名)

  • 动作关键字:描述切入点的行为动作
execution:用于匹配方法执行的连接点;
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这
样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就
不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的
执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风
格不支持。

5.2AOP切入点表达式通配符
AspectJ类型匹配的通配符:
    *:匹配任何数量字符;
    ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式
    中匹配任何数量参数。
    +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
5.3切入点表达式逻辑
&& and       || or        !  not

关于切入点表达式下面这篇文章讲的很细

于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的
执行方法;
reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风
格不支持。


##### 5.2AOP切入点表达式通配符

AspectJ类型匹配的通配符:
*:匹配任何数量字符;
…:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式
中匹配任何数量参数。
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。


##### 5.3切入点表达式逻辑

&& and || or ! not


**关于切入点表达式下面这篇文章讲的很细**

 [(158条消息) SpringAOP的使用_小老虎Love的博客-CSDN博客_springaop使用](https://blog.csdn.net/tenghu8888/article/details/122444743?ops_request_misc=%7B%22request%5Fid%22%3A%22165819100916780357279424%22%2C%22scm%22%3A%2220140713.130102334..%22%7D&request_id=165819100916780357279424&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-122444743-null-null.142^v32^new_blog_pos_by_title,185^v2^control&utm_term=Spring使用aop&spm=1018.2226.3001.4187) 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值