面向对象编程(OOP)的好处是显而易见的,缺点也一样明显。当须要为多个不具备继承关系的对象添加一个公共的方法的时候,例如日志记录、性能监控等,若是采用面向对象编程的方法,须要在每一个对象里面都添加相同的方法,这样就产生了较大的重复工做量和大量的重复代码,不利于维护。面向切面编程(AOP)是面向对象编程的补充,简单来讲就是统一处理某一“切面”的问题的编程思想。若是使用AOP的方式进行日志的记录和处理,全部的日志代码都集中于一处,不须要再每一个方法里面都去添加,极大减小了重复代码。spring
通知(Advice)包含了须要用于多个应用对象的横切行为,彻底听不懂,不要紧,通俗一点说就是定义了“何时”和“作什么”。编程
链接点(Join Point)是程序执行过程当中可以应用通知的全部点。浏览器
切点(Poincut)是定义了在“什么地方”进行切入,哪些链接点会获得通知。显然,切点必定是链接点。app
切面(Aspect)是通知和切点的结合。通知和切点共同定义了切面的所有内容——是什么,什么时候,何地完成功能。spring-boot
引入(Introduction)容许咱们向现有的类中添加新方法或者属性。性能
织入(Weaving)是把切面应用到目标对象并建立新的代理对象的过程,分为编译期织入、类加载期织入和运行期织入。测试
Spring Boot使用AOP须要添加spring-boot-starter-aop依赖,以下:代理
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
不须要再添加aspectjweaver的依赖了,由于spring-boot-starter-aop包含了aspectjweaver,而且版本是较新的版本,若是在添加老版本(如1.5.4)启动会报错。
日志
直接定义一个controller,代码以下:code
@RestController public class AopController { @RequestMapping("/hello") public String sayHello(){ System.out.println("hello"); return "hello"; } }
Spring采用@AspectJ注解对POJO进行标注,该注解代表该类不只仅是一个POJO,仍是一个切面。切面是切点和通知的结合,那么定义一个切面就须要编写切点和通知。在代码中,只须要添加@Aspect注解便可。
切点是经过@Pointcut注解和切点表达式定义的。
@Pointcut注解能够在一个切面内定义可重用的切点。
因为Spring切面粒度最小是达到方法级别,而execution表达式能够用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,而且实际中,大部分须要使用AOP的业务场景也只须要达到方法级别便可,于是execution表达式的使用是最为普遍的。如图是execution表达式的语法:
execution表示在方法执行的时候触发。以“”开头,代表方法返回值类型为任意类型。而后是全限定的类名和方法名,“”能够表示任意类和任意方法。对于方法参数列表,可使用“..”表示参数为任意类型。若是须要多个表达式,可使用“&&”、“||”和“!”完成与、或、非的操做。
通知有五种类型,分别是:
前置通知(@Before):在目标方法调用以前调用通知
后置通知(@After):在目标方法完成以后调用通知
环绕通知(@Around):在被通知的方法调用以前和调用以后执行自定义的方法
返回通知(@AfterReturning):在目标方法成功执行以后调用通知
异常通知(@AfterThrowing):在目标方法抛出异常以后调用通知
代码中定义了三种类型的通知,使用@Before注解标识前置通知,打印“beforeAdvice...”,使用@After注解标识后置通知,打印“AfterAdvice...”,使用@Around注解标识环绕通知,在方法执行前和执行以后分别打印“before”和“after”。这样一个切面就定义好了,代码以下:
@Aspect @Component public class AopAdvice { @Pointcut("execution (* com.shangguan.aop.controller.*.*(..))") public void test() { } @Before("test()") public void beforeAdvice() { System.out.println("beforeAdvice..."); } @After("test()") public void afterAdvice() { System.out.println("afterAdvice..."); } @Around("test()") public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("before"); try { proceedingJoinPoint.proceed(); } catch (Throwable t) { t.printStackTrace(); } System.out.println("after"); } }
完成以后的代码结构如图所示:
运行AopApplication,在浏览器访问http://localhost:8080/hello,不出意外,控制台输出如图所示: