Spring 切面编程让你的代码策马奔腾!

昨天介绍了自定义校验器注解的使用,后续自己可以再添加一个统一异常处理类,一套参数校验框架就出炉了,说道异常处理类,就要使用到spirng的aop

给介绍一下自定义注解 + spring boot切面的使用

1. 声明一个自定义注解(一个比较简单的demo)

这注解是我用来标注哪些方法需要参与spring mybatis 的数据库分表,这个会在下一篇介绍

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SplitParam {

    /**
     *
     * 使用参数列表的索引
     * @return
     */
    int index() default 0;
}

同样关于java的自带的注解@Retention@Target就不过多介绍了,具体的枚举类型使用可以参见javadoc

2. 定义一个切面处理自定义注解SplitParamAspect

我这里使用的是spring boot 配置注解,让spring扫描到就ok了

3. 切面的定义


@Order(Ordered.HIGHEST_PRECEDENCE + 100)
@Component
@Aspect
@Slf4j
public class SplitParamAspect

@Order: 这个注解定义了你的自定义注解被处理的顺序,value指定了优先级(可选的),value的值越小被处理的优先级越高,相反则越低,这个值因人而异,如果项目中自定义注解比较多的化建议加上

    /**
     * 最高优先级
     * @see java.lang.Integer#MIN_VALUE
     */
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

    /**
     * 最低优先级
     * @see java.lang.Integer#MAX_VALUE
     */
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

@Component 这个在 spring boot中经常被用到,看注释就能看懂意思了:表示标有此注释的类是一个组件,并且呢,如果使用在spring 的配置上(也就是注解@Configuration),后者这个注解类在@ComponentScan的扫描路径下的化,此类会被自动扫描auto-detection

@Aspect 这个是切面的核心注解,声明一个切面

4. 切面的使用

上面介绍了切面编程的首要工作,可能你现在对整个逻辑还云里雾里,别慌,下面马上介绍:

举个例子:一个比较简单的场景
一个类中有一个的方法,在执行此方法前我想获取到方法的参数值并放到ThreadLocal中,执行完毕在后从ThreaLocal中删除。

为了高内聚低耦合,业务的方法应该单独封装在一个类里面,参数放入和参数删除的功能应该另外封装,但是在调用时必须按照顺序来调用执行,这时候就需要用到切面编程了

或者统计一个项目中这个方法被调用的次数;

或者在方法的执行前后打印时间统计方法的执行时间等等;

  • [x] 切入点(Pointcut):在你方法上使用切点,表明这是一个需要处理的点。这里我们没有直接在方法使用@PointCut(value=切点表达式)来直接表明这是一个切点,而是在方法上使用了自定义注解@SplitParam,然后使用@PointCut(@SplitParam路径)标明含有此注解的方法全部作为切点
  • [x] 通知、增强处理(Advice):就是上述你想要把参数放入到ThreadLoal、执行完毕后从ThreadLocl中移除的功能;
  • [x] 连接点(JoinPoint): Spring允许你是Advice的地方,基本方法前、方法后、或者是(Around方法)、抛出异常都可以是连接点
  • [x] 切面(Aspect):切面是你处理功能点,全部实现的总称

5. 具体实现之一码便知


import com.google.common.collect.Maps;
import com.huatu.common.exception.BizException;
import com.huatu.tiku.push.annotation.SplitParam;
import com.huatu.tiku.push.constant.NoticePushErrors;
import com.huatu.tiku.push.dao.strategy.Strategy;
import com.huatu.tiku.push.util.ConsoleContext;
import com.huatu.tiku.push.util.ThreadLocalManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.NumberUtils;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * 描述:参数处理切面
 *
 * @author biguodong
 * Create time 2018-12-04 下午8:37
 **/

@Order(Ordered.HIGHEST_PRECEDENCE + 100)
@Component
@Aspect
@Slf4j
public class SplitParamAspect {

    @Pointcut(value = "@annotation(com.yourpath.SplitParam)")
    public void pointCut(){

    }

    /**
     * joinPoint 一种定义方式-直接使用注解简单粗暴
     * 代码逻辑因人而异
     * 放入本地线程中
     * @param joinPoint
     * @param splitParam
     */
    @Before("@annotation(splitParam)")
    public void before(JoinPoint joinPoint, SplitParam splitParam){
        Object[] argsObject = joinPoint.getArgs();
        if(argsObject.length == 0){
            throw new BizException(NoticePushErrors.TABLE_SPLIT_PARAMS_EMPTY);
        }
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        try{
            Object value = argsObject[splitParam.index()];
            if(!(value instanceof Number)){
                throw new BizException(NoticePushErrors.TABLE_SPLIT_PARAMS_TYPE_ERROR);
            }
            Long valueL = NumberUtils.convertNumberToTargetClass((Number) value, Long.class);
            ConsoleContext consoleContext = ConsoleContext.getInstance();
            Map<String, Object> params = Maps.newHashMap();
            params.put(Strategy.USER_ID, valueL);
            consoleContext.setRequestHeader(params);
            ThreadLocalManager.setConsoleContext(consoleContext);
        }catch (Exception e){
            log.error("pars split params value error, method:{}", method.getName());
        }
    }

    /**
     * joinPoint的另外一中实现方式
     * 切面执行完后移除
     */
    @AfterReturning("pointCut()")
    public void after(){
        ThreadLocalManager.clear();
    }

    @AfterThrowing(value = "pointCut()", throwing = "throwable")
    public void afterException(Throwable throwable){
        if(null != throwable && null != throwable.getCause()){
            log.error(" run aspect caught an error:{}", throwable.getCause().getMessage());
        }
        throw new BizException(NoticePushErrors.TABLE_SPLIT_PARAMS_AOP_ERROR);
    }
}


上述可以看到我有两种方式实现了连接点@JointPoint;

Before上直接用的注解形式;

AfterReturning上则是用的自定义的PointCut()方法,这个方上使用了@PointCut()来声明切点表达式,效果都是一样的;

马上用起来吧!

end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343