实战用法:
- 1:接口方法
package com.xxx.xxx.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/api/open")
public class OpenController {
@RequestMapping(value = "/hello", method = { RequestMethod.GET })
public JsonResult hello() {
return new JsonResult("welcome to HOME");
}
}
复制代码
- 2:定义切面
package com.xxx.xxx.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
@Aspect
@Component
@Order(2)
public class TestAspect {
protected static Logger logger = LoggerFactory.getLogger(TestAspect.class);
/**
* 第一个.*:相当于com.mdtech.zfbw.controller下的所有类 第二个。*:相当于类下的所有方法 (..):所有方法的参数
*/
//可监听在controller包中,也可监听server包中
@Pointcut("execution(public * com.xxx.xxx.controller.OpenController.hello(..))")
public void hello() {
}
/**
* 前置通知,请求方法之前去做拦截,走完前置(打印请求信息),才会进行目标方法
*
* @param joinPoint
*/
@Before("hello()")
public void doBefore(JoinPoint joinPoint) {
logger.info("test1");
}
/**
* 后置通知,调用完目标方法后,执行此方法(打印返回信息)
*
* @param ret
*/
@AfterReturning(returning = "ret", pointcut = "hello()")
public void doAfterReturning(JoinPoint joinPoint,Object ret) {
logger.info("test2");
}
}
复制代码
-
3:预期结果
test1
welcome to HOME
test2
结果分析
- 接口处定义了 hello 的方法,正常输出应该是 welcome to HOME
- 随后定义的 切面Aspect @Before注解会在方法调用之前先执行,也就是执行了 logger.info("test1"); @AfterReturning 注解会在方法调用之后执行,也就是目标方法(hello())执行后,执行了logger.info("test2");
切面类全面分析
-
@Aspect 定义了一个切面类
-
@Component 定义了一个bean,并把他交由spring进行管理
-
@Order(2) 定义了这个切面类的执行顺序
-
比如定义了两个切面类 Aspect1 和 Aspect2
- 大家都会对hello()这个方法进行输出,那么,@order()这个注解就可以定义了执行的先后顺序,数值越小,越先执行
-
-
@Pointcut("execution(public * com.xxx.xxx.controller.OpenController.hello(..))")
-
定义了一个切点,切点就是连接点的集合,而连接点指的就是一个方法,比如 hello方法就是一个连接点,证明切点包含了连接点
-
execution用法说明
-
格式:execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
- 其中后面跟着“?”的是可选项
- 修饰符匹配(modifier-pattern?)
- 返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
- 类路径匹配(declaring-type-pattern?)
- 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set开头的所有方法
- 参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“ ”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;( ,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(..)表示零个或多个任意参数
- 异常类型匹配(throws-pattern?)
-
具体例子说明
-
execution(* *(..))
- 表示匹配所有方法
-
execution(public * com. xxx.xxx.xxxService.*(..))
- 表示匹配com. xxx.xxx.xxxService中所有的公有方法
-
execution(* com.xxx.xxx.. . (..))
- 表示匹配com.xxx.xxx包及其子包下的所有方法
-
-
-
从execution的定义上来看,可以知道,确实一个切点是可以包含很多连接点的
-
-
在Spring 2.0中,Pointcut的定义包括两个部分:Pointcut表示式(expression)和Pointcut签名(signature)
- 比如:
@Pointcut("execution(public * com.xxx.xxx.controller.OpenController.hello(..))")
public void hello() {
}
复制代码
- 这个hello()的这个方法就是Pointcut的签名,可以说以后hello()等同于execution(public * com.xxx.xxx.controller.OpenController.hello(..))这个用法,比如下面这里
复制代码
@Before("hello()")
public void doBefore(JoinPoint joinPoint) {
logger.info("test1");
}
复制代码
等同于==
复制代码
@Before("execution(public * com.xxx.xxx.controller.OpenController.hello(..))")
public void doBefore(JoinPoint joinPoint) {
logger.info("test1");
}
复制代码
-
通知类型注解合集
-
@Before
- 参数:切点签名
- 如:@Before("hello()")
-
@AfterReturning
- 参数:returning(返回参数的定义), pointcut(切点)
- 如:
-
@AfterReturning(returning = "ret", pointcut = "hello()")
public void doAfterReturning1(JoinPoint joinPoint,Object ret) {
logger.info("test2");
}
复制代码
-
@AfterThrowing
- 参数:切点签名
- 如:@AfterThrowing("hello()")
-
@After
- 参数:切点签名
- 如:@AfterThrowing("hello()")
-
@Around
- 这种最灵活,既能做 @Before 的事情,也可以做 @AfterReturning 的事情
- 参数:切点签名
- 如:@Around("hello()")