AOP学习笔记


一、AOP简介

AOP (Aspect Oriented Programming) 面向切面编程
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点
在这里插入图片描述

二、AOP示例

①在 Spring 应用中使用 AspectJ 注解, 必须依赖 AspectJ 类库: aopalliance.jar、 aspectj.weaver.jar 和 spring-aspects.jar

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.0.8.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.2</version>
    </dependency>

②spring 的配置文件需要引用 aop 的命名空间

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd">
</beans>

③在 Spring 的配置文件中加入< aop:aspectj-autoproxy >

    <aop:aspectj-autoproxy/>
    <context:component-scan base-package="cn.com.et"/>

④在实现类中增加注解,将其纳入到 spring 容器中

@Component
public class Add implements Calculator {
    @Override
    public double cal(double num1, double num2) {
        return num1 + num2;
    }
}

⑤编写切面

public class CalculatorAspect {
    @Before("execution(* cn.com.et.impl.*.cal(..))")
    public void before(){
        System.out.println("执行前需要执行的内容");
    }
}

⑥测试程序

public class AspectTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml");
        Calculator calculator = (Calculator) ctx.getBean("add");
        double rst = calculator.cal(777, 111);
        System.out.println(rst);
    }
}

控制台打印:

执行前需要执行的内容
888.0

三、切入点表达式

1.完整表达式 例:
execution(public double cn.com.et.impl.Add.cal(double,double))

  • execution:动作关键字,描述切入点的行为动作,例如execution表示执行到指定切入点
  • public:访问修饰符,还可以是public,private等,可以省略
  • double:返回值,写返回值类型
  • cn.com.et.impl:包名,多级包使用点连接
  • Add:类/接口名称
  • cal:方法名
  • double:参数,直接写参数的类型,多个类型用逗号隔开
  • 异常名:方法定义中抛出指定异常,可以省略

2.通配符
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
..:多个连续的任意符号,可以独立出现,常用于简化参数的书写
例:execution(* *.cal(..))

四、JoinPoint参数

可以在通知方法中声明一个类型为 JoinPoint 的参数. 然后就能访问链接细节. 如方法 名称和参数值。

    @Before("execution(* cn.com.et.impl.*.cal(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("被通知对象名称" + joinPoint.getTarget().getClass().toString());
        System.out.println("被指定的方法名称" + joinPoint.getSignature().getName());
        System.out.println("方法的参数" + Arrays.toString(joinPoint.getArgs()));
        System.out.println("执行前需要执行的内容");
    }

五、AOP术语

  • 切面(Aspect):描述通知与切入点的对应关系
    加上注解@Aspect的类
  • 通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
    在切面中加入注解@Before;@After 等等的方法
  • 目标对象(Target):被代理的原始对象成为目标对象
    要使用这个切面通知的类。JoinPoint.getTarget()方法来获取
  • 代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
    JoinPoint.getThis()方法来获取
  • 连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
    可以通过通知方法的参数 JoinPoint 来获取
  • 切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
    通过切点表达式所匹配到的方法

1.通知

@Before
前置通知。表示在业务方法执行之前去执行的通知方法。
@After
后置通知, 在业务方法执行之后执行
@AfterReturning
返回通知, 在方法返回结果之后执行。无论连接点是正常返回还是抛出异常, 后置通知 都会执行. 如果只想在连接点返回的时候记录日志, 应使用返回通知代替后置通知
@AfterThrowing
异常通知,只在连接点抛出异常时才执行异常通知
@Around:
环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是 否执行连接点. 对于环绕通

@Component
@Aspect
public class CalculatorAspect {
    @Before("execution(* cn.com.et.impl.*.cal(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("被通知对象名称" + joinPoint.getTarget().getClass().toString());
        System.out.println("被指定的方法名称" + joinPoint.getSignature().getName());
        System.out.println("方法的参数" + Arrays.toString(joinPoint.getArgs()));
        System.out.println("执行前需要执行的内容");
    }

    @After("execution(* cn.com.et.impl.*.cal(..))")
    public void after(){
        System.out.println("执行之后...");
    }

    @AfterReturning(pointcut = "execution(* cn.com.et.impl.*.cal(..))",returning = "obj")
    public void returning(JoinPoint joinPoint,Object obj){
        System.out.println("返回通知......方法返回值为" + obj);
    }

    @Around("execution(* *.cal(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("before");
        Object obj = null;
        try {
            obj = proceedingJoinPoint.proceed();
            System.out.println("after return");
        } catch (Throwable e) {
            System.out.println("Throwable");
        }finally {
            System.out.println("after");
        }
        return obj;
    }
}

2.代理

遇到的小问题:(Add实现了Calculator接口)

    Add add = (Add) ctx.getBean("add");

Exception in thread “main” java.lang.ClassCastException:
com.sun.proxy.$Proxy11 cannot be cast to cn.com.et.impl.Add

不能用接口的实现类(Add)来转换Proxy的实现类,它们是同级,应该用共同的接口(Calculator)来转换。

    Calculator calculator = (Calculator) ctx.getBean("add");

查询得知:
Spring AOP实现方式有两种
1:使用JDK动态代理,如果被代理的目标实现了至少一个接口,则会使用JDK动态代理,所有该目标类型实现的接口都将被代理。
2:通过CGLIB来为目标对象创建代理,若该目标对象没有实现任何接口,则创建一个CGLIB代理,创建的代理类是目标类的子类。
参考文章:https://www.jianshu.com/p/a0c349e813eb


总结

本文对今天的AOP学习做了一个简单的总结,参考了多位大佬的资料,受益匪浅。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值