Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing 您所在的位置:网站首页 注解的定义和使用 Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing

Spring AOP详解一文搞懂@Aspect、@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing

2024-04-07 06:46| 来源: 网络整理| 查看: 265

文章目录 1、AOP是什么2、AOP中注解的含义3、Pointcut切入点的语法4、AOP代码实现

1、AOP是什么

AOP:Aspect Oriented Programming,翻译过来就是大名鼎鼎的“面向切面编程”,它是对面向对象的一种补充和完善。 AOP的使用场景一般有:数据源切换、事务管理、权限控制、日志打印等。根据它的名字我们不难理解,它的实现很像是将我们要实现的代码切入业务实现的逻辑中。它有以下特点: 1、侵入性小,几乎可以不改动原来逻辑的情况下把新的逻辑加入业务。 2、实现方便,使用几个注解就可以实现,使系统更容易扩展。 3、更好的复用代码,比如事务日志打印,简单逻辑适合所有情况。

相关代码请参考: chapter-4-springmvc-sourcecode-analysis https://gitee.com/leo825/spring-framework-learning-example.git

2、AOP中注解的含义

@Aspect:切面。表示一个横切进业务的一个对象。它里面包含切入点(Pointcut)和Advice(通知)。 @Pointcut:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。 @Before:Advice(通知)的一种,切入点的方法体执行之前执行。 @Around:Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。 @After:Advice(通知)的一种,在切入点正常运行结束后执行。 @AfterReturning:Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行 @AfterThrowing:Advice(通知)的一种,在切入点运行异常时执行。

3、Pointcut切入点的语法

语法规则如下代码中注释所示:

/** * 1、使用within表达式匹配 * 下面示例表示匹配com.leo.controller包下所有的类的方法 */ @Pointcut("within(com.leo.controller..*)") public void pointcutWithin(){ } /** * 2、this匹配目标指定的方法,此处就是HelloController的方法 */ @Pointcut("this(com.leo.controller.HelloController)") public void pointcutThis(){ } /** * 3、target匹配实现UserInfoService接口的目标对象 */ @Pointcut("target(com.leo.service.UserInfoService)") public void pointcutTarge(){ } /** * 4、bean匹配所有以Service结尾的bean里面的方法, * 注意:使用自动注入的时候默认实现类首字母小写为bean的id */ @Pointcut("bean(*ServiceImpl)") public void pointcutBean(){ } /** * 5、args匹配第一个入参是String类型的方法 */ @Pointcut("args(String, ..)") public void pointcutArgs(){ } /** * 6、@annotation匹配是@Controller类型的方法 */ @Pointcut("@annotation(org.springframework.stereotype.Controller)") public void pointcutAnnocation(){ } /** * 7、@within匹配@Controller注解下的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.CLASS) */ @Pointcut("@within(org.springframework.stereotype.Controller)") public void pointcutWithinAnno(){ } /** * 8、@target匹配的是@Controller的类下面的方法,要求注解的@Controller级别为@Retention(RetentionPolicy.RUNTIME) */ @Pointcut("@target(org.springframework.stereotype.Controller)") public void pointcutTargetAnno(){ } /** * 9、@args匹配参数中标注为@Sevice的注解的方法 */ @Pointcut("@args(org.springframework.stereotype.Service)") public void pointcutArgsAnno(){ } /** * 10、使用excution表达式 * execution( * modifier-pattern? //用于匹配public、private等访问修饰符 * ret-type-pattern //用于匹配返回值类型,不可省略 * declaring-type-pattern? //用于匹配包类型 * name-pattern(param-pattern) //用于匹配类中的方法,不可省略 * throws-pattern? //用于匹配抛出异常的方法 * ) * * 下面的表达式解释为:匹配com.leo.controller.HelloController类中以hello开头的修饰符为public返回类型任意的方法 */ @Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))") public void pointCut() { }

需要注意:上面的匹配的类型中支持或(||)与(&&)非(!)运算。

4、AOP代码实现

首先配置pox.xml进行注解jar包的支持

org.aspectj aspectjrt 1.8.9 org.aspectj aspectjweaver 1.8.9

然后在配置文件中启动注解支持,例如我的是myspringmvc-servlet.xml文件,这里需要注意的是注解要跟配置文件在一块,举个例子:如果需要切入Controller的配置就需要在myspringmvc-servlet.xml中添加,如果是切入应用的配置如Service则需要在applicationContext.xml中添加。

Aop核心代码如下:

ThreadLocal startTime = new ThreadLocal(); /** * 10、使用excution表达式 * execution( * modifier-pattern? //用于匹配public、private等访问修饰符 * ret-type-pattern //用于匹配返回值类型,不可省略 * declaring-type-pattern? //用于匹配包类型 * name-pattern(param-pattern) //用于匹配类中的方法,不可省略 * throws-pattern? //用于匹配抛出异常的方法 * ) * * 下面的表达式解释为:匹配com.leo.controller.HelloController类中以hello开头的修饰符为public返回类型任意的方法 */ @Pointcut(value = "execution(public * com.leo.controller.HelloController.hello*(..))") public void pointCut() { } /** * 在方法执行之前执行 * * @param joinPoint */ @Before(value = "pointCut()") public void beforeLog(JoinPoint joinPoint) { System.out.println("进入LogAop的beforeLogger"); startTime.set(System.currentTimeMillis()); } /** * 在进入类之前执行,然后返回pjp.proceed()之前执行before,再执行方法体,在到after * * @param */ @Around(value = "pointCut()") public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable { System.out.println("进入LogAop的aroundLogger"); return pjp.proceed(); } /** * 在方法执行返回之后执行 */ @After(value = "pointCut()") public void afterLog() { System.out.println("进入LogAop的afterLogger"); long start = startTime.get(); System.out.println("方法体执行耗时:" + (System.currentTimeMillis() - start) + "ms"); startTime.remove(); } /** * 在返回之后执行 * @param o */ @AfterReturning(value = "pointCut()",returning = "o") public void afterRunningLog(Object o){ System.out.println("进入LogAop的afterRunningLog"); System.out.println(o.getClass()); } /** * 在产生异常时执行 */ @AfterThrowing(value = "pointCut()") public void afterThrowingLog(){ System.out.println("进入LogAop的afterThrowingLog"); }

开启debug模式,查看各个标签执行顺序; 1、断点进入@Around位置 断点 2、执行完pjp.proceed()之后,开始进入@Before方法 断点2 3、执行完@Before中的方法后进入主方法 主方法 4、执行完主方法体,又回到了@Around的返回值 回来了 5、然后开始执行@After中内容 ater内容 6.最后到了@AfterReturning方法体中 最后到了 所以,正常的执行顺序是:@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterReturning

如果存下异常那执行顺序是什么呢?以下模拟了一个异常。 1、首先进入了@Around的方法 异常1 2、进入了@After的方法 异常2 3、最后进入了@AfterThrowing的方法 异常3 因此如果异常在Around中pjp.proceed()之前,则执行顺序为:@Around -> @After -> @AfterThrowing

同理,如果异常在Around中pjp.proceed()之后,则执行顺序为@Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterThrowing*



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有