深入浅出——spring和AspectJ实现AOP操作

103 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情

基本概念可看我之前的文章,这次直接来实现AOP的操作,在介绍操作之前,我们得先介绍AOP的各种术语

aop操作术语

1、连接点(JoinPoint): 类里面哪些方法可以被增强,这些方法称为连接点

比如说:一个类中有add updat delect select方法,这些方法都可以被增强,所以被称为连接点

2、切入点(Pointcut ): 实际被真正增强的方法,称为切入点

3、通知/增强(Advice):

(1)实际增强的逻辑部分称为通知/增强

(2)通知有多种类型:

  • 前置通知:在目标方法执行之前执行执行的通知。无论何时都第一个执行

  • 后置通知:在目标方法执行之后执行的通知。正常执行时第三个执行

  • 环绕通知:在环绕通知中必须显式的调用目标方法,目标方法才会执行,简单理解,环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的.

  • 异常通知:在目标方法抛出异常时执行的通知。出现异常时第三个执行

  • 最终通知:是在目标方法执行之后执行的通知。无论何时都第二个执行,和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返回例如抛出异常,则后置通知不会执行。而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。另外,后置通知可以通过配置得到返回值,而最终通知无法得到。

4、切面(Aspect): 是动作,把通知应用到切入点过程

5、引入:(Introduction) 表示向现有的类中添加新方法、新属性。

6、目标对象(Target) 表示被增强的对象。

7、代理(Proxy)  表示实现AOP的机制。

8、织入(Weaving) 表示把增强应用到目标对象的过程。

在了解了AOP各种操作术语后,我们就需要对实现AOP操作做准备工作了

aop准备工作

1、Spring的框架中,一般都是基于AspectJ来实现AOP操作

(1)什么是AspectJ

  AspectJ并不是Spring组成部分,它是独立AOP框架,一般把AspectJ和Spring框架一起使用, 进行AOP操作

(2)基于AspectJ实现AOP操作

  • 基于xml配置文件实现
    
  • 基于注解方式实现(使用)
    

3、在项目工程里面引入AOP相关依赖。

我们需要准备以下的AspectJ jar包

image.png

把这些jar导入到相关的spring项目下

4、切入点表达式、

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2)语法结构:

execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))

标准表达式写法:execution(public void com.dao.UserDao.add())

表达式中,可以用通配符来表示如:

举例1: 对com.dao.UserDao类里面的add进行增强。

execution(* com.dao.UserDao.add(..))

举例2:对com.atguigu.dao.BookDao类里面的所有的方法进行增强

execution(* com.dao.UserDao.* (..))

举例3:对com.atguigu.dao包里面所有类,类里面所有方法进行增强

execution(* com.atguigu.dao.*.* (.))

AspectJ注解

1.创建类,在类中定义方法

import org.springframework.stereotype.Component;
//User类,作为目标对象
@Component
public class AspectJUser {
    public void add(){
        System.out.println("add");
    }
}

2.创建增加类,编写增强逻辑

我们在增强类中创建不同的方法,让不同的方法代表不同的增强逻辑

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AspectJUserProxy {
//    前置通知
    @Before("execution(* com.springAOP.AspectJUser.add(..))")
    public void before(){
        System.out.println("before... ");
    }

    //最终通知
    @After("execution(* com.springAOP.AspectJUser.add(..))")
    public void after(){
        System.out.println("after...");
    }

    //后置通知
    @AfterReturning("execution(* com.springAOP.AspectJUser.add(..))")
    public void AfterReturning(){
        System.out.println("AfterReturning...");
    }

    //异常通知
    @AfterThrowing("execution(* com.springAOP.AspectJUser.add(..))")
    public void AfterThrowing(){
        System.out.println("AfterThrowing...");
    }

    //环绕通知——在方法之前之后都执行
    @Around("execution(* com.springAOP.AspectJUser.add(..))")
    public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    //简单理解,环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的.
        System.out.println("Around前");
        proceedingJoinPoint.proceed();
        System.out.println("Around后");
    }
}

3、进行通知的配置 (1)在spring配置文件中,开启注解扫描。 (2)在spring配置文件中开启生成代理对象。

image.png


<!--    开启注解扫描-->
<context:component-scan base-package="com.springAOP"></context:component-scan>
<!--    开启aspectj生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

所有类写好,我们写一个test来验证一下

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AspectJTest {
    @Test
    public void testAop(){
        ApplicationContext context =
                new ClassPathXmlApplicationContext("com/bean1.xml");
        AspectJUser user = context.getBean("aspectJUser", AspectJUser.class);
        user.add();
    }
}

控制台:

image.png