基于注解的Spring AOP实现demo

一直想找机会理解下Spring的AOP,趁着这段时间还行,就照着网上的例子,写了两个基于注解/XML的demo.这两个demo参考了开涛,嘟嘟MD两位大大的博客,有兴趣的可以看下.

为了理解AOP,我们必须先了解AOP的相关术语,很简单不难:

通知(Advice):在AOP中,切面的工作被称为通知。通知定义了切面“是什么”以及“何时”使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。
Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
  • 返回通知(After-returning):在目标方法成功执行之后调用通知
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

运行顺序:前置通知--目标方法执行--返回通知/异常通知--后置通知
PS: 环绕通知也可以称作自定义通知,这是最强大的一种通知类型,环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行

连接点(Join point):连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加行为。
切点(Pointcut):如果说通知定义了切面“是什么”和“何时”的话,那么切点就定义了“何处”。比如我想把日志引入到某个具体的方法中,这个方法就是所谓的切点。
切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容———他是什么,在何时和何处完成其功能。

package com.sam.aop.aspectj;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
 * 切面类
 * Created by sam on 16/6/1.
 */
@Order(1)
@Aspect
@Component
public class AspectjLog {
    private Logger logger = LoggerFactory.getLogger(AspectjLog.class);
  
    //此处定义一个通用的切点,以便下方4个通知使用
    @Pointcut("execution(* com.sam.aop.service.AspectService.*(..))")
    public void logAop() {
    }

    // 如果没有定义公用的切点或者需要单独指定其他切点,可以使用表达式指定
    // @Before("execution(* com.sam.aop.service.AspectService.*(..)) && args(email)")
    @Before("logAop() && args(email)")
    public void logBefore(JoinPoint joinPoint,String email) {
        logger.debug("前置通知Before>>{}", email);
    }

    @AfterReturning("logAop() && args(email)")
    public void logAfterReturning(String email) {
        logger.debug("返回通知AfterReturning>>{}", email);
    }

    @After("logAop() && args(email)")
    public void logAfter(String email) {
        logger.debug("后置通知After>>{}", email);
    }

    @AfterThrowing("logAop() && args(email)")
    public void logAfterThrow(String email) {
        logger.debug("异常通知AfterThrowing>>{}", email);
    }

    //环绕通知功能很强,可以替代上面的所有通知
    @Around("logAop() && args(email)")
    public void logAround(ProceedingJoinPoint jp, String email) {
        try {
            logger.debug("自定义前置通知Before>>>{}", email);
            jp.proceed();//将控制权交给被通知的方法,也就是执行sayHello方法
            logger.debug("自定义返回通知AfterReturning>>>{}", email);
        } catch (Throwable throwable) {
            logger.debug("异常处理>>>>{}", email);
            throwable.printStackTrace();
        }
         logger.debug("自定义后置通知After>>>{}", email);
    }
}
  • 当调用AspectService的方法时,就会触发通知
/**
 * Created by sam on 16/6/1.
 */
@Service
public class AspectService implements IAspectService {

    private Logger logger = LoggerFactory.getLogger(AspectService.class);

    @Override
    public void sayHello(String email) {
        logger.debug("sayHello to >>>>{}", email);
    }
}
public interface IAspectService {  
     void sayHello(String email);
}
  • controller中
 @Autowired
 AspectService aspectService;

 @RequestMapping("/aop")public String aop(Model model) {    
      aspectService.sayHello("sam@balabala.com");      
      model.addAttribute("hello", "您好! AOP");      
      model.addAttribute("world", "everything is AOP");   
      return "sam";
}
  • console中的执行顺序如图


    Paste_Image.png
  • 关于执行顺序的相关说明 : 你会发现这里的环绕通知先于前置通知执行了,而实际上,如果两个 advice 位于同一 aspect 内,且执行顺序有先后,通过 advice 的声明顺序是无法确定其执行顺序的,因为 advice 方法的声明顺序无法通过反射获取.而如果两个advice位于不同的aspect的话,可以通过如下两种方式控制 Aspect 的施加顺序:

1.Aspect 类添加注解:org.springframework.core.annotation.Order
顺序值:使用注解属性指定
2.Aspect 类实现接口:org.springframework.core.Ordered
顺序值:实现 Ordered 接口的 getOrder() 方法即可

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

推荐阅读更多精彩内容