我这里要演示的就是一个简单的AOP编程的方式,其中使用了自定义注解和正常的切面
直接上代码
1.自定义注解,具体每个注解的解释自己百度
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
2.定义切面类
使用@Aspect注解的类,才能定义切面以及切面@Pointcut的增强方法@before,@After@around...
@Aspect
@Configuration
public class SysLogAspect {
/**
* 这里我们使用注解的形式,只要使用了@SysLog的注解都会调用这个方法,而这个方法是个切面方法,
* 会被增强
* 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
* 切点表达式: execution(...)
*/
@Pointcut("@annotation(com.wexin.aop.SysLog)")
public void logPointCut1(){}
/**
* 这里就是正常的切面方法,指定到方法和参数,
* 第一个*通配 权限修饰符和返回参数
* (..) 表示通配入参
*/
@Pointcut("execution(* com.wexin.aop.SysLogService.save(..))")
public void logPointCut2(){}
/**
* 环绕通知 @Around , 当然也可以使用 @Before (前置通知) @After (后置通知)
* @param point
* @return
* @throws Throwable
*/
@Around("logPointCut1()")
public Object around() throws Throwable {
long beginTime = System.currentTimeMillis();
System.out.println("环绕之前");
Object result = point.proceed();
System.out.println("环绕之后");
long time = System.currentTimeMillis() - beginTime;
try {
saveLog(time);
} catch (Exception e) {
}
return result;
}
/**
* 保存日志
* @param joinPoint
* @param time
*/
public void saveLog(long time) {
System.out.println("保存日志 :" +time);
}
@Before("logPointCut1()")
public void beforeT(){
System.out.println("before1111");
}
@Before("logPointCut2()")
public void before2(){
System.out.println("before2222");
}
}
3.定义Service层,不需要调用方法,输出一下即可,验证一下执行顺序
@Service
public class SysLogService {
public boolean save (SysLogBo sysLogBo){
System.out.println("beginService - save");
return true;
}
}
4.定义Controller层,查看验证结果
@RestController
public class TestController2 {
@Autowired
private SysLogService sysLogService;
@SysLog("测试")
@GetMapping("/test")
public String test(){
System.out.println("beginTest");
sysLogService.save();
return "over";
}
}
我们期待的执行顺序应该是这样的:
1.自定义注解先生效,自定义注解被定义在切面中:
@Pointcut("@annotation(com.wexin.aop.SysLog)")
public void logPointCut1(){}
@Around("logPointCut1()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
System.out.println("环绕之前");
Object result = point.proceed();
System.out.println("环绕之后");
long time = System.currentTimeMillis() - beginTime;
try {
saveLog(point, time);
} catch (Exception e) {
}
return result;
}
@Before("logPointCut1()")
public void beforeT(){
System.out.println("before1111");
}
所以,应该会先打印出 @Around 中 调用 point.proceed(); 之前的所有方法和日志:环绕之前
然后再打印@Before的日志:before1111,然后输出Controller的日志:beginTest;
然后调用save方法,此时由于save方法也在切面类中定义了:
@Pointcut("execution(* com.wexin.aop.SysLogService.save(..))")
public void logPointCut2(){}
@Before("logPointCut2()")
public void before2(){
System.out.println("before2222");
}
所以,会先输出before2222,然后才输出save方法中所写的:
public boolean save (SysLogBo sysLogBo){
System.out.println("beginService - save");
return true;
}
由于save方法的切面包含在自定义注解的切面中,所以 point.proceed(); 所运行的方法应该是这两个切面,之后才会打印环绕之后,这个顺序如果理解了,能够很快帮你搞清楚这几个注解的使用方法。
最终的输出控制台的日志顺序应该是:
环绕之前
before1111
beginTest
before2222
beginService - save
环绕之后
保存日志 : 2019-04-14)