3 - AOP

发布时间:2024年01月14日

1. 快速入门

1.1 基本说明

AOP(aspect oriented programming) ,面向切面编程

切面类中声明通知方法:

  • 前置通知:@Before
  • 返回通知:@AfterReturning
  • 异常通知:@AfterThrowing
  • 后置通知:@After
  • 环绕通知:@Around

1.2 快速入门?

参考视频:B站 三更草堂视频

1.3 细节说明

1)切面类方法命名

  • showBeginLog()
  • showSuccessEndLog()
  • showExceptionLog()
  • showFinallyEndLog()

2)切入表达式更多的配置

使用模糊配置

@Before(value="execution(* com.hspedu.aop.proxy.SmartDog.*(..))")

这个就表示这个切面方法作用与? com.hspedu.aop.proxy.SmartDog 类的函数

3)所有访问权限

@Before(value="execution(* *.*(..))")
  1. 第一个 * 表示 任意方法权限及任意返回值
  2. 第二个 * 表示 任意路径
  3. 第三个 * 表示 任意方法
  4. (..) 表示任意数量,任意类型的参数

所有包的下所有有类的所方法,都会被执行该前置通知方法

4)使用基于注解的AOP需要在配置文件里开启

<aop:aspectj-autoproxy/> 

5)开启了基于注解的后获取对象

这个时候去获取 ioc 容器里的对象的话,在底层被转换为 代理对象

我们获取注入的对象, 也可以通过 id 来获取, 但是也要转成接口类型


2.?AOP-切入表达式

2.1 具体使用

1)作用

通过表达式的方式来定位一个或多个具体的切入点

2)语法细节

格式: execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名] ([参数列表]))

若目标类、接口 与切面类在同一个包下,可以省略包名

2.2?注意事项和细节

1)表达式指向类

切入表达式也可以指向类的方法, 这时切入表达式会对该类/对象生效

2)表达式指向接口

切入表达式也可以指向接口的方法, 这时切入表达式会对实现了接口的类/对象生效

3)表达式指向没有实现接口的类

切入表达式也可以对没有实现接口的类,进行切入

@Component
class Car {
    public void run() {
        System.out.println("car run");
    }
}

切面类:

@Before(value = "execution(public void com.hspedu.spring.aop.aspectj.Car.run())")
    public static void showBeginLog3(JoinPoint joinPoint) {
        //获取方法签名
        Signature signature = joinPoint.getSignature();
        System.out.println("方法执行前--方法名--" + signature.getName());
    }

测试:

@Test
    public void carTestProxy() {
        //得到spring容器
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans08.xml");
        Car car = ioc.getBean(Car.class);
        System.out.println(car.getClass());
        car.run();

    }

不一样的时,这此取出来的 car 对象的运行类型:

? ? class com.hspedu.spring.aop.aspectj.Car$$EnhancerBySpringCGLIB$$8e9f1fa7

这个时候 就是 Spring 的 CGlib 方案:取出的时 Car 对象的子类

参考:jdk的Proxy与spring的CGlib


3. 切入方法细节

3.1?AOP-JoinPoint?

常用方法一览

  • joinPoint.getSignature().getName(); 获取目标方法名
  • joinPoint.getSignature().getDeclaringType().getSimpleName(); 获取目标方法所属类的简单类名
  • joinPoint.getSignature().getDeclaringTypeName();? 获取目标方法所属类的类名
  • joinPoint.getSignature().getModifiers(); /获取目标方法声明类型(public、private、protected)
  • Object[] args = joinPoint.getArgs();? 获取传入目标方法的参数,返回一个数组
  • joinPoint.getTarget();? ?获取被代理的对象
  • joinPoint.getThis();? ? 获取代理对象自己

3.2?返回通知获知结果

如何在返回通知方法获取返回结果?

**
* returning = "res", Object res 名称保持一致
* @param joinPoint
* @param res 调用 getSum() 返回的结果
*/
@AfterReturning(value="execution(public float com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))",returning = "res")
public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
    System.out.println("返回通知" + "--结果是--" + res );
}

3.3 AOP-异常通知中获取异常

@AfterThrowing(value="execution(public float
com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))",throwing = "throwable")
public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
    System.out.println("异常通知 -- 异常信息--" + throwable);
}

3.4 环绕通知(了解)

环绕通知可以完成其它四个通知要做的事情

@Aspect //表示这个类是一个切面类
@Component //需要加入到 IOC 容器
public class SmartAnimalAspect {
    //=====环绕通知 start=====
    //@Around(value = "execution(* *.*(..))")
    @Around(value="execution(public float
    com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object result = null;
        String methodName = joinPoint.getSignature().getName();
        try {
            //1.相当于前置通知完成的事情
            Object[] args = joinPoint.getArgs();
            List<Object> argList = Arrays.asList(args);
            System.out.println("AOP 环绕通知--" + methodName + "开始了--参数有:
                " + argList);
            //在环绕通知中一定要调用 joinPoint.proceed()来执行目标方法
            result = joinPoint.proceed();
            //2.相当于返回通知完成的事情
            System.out.println("AOP 环绕通知" + methodName + "结束了--结果是:"
                + result);
        } catch (Throwable throwable) {
            //3.相当于异常通知完成的事情
            System.out.println("AOP 环绕通知" + methodName + "抛异常--异常对象:" + 
                throwable);
        } finally {
            //4.相当于最终通知完成的事情
            System.out.println("AOP 后置通知" + methodName + "最终结束...");
        }
    return result;
}

环绕通知和动态代理完成的事情相似

3.5??切入点表达式重用

为了统一管理切入点表达式,可以使用切入点表达式重用技术

在切面类上添加:

//=====AOP-切入点表达式重用 start ======
/*
* 这样定义的一个切入点表达式,就可以在其它地方直接使用
*/
@Pointcut(value="execution(public float
com.hspedu.spring.aop.joinpoint.SmartDog.getSum(float, float))")
public void myPointCut() {
}

这样其他切面方法就可以简写:

@Before(value = "myPointCut()")
public void showBeginLog(JoinPoint joinPoint) { //前置方法
    Signature signature = joinPoint.getSignature();
    //得到方法名.
    String method_name = signature.getName();
    //得到参数
    Object[] args = joinPoint.getArgs();
    System.out.println("前置通知" + "--调用的方法是 " + method_name + "--参数是
        --" + Arrays.asList(args));
}

3.6?AOP-切面优先级问题

如果同一个方法,有多个切面在同一个切入点切入,那么执行的优先级如何控制?

@order(value=n) 

通过设定 order 的value 值来控制, n 值越小,优先级越高

@Aspect //表示这个类是一个切面类
@Order(value = 2)
@Component //需要加入 IOC 容器
public class SmartAnimalAspect2 {
    /**
     内部是切面方法
    */
}

/****************************/

@Aspect //表示这个类是一个切面类
@Order(value = 1)
@Component //需要加入 IOC 容器
public class SmartAnimalAspect {
    /**
     内部是切面方法
    */
}

输出的信息顺序,类似 Filter 的过滤链式调用机制


3.??基于 XML 配置 AOP

前面是通过注解来配置 aop 的,在 spring 中,我们也可以通过 xml 的方式来配置 AOP

1)准备对象

去掉切面方法的所有注解

2)配置 xml

<!-- 配置 SmartAnimalAspect bean -->
<bean id="smartAnimalAspect" class="com.hspedu.spring.aop.xml.SmartAnimalAspect"/>

<!--配置 SmartDog-->
<bean class="com.hspedu.spring.aop.xml.SmartDog" id="smartDog"/>

<aop:config>
    <!-- 配置统一切入点 -->
    <aop:pointcut expression="execution(public float
        com.hspedu.spring.aop.xml.SmartDog.getSum(float, float))" id="myPointCut"/>
    <aop:aspect ref="smartAnimalAspect" order="1">
    <!-- 配置各个通知对应的切入点 -->
        <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
        <aop:after-returning method="showSuccessEndLog" pointcut-ref="myPointCut" 
            returning="res"/>
        <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" 
            throwing="throwable"/>
        <aop:after method="showFinallyEndLog" pointcut-ref="myPointCut"/>
        <!-- 还可以配置环绕通知 -->
        <!-- <aop:around method=""/> -->
    </aop:aspect>
</aop:config>

文章来源:https://blog.csdn.net/lin_xiangxiang/article/details/135585983
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。