Springboot通过自定义注解实现AOP

AOP的一大好处就是解耦。通过切面,我们可以将那些反复出现的代码抽取出来,放在一个地方统一处理。
同时,抽出来的代码很多是与业务无关的,这样可以方便开发者更加专注自己的业务逻辑的开发。
AOP对定时任务不起作用,因为定时任务的bean不是在Spring管理,需要写代码让定时任务的bean也被Spring管理,才能切入,简单的方法是改方法加个ReqeustMap通过url调用。

基础概念

1、切面(Aspect)
首先要理解‘切’字,需要把对象想象成一个立方体。
传统的面向对象编程思维,类定义完成之后。每次实例化一个对象,对类定义中的成员变量赋值,就相当于对这个立方体进行了一个定义,定义完成之后,那个对象就在那里等着被使用,等着被回收。

面向切面编程则是指,对于一个我们已经封装好的类,我们可以在编译期间或在运行期间,对其进行切割,把立方体切开,在原有的方法里面添加(织入)一些新的代码,对原有的方法代码进行一次增强处理。而那些增强部分的代码,就被称之为切面,如:通用日志处理代码,常见的还有事务处理、权限认证等等。

2、切入点(PointCut)
要对哪些类中的哪些方法进行增强,进行切割,指的是被增强的方法。即要切哪些东西。

3、连接点(JoinPoint)
我们知道了要切哪些方法后,剩下的就是什么时候切,在原方法的哪一个执行阶段加入增加代码,这个就是连接点。如方法调用前,方法调用后,发生异常时等等。

4、通知(Advice)
通知被织入方法,该如何被增强。定义切面的具体实现。那么这里面就涉及到一个问题,空间(切哪里)和时间(什么时候切,在何时加入增加代码),空间我们已经知道了就是切入点中定义的方法,而什么时候切,则是连接点的概念,如下面实例中,通用日志处理(切面),@Pointcut规则中指明的方法即为切入点,@Before、@After是连接点,而下面的代码就是对应通知。

@Pointcut("@annotation(com.guo.annotation.banktype)")
private void cutMethod() {

}

@Before("cutMethod()")
public void begin() {
    System.out.println("==@Before==");
}

5、目标对象(Target Object)
被一个或多个切面所通知的对象,即为目标对象。

6、AOP代理对象(AOP Proxy Object)
AOP代理是AOP框架所生成的对象,该对象是目标对象的代理对象。代理对象能够在目标对象的基础上,在相应的连接点上调用通知。

7、织入(Weaving)
将切面切入到目标方法之中,使目标方法得到增强的过程被称之为织入。

具体的aop实现

  1. 引入依赖
        <!--aop切面相关-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  1. 自定义注解
package com.pay.payee.service.aop.annotation;

import java.lang.annotation.*;

/**
 * @ClassName: CheckWorkDay
 * @Description: 工作日的自定义注解
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/5/4 18:03
 * @Copyright:
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckWorkDay {

    //自定义工作日数组,值为星期几,如果是多个日期,用逗号分割。
    String workDays() default "1,2,3,4,5";

}
  1. AOP绑定自定义注解,并在AOP中实现处理逻辑。取得注解中配置的工作日(周几的拼接字符串)判断今天是否是工作日配置列表中。后续可以注入其他service结合数据库进行判断。
package com.pay.payee.service.aop;

import com.pay.common.exception.NotWorkdayException;
import com.pay.common.util.DateUtils;
import com.pay.common.util.StringUtils;
import com.pay.payee.service.aop.annotation.CheckWorkDay;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

@Aspect //定义一个切面类
@Component
@Slf4j
public class CheckWorkdayAspect {

    //切入点表达式决定了用注解方式的方法切还是针对某个路径下的所有类和方法进行切,方法必须是返回void类型
    @Pointcut("@annotation(com.pay.payee.service.aop.annotation.CheckWorkDay)")
    private void checkWorkdayCut() {
    }

    //定义了切面的处理逻辑。即方法上加了@checkWorkday
    @Around("checkWorkdayCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        log.info("==========进入AOP============");
        //1.记录日志信息
        Signature signature = pjp.getSignature();
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = signature.getName();
        log.info("className:{},methodName:{}", className, methodName);

        //2.检查工作日
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        if (targetMethod.isAnnotationPresent(CheckWorkDay.class)) {
            //获取方法上注解中标明的工作日信息
            CheckWorkDay permission = (CheckWorkDay) targetMethod.getAnnotation(CheckWorkDay.class);
            String workDays = permission.workDays();
            log.info("当前接口请求的工作日数据是:{}", workDays);
            if (StringUtils.isNotEmpty(workDays)) {
                String[] workDayArr = workDays.split(",");//接口允许的工作日

                String currentDate = DateUtils.getCurrentDate("yyyy-MM-dd");
                log.info("今天日期是:" + currentDate);
                String week = DateUtils.getWeekNum(currentDate);
                System.out.println("今天是星期: " + week);
                List<String> list = Arrays.asList(workDayArr);
                //判断今天是否在注解配置的周几范围
                if (list.contains(week)) {
                    log.info("注解中配置的星期数组跟今天进行校验通过,进入业务层处理!");
                    //3.执行业务逻辑,放行
                    return pjp.proceed();
                } else {
                    //如果校验不通过,抛出异常,由Spring框架捕获
                    throw new NotWorkdayException("不在工作日配置范围内");
                }
            }
        }
        throw new NotWorkdayException("没有配置注解CheckWorkDay的类");
    }

}

NotWorkdayException

package com.pay.common.exception;

/**
 * @ClassName: BankConnectionException
 * @Description:
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/5/2 16:15
 * @Copyright:
 */
public class NotWorkdayException extends RuntimeException {

    public NotWorkdayException(String message) {
        super(message);
    }
}

DateUtils

 /*
     * @Description 取得日期是数字的星期几
     * @Param [sdate]
     * @return int
     */
    public static String getWeekNum(String sdate) {
        Date date = DateUtils.strToDate(sdate);
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int weekday = c.get(Calendar.DAY_OF_WEEK) - 1;
        return "" + weekday;
    }

    public static Date strToDate(String strDate) {
        if (strDate == null) {
            return null;
        }
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        ParsePosition pos = new ParsePosition(0);
        Date strtodate = formatter.parse(strDate, pos);
        return strtodate;
    }

    public static String getCurrentDate(String aFormat) {
        String tDate = new SimpleDateFormat(aFormat).format(new java.util.Date(System.currentTimeMillis()));
        return tDate;
    }
  1. Controller的需要AOP的方法添加注解,配置工作日是周二和周五。
    @ApiVersion(5)
    @CheckWorkDay(workDays = "2,5")
    @RequestMapping(value = "/getSendFileHandlerByChannel")
    // 加入接口的版本控制http://localhost:8555/v5/packageIndex/getSendFileHandlerByChannel
    public JsonResult getSendFileHandlerByChannel(@RequestParam String channel) {
        ISendFileHandler sendFileHandler = bankHandlerFactory.getSendFileHandlerByChannel(channel);
        sendFileHandler.sendFileToBankScheduledTask();
        return JsonResult.of(sendFileHandler, true, "成功调用");
    }

访问接口,如果今天是周一将抛出异常

{
  "data": "不在工作日配置范围内;",
  "flag": false,
  "code": 5001,
  "msg": "NOT_IN_WORK_DAY"
}

如果今天在配置的工作日范围,则正常进入controller的方法体内部继续执行。

改进

从数据库读取工作日的配置而不是通过注解编码在代码里面。
修改的代码在类CheckWorkdayAspect里面。

        if (targetMethod.isAnnotationPresent(CheckWorkDay.class)) {
            //获取方法上注解中标明的工作日信息
            CheckWorkDay checkWorkDay = (CheckWorkDay) targetMethod.getAnnotation(CheckWorkDay.class);
            String workDays = checkWorkDay.workDays();
            log.info("****来自注解配置****工作日数据是:{}", workDays);
            String wtimeId = bzSetupWorktimeService.findById(8159880589902544002l).orElse(new BzSetupWorktimeEntity()).getWtimeId();
            log.info("从数据库中根据ID:8159880589902544002,取出的wtimeId = " + wtimeId);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352