Spring注解式开发(六):五分钟搞定Spring AOP开发

AOP功能

AOP(Aspect Oriented Programming):面向切面编程 指在程序运行期间动态的将某段代码切入到指定方法指定位置运行的编程方式
进行aop开发时首先要导入spring aop的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

然后定义一个切面类,切面类里面包含各种通知方法

  • 前置通知(@Before(“execution表达式”)):在目标方法运行之前执行
  • 后置通知(@After(“execution表达式”)):在目标方法运行之后执行
  • 异常通知(@AfterThrowing(“execution表达式”)):在目标方法出现异常时执行
  • 返回通知(@AfterReturning(“execution表达式”)):在目标方法又返回时执行
  • 环绕通知(@Around(“execution表达式”)):这个通知需要手动让目标方法执行(joinPoint.procced)

execution表达式

基本语法如下
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) 除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
execution(* com.sample.service.impl….(…))

解释如下:

符号 含义
execution() 表达式的主体
第一个”*“符号 表示返回值的类型任意;
com.sample.service.impl AOP所切的服务的包名,即,我们的业务部分
包名后面的”…“ 表示当前包及子包
第二个”*“ 表示类名,*即所有类。此处可以自定义,下文有举例
.*(. .) 表示任何方法名,括号表示参数,两个点表示任何参数类型

Spring Aop的使用

定义一个切面类,使用@Aspect表示这是一个切面类

/**
 * @Aspect注解告诉spring这是一个通知类
 */
@Aspect
public class MyAspect {
    /**
     * @Pointcut抽取公共的切入点
     */
    @Pointcut("execution(* com.wmq.spring.service.SpringService.print(..))")
    public void pointcut(){}
    //如果使用joinpoint,必须放在方法参数表的第一位
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        //获取方法签名
        String name = joinPoint.getSignature().getName();
        System.out.println("before.........." + name);
        System.out.println("前置通知拿到的方法名" + name);
    }

    @After("execution(* com.wmq.spring.service.SpringService.print(..))")
    public void after(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println("after..........");
        System.out.println("后置通知拿到的参数列表" +   Arrays.asList(args));
    }
    //获取返回结果,使用result
    @AfterReturning(value = "pointcut()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        System.out.println("afterReturning.........." + joinPoint.getSignature().getClass());
        System.out.println("返回通知拿到 的返回值" + result);
    }

    //获取异常
    @AfterThrowing(value = "execution(* com.wmq.spring.service.SpringService.print(..))",throwing = "exception")
    public void afterThrowing(RuntimeException exception){
        System.out.println("afterThrowing..........");
        System.out.println("捕获到的异常" + exception.getMessage());
    }

    @Around("execution(* com.wmq.spring.service.SpringService.print(..))")
    public void arouund(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around 目标方法执行之前");
        Object proceed = null;
        try {
            //通知方法执行
            proceed = joinPoint.proceed();
            System.out.println("around 环绕通知获取的返回值" + proceed);
        } catch (Throwable throwable) {
            System.out.println("around 目标方法异常后执行");
        }
        System.out.println("arouund 目标方法执行之后" );
    }
}

定义一个业务类

public class SpringService {
    public Integer print(int a,int b){
        System.out.println("this is a service" + a + b);
        //if (a == 1) throw new RuntimeException("测试异常通知");
        return a + b;
    }
}

配置类,注意此处使用@EnableAspectJAutoProxy要开启基于注解式的aop方式

@Configuration
/**
 * AOP 面向切面编程:只在程序运行期间动态的将某段代码切入到指定方法指定位置运行的编程方式
 */
//开启基于注解式的aop方式
@EnableAspectJAutoProxy
public class MyAopConfig {
    @Bean
    public SpringService springService(){
        return new SpringService();
    }
    @Bean
    public MyAspect myAspect(){
        return new MyAspect();
    }
}

测试

@Test
public void testAspect(){
    //创建一个applicationContext
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyAopConfig.class);
    SpringService bean = applicationContext.getBean(SpringService.class);
    bean.print(1,2);
}

没有异常的结果

around 目标方法执行之前
before..........print
前置通知拿到的方法名print
this is a service12
around 环绕通知获取的返回值3
arouund 目标方法执行之后
after..........
后置通知拿到的参数列表[1, 2]
afterReturning..........class org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$MethodSignatureImpl
返回通知拿到 的返回值null

有异常的结果

around 目标方法执行之前
before..........print
前置通知拿到的方法名print
this is a service12
around 目标方法异常后执行:测试异常通知
arouund 目标方法执行之后
after..........
后置通知拿到的参数列表[1, 2]
afterReturning..........class org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$MethodSignatureImpl
返回通知拿到 的返回值null

对比一下发现,当有环绕通知时目标方法发生异常时异常通知是不会执行的,但是这个地方为什么出现异常了返回通知还是执行

结束语

springaop的开发实际很简单,但是对一门技术的学习我们不仅要学会怎么使用,更重要的是,要知道原理,所以下一篇文章 将会介绍aop的原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章