Springboot AOP 实战

 

简介

此处,只梳理AOP的基本概念和应用。底层原理是动态代理和反射,不作探究。

aop 面向切面 :大白话来说就是针对一些类的一些方法需要追加一些功能,这些功能和原来的逻辑无关。

切面编程的三个要素:

这些功能可以单独定义一个类来实现称之为通知

另外再通过一个类实现追加,这个类称之为切面

那么这些待追加功能的方法就是称为切入点

1实战

1.1 依赖

spring boot项目中使用AOP 需要添加maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>

1.2 aop实现

1.2.1 基于注解切入

1 自定义一个注解 为了标识需要增强的位置

package com.wang.seckill.annotation;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 注解,用于标识切入点,添加该注解的方法,
 * 执行前后会先执行通知
 */

@Retention(RetentionPolicy.RUNTIME)  //什么时期
@Target(ElementType.METHOD)///用于方法
public @interface Validate {

}

2 通知定义--对原有功能增强

实质上还是一个类,承载了功能逻辑。

package com.wang.seckill.advice;


import org.springframework.stereotype.Component;

/**
 *
 * 通知类,通过切面对其他方法增强
 */

@Component
public class CheckUserServiceImpl implements CheckUserService{
    @Override
    public void check() {
        System.out.println("我是一个通知方法,我现在执行了哦");
    }
}

3 切面实现--- 

使用@Aspect  

package com.wang.seckill.aspect;

import com.wang.seckill.advice.CheckUserService;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 *
 *切面: 把通知和需要增强的方法结合起来
 */
@Aspect  //开启切面功能
@Component
public class CheckUserAspect {
    @Autowired
    private CheckUserService checkUserService;
    //@annotation 切入点,被Validate注解的方法,就会有通知
    @Pointcut("@annotation(com.wang.seckill.annotation.Validate)")
    public void point(){
        
    }

    @Before("point()")///通知类型,关联到切入点,
    public void checkBefore(){
//        通知逻辑
        checkUserService.check();
    }
}

测试: 

exeSelect() 执行之前,会先执行通知方法:

   /**
     * 查询所有秒杀商品
     * @return
     */
    @Override
    @Validate
    public List<TbSeckillGoods> exeSelect() {
        return redisTemplate.opsForHash().values(TbSeckillGoods.class.getSimpleName());
    }

前台查询结果:

可以看到通知方法被执行了: 

 

1.2.2 基于表达式切入

1 通知

package com.wang.seckill.advice;

import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Component
public class TestExecutionAdvice {

    public void exeBefore(){
        System.out.println("我是通过切面表达式触发的切面方法");
    }
}

2 切面

切面表达式execution()  

package com.wang.seckill.aspect;


import com.wang.seckill.advice.TestExecutionAdvice;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectExecution {
    @Autowired
    private TestExecutionAdvice testExecutionAdvice;

    //切面表达式   com.wang.seckill.service包下面所有类的所有方法 任意返回类型
    @Pointcut("execution(* com.wang.seckill.service.*.*(..))")
    public void point(){
    }

    @Before("point()")
    public void exeBefore(){
        testExecutionAdvice.exeBefore();
    }

}

测试

package com.wang.seckill.service; //包中查询一个上面的方法已经被增强
    /**
     * 根据id从redis查到商品返回
     * @param id
     * @return
     */
    public TbSeckillGoods findOne(long id) {
        return (TbSeckillGoods) redisTemplate.boundHashOps(TbSeckillGoods.class.getSimpleName()).get(id);
    }

查询一个商品:

 

通知执行:

 

 

 

通知类型

@Before

@after

@Round

@AfterReturning

@AfterThrowing

在一个方法加了所有通知之后执行:

方法类

package com.wang.seckill.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Component
public class TestExecutionAdvice {

    public void exeBefore(){
        System.out.println("我是@Before通过切面表达式触发的切面方法");
    }
    public void exeAfter(){
        System.out.println("我是@After我在方法执行之后执行");
    }
    public Object exeRound(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("我是@Around我环绕执行");
        return proceedingJoinPoint.proceed();

    }
    public void throwing(){
        System.out.println("我是@AfterThrowing 发生异常我就执行");
    }
    public void returning(){
        System.out.println("我是@Returning方法返回后执行");
    }
}

切面

package com.wang.seckill.aspect;


import com.wang.seckill.advice.TestExecutionAdvice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectExecution {
    @Autowired
    private TestExecutionAdvice testExecutionAdvice;

    //切面表达式   com.wang.seckill.service包下面所有类的所有方法 任意返回类型
    @Pointcut("execution(* com.wang.seckill.service.*.*(..))")
    public void point(){
    }

    @Before("point()")
    public void exeBefore(){
        testExecutionAdvice.exeBefore();
    }

    @After("point()")
    public void exeAfter(){testExecutionAdvice.exeAfter();}

    @AfterReturning("point()")
    public void exeReturn(){testExecutionAdvice.returning();}


    @Around("point()")
    public void exeRound(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {testExecutionAdvice.exeRound(proceedingJoinPoint);}

    @AfterThrowing("point()")
    public void exeThrowing(){
        testExecutionAdvice.throwing();
    }

}

 正常通知:

 

发生异常的通知:

问题

@Around  增强的方法有返回,那么通知也要有返回值

org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public int com.wang.seckill.service.SeckillServiceImpl.testAspect()
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值