先介绍一些aop的名词,其实这些名词对使用aop没什么影响,但为了更好的理解最好知道一些
- 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。
- 连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
- 通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
- 切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
- 引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
- 目标对象(Target Object):被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
- AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
- 织入(Weaving):把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
其中重要的名词有:切面,切入点
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
spring-boot-starter-aop会引入spring-aop
和aspectjweaver
。引入aspectjweaver是为了使用AspectJ的注解功能。
注解说明
- @Aspect:描述一个切面类,定义切面类的时候需要打上这个注解
- @Pointcut:声明一个切入点,切入点决定了连接点关注的内容,使得我们可以控制通知什么时候执行。Spring AOP只支持Spring bean的方法执行连接点。所以你可以把切入点看做是Spring bean上方法执行的匹配。一个切入点声明有两个部分:一个包含名字和任意参数的签名,还有一个切入点表达式,该表达式决定了我们关注那个方法的执行。作为切入点签名的方法必须返回void 类型。
- @Before:前置通知:在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。
- @AfterReturning:后置通知:在某连接点正常完成后执行的通知,通常在一个匹配的方法返回的时候执行。
- @AfterThrowing:异常通知:在方法抛出异常退出时执行的通知。
- @After:最终通知。当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
- @Around:环绕通知:包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。
通知参数
任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型(环绕通知需要定义第一个参数为ProceedingJoinPoint类型,它是 JoinPoint 的一个子类)。JoinPoint接口提供了一系列有用的方法,比如 getArgs()(返回方法参数)、getThis()(返回代理对象)、getTarget()(返回目标)、getSignature()(返回正在被通知的方法相关信息)和 toString()(打印出正在被通知的方法的有用信息)。
切入点表达式
切入点表达式的格式:execution([可见性] 返回类型 [声明类型].方法名(参数) [异常])
其中【】中的为可选,其他的还支持通配符的使用:
*:匹配所有字符
..:一般用于匹配多个包,多个参数
+:表示类及其子类
运算符有:&&、||、!
实例
声明一个切面:
package me.yanand.config; 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.context.annotation.Configuration; import org.springframework.util.StopWatch; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; @Configuration @Aspect public class AspectConfig { private static final Logger LOGGER = LoggerFactory.getLogger(AspectConfig.class); @Pointcut("execution(* me.yanand.controller.TestController.*(..))") public void toLog(){}; @Before("toLog()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); LOGGER.info("IP:{}",request.getRemoteAddr()); LOGGER.info("请求方法:{}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()); } @After("toLog()") public void doAfter(JoinPoint joinPoint){ LOGGER.info("请求结束:{}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()); } @Around("toLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint){ StopWatch stopWatch = new StopWatch(); stopWatch.start(); try { Object obj = proceedingJoinPoint.proceed(); return obj; } catch (Throwable throwable) { throwable.printStackTrace(); }finally { stopWatch.stop(); LOGGER.info("耗时ms:{}",stopWatch.getTotalTimeMillis()); } return null; } }
测试:
转载请注明:Terry's blog » springboot整合aop