Spring AOP的简单应用

2020-06-02 10:06:28 浏览数 (1)

在各种业务场景中,我们可会有打印日志这种语句,通常为了方便我们直接写在业务逻辑的代码中。实际上和业务无关的代码我们也放入到业务逻辑中,会带来了较强的侵入性编码。通常来说,日志和业务代码应该是分离的,而Spring AOP能很好的实现日志和业务代码的分离,当然Spring AOP的作用不仅仅是用来打印日志的,还可以用来做权限控制,缓存等等......

AOP也叫做面向切面编程(Aspect-Oriented Programming),它的核心是动态代理,了解过设计模式的小伙伴应该都知道代理模式,代理模式包含两大类:静态代理和动态代理。静态代理这里就不说了;动态代理实现方法我知道的有两种:JDK自带的动态代理和CGLIB的动态代理,JDK自带的动态代理基于反射,所以效率相对低,而且只能代理实现接口的对象。CGLIB的动态代理基于字节码实现(这比反射效率高)而且可以代理没有实现接口的对象,但是不能代理final方法。

这篇文章我们先简单实现利用AOP实现日志打印,然后再看相关注解含义。

相关文章: 代理模式

Spring Boot的搭建

01

利用AOP实现日志打印

项目中我们首先导入相关依赖:

代码语言:javascript复制
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

题外话:我是真的觉得现在SpringBoot集成其他框架很简单,我在公司项目上,想要集成AOP做自己开发代码的日志监控,先要导入六七个jar包,然后写(抄)Spring的配置文件,修改(抄)web.xml文件,然后还失败了,因为配置AOP之后,初始化容器报错说不支持XML Schema,只能用XML DTD,最后东改西改的成功启动了,但是AOP还没有集成成功,有这方面有经验的小伙伴请留言指导(我对配置文件基本是完全不懂)。

当我们导入完依赖之后,我们就可以写自己的Aspect类,用于监控方法。

代码语言:javascript复制
package xcx.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @Auther: chenlong
 * @Date: 2019/3/2 16:19
 * @Description:
 */

@Aspect
@Component
public class MyAspect {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* xcx.request.controller.*.*(..))")
    public void logging() {
    }

    @Before(value = "logging() ")
    public void logStart(JoinPoint joinPoint) {
        logger.info(joinPoint.getSignature().getName()   "运行,参数列表是: {"   Arrays.asList(joinPoint.getArgs())   "}");
    }

    @After(value = "logging()")
    public void logEnd(JoinPoint joinPoint) {
        System.out.println(joinPoint.getSignature().getName()   "结束...");
    }

    @AfterReturning(value = "logging()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        System.out.println(joinPoint.getSignature().getName()   "正常结束,结果是: {"   result   "}");
    }

    @AfterThrowing(value = "logging()", throwing = "e")
    public void logException(JoinPoint joinPoint, Exception e) {
        System.out.println(joinPoint.getSignature().getName()   "异常,异常信息: {"   e.getMessage()   "}");
    }

}

然后.....就没了,我们启动项目然后访问xcx.request.controller下的API就会打印相应日志了:是不是很简单,这样就成功的将业务逻辑和日志打印成功分离,我这里为了测试将execution配置范围比较大,大家可以指定到某个具体的方法,进行单独监控。

下面我们再来看看这些注解有什么含义

02

AOP的术语

通知(Advice):

Spring中的切面一共提供5种通知的类型:

  前置通知(Before)   后置通知(After)   返回通知(After-Running)   异常通知(After-throwing)   环绕通知(Around)

前面4个较为容易理解,例如“前置通知”,我们通常在一个方法的第一句打印出传入的方法参数,此时就可以使用前置通知在方法调用前打印出传入的参数。对于“后置通知”实际是“返回通知”和“异常通知”的并集,返回通知表示程序正确运行返回后执行,异常通知表示程序不正常运行抛出异常时执行,而后置通知则不论程序是否正确运行,一旦离开方法就会执行。

环绕通知最为强大,它包裹了被通知的方法,可同时定义前置通知和后置通知。

切点(Pointcut):

通知定义了何时工作以及工作内容,切点则定义了在何处工作,也就是在哪个方法应用通知。要表达出在哪个方法中运用通知,这需要用到切点表达式。Spring AOP借助AspectJ(另一种AOP实现)的切点表达式来确定通知被应用的位置,虽然是借助但并不支持所有AspectJ的所有切点指示器而仅仅是其一个子集,这其中最为常用的就是execution切点指示器,表示执行。就如同上述代码:

代码语言:javascript复制
 @Pointcut("execution(* xcx.request.controller.*.*(..))")

execution()是最常用的切点函数,其语法如下所示:

整个表达式可以分为五个部分:

1、execution(): 表达式主体。

2、第一个*号:表示返回类型,*号表示所有的类型。

3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

4、第二个*号:表示类名,*号表示所有的类。

5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

到这里就引入Spring AOP到项目中,集成很简单,但是明白原理才是最重要的,在文章的开始我大致的说了AOP是如何实现的,但是很浅显,下篇文章我们再详细的看一下动态代理。

0 人点赞