基于注解的Spring切面(Aspect)实现

为什么要用切面

现在面向切面编程(AOP)早就是非常normal的概念了,不光是java(Spring),其他很多语言框架也借鉴了AOP的思想,实现了面向切面编程的功能。AOP将一些非常繁琐的、通用的检查收敛到某些点上(Pointcut),由各个业务流程自行选择使用,而且这种使用通常来说是对业务代码无侵入性的(或者侵入很少),使得业务的开发人员们可以更集中精力在具体的业务流程上,这种业务与条件检查的分离,对于业务代码的可读性、可维护性都大有好处,这也是AOP概念得以流行的原因。

为什么要使用基于注解的AOP

由于Spring家族的不断膨胀,以前基于xml配置的使用方式使各个开发者头疼不已,维护一个大型系统的配置文件居然成了一笔不小的人力成本,显然,从Spring framework到SpringBoot,从以前的集中一站式应用到现在的微服务应用,技术的发展的趋势是不断的应用拆分和精简,将单个系统的规模控制在一个可控的范围内,而基于注解的配置方式显然要比以前的基于xml的方式精简太多,而且注解通常是与代码放在一起,提升了代码的可读性。

Spring中的AOP实现示例

Spring中的AOP默认通过AspectJ实现,关于AspectJ的设计和实现我们以后再讨论,今天在这里只展示一个简单的切面实现示例。

假设我们要实现的切面功能是打印方法名,首先我们定义一个切面注解@PrintMethodName,它可以使用在Class和Method上。

 1 package com.chenrui.aspect;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 @Target({ElementType.TYPE, ElementType.METHOD})
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface PrintMethodName {
11 
12 }

然后我们实现我们的切面逻辑,需要在我们的Class上加上@Aspect注解,用于标注这是一个切面实现,注意也要加上@Component注解,否则不会被Spring识别。

 1 package com.chenrui.aspect;
 2 
 3 import org.aspectj.lang.ProceedingJoinPoint;
 4 import org.aspectj.lang.annotation.Around;
 5 import org.aspectj.lang.annotation.Aspect;
 6 import org.aspectj.lang.annotation.Pointcut;
 7 import org.springframework.stereotype.Component;
 8 
 9 @Aspect
10 @Component
11 public class PrintMethodNameAspect {
12 
13     @Pointcut("@within(com.chenrui.aspect.PrintMethodName) || @annotation(com.chenrui.aspect.PrintMethodName)")
14     public void pointcut() {
15 
16     }
17 
18     @Around("pointcut()")
19     public Object process(ProceedingJoinPoint point) throws Throwable {
20         System.out.println(point.getSignature().getName());
21         return point.proceed();
22     }
23 
24 }

其中

1 @Pointcut("@within(com.chenrui.aspect.PrintMethodName) || @annotation(com.chenrui.aspect.PrintMethodName)")

是我们定义的一个检查点(Pointcut),@within表示的是在PrintMethodName注解所标记的Class内,而@annotation表示的是被PrintMethodName注解标记的方法。因此,不管我们将切面注解标注在Class上还是Method上,我们都可以拦截到。下面我们定义一个简单的实现类。

 1 package com.chenrui.aspect;
 2 
 3 import org.springframework.stereotype.Component;
 4 
 5 @Component
 6 public class AspectService {
 7 
 8     @PrintMethodName
 9     public void firstMethod() {
10         System.out.println("Into the first method.");
11         innerCallMethod();
12     }
13 
14     @PrintMethodName
15     public void secondMethod() {
16         System.out.println("Into the second method.");
17     }
18 
19     @PrintMethodName
20     public void innerCallMethod() {
21         System.out.println("Into the inner call method.");
22     }
23 
24 }

三个方法firstMethod,secondMethod和innerCallMethod我们都加上了@PrintMethodName注解,现在我们来看一看效果。

 1 package com.chenrui.aspect;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.web.bind.annotation.RequestMapping;
 5 import org.springframework.web.bind.annotation.RestController;
 6 
 7 @RestController
 8 @RequestMapping("/aspect")
 9 public class AspectTestController {
10 
11     @Autowired
12     private AspectService aspectService;
13 
14     @RequestMapping("/test")
15     public Object testPrintMethod() {
16         aspectService.firstMethod();
17         aspectService.secondMethod();
18         return "OK";
19     }
20 
21 }

我们只调用了firstMethod和secondMethod,现在来看看控制台会打印出什么。

firstMethod
Into the first method.
Into the inner call method.
secondMethod
Into the second method.

显然,切面在firstMethod和secondMethod处都起作用了,但是在innerCallMethod处失效了,下一篇文章我将从Spring的实现方式上来说明为什么切面对于innerCallMethod方法会失效。

猜你喜欢

转载自www.cnblogs.com/chrhust/p/10424742.html