需求:基于OCP(开闭原则-对扩展开放对修改关闭)设计原则对功能进行扩展
基于继承方式实现功能扩展,代码简单,容器理解,但是不够灵活,耦合性比较强。
基于组合方式实现功能扩展,代码比较灵活,耦合低,稳定性强,但理解相对比较困难,代码繁琐。
一. AOP简介
AOP(Aspect Orient Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善。
二. 实现原理
AOP可以在系统启动时为目标类型创建子类或兄弟类型对象,这样的对象我们通常会称之为动态代理对象
第一种方式:借助JDK官方API为目标对象类型创建其兄弟类型对象,但是目标对象类型需要实现相应接口.
第二种方式:借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰.
三. 相关术语分析
切面(aspect) :
横切面对象,一般为一个具体类对象。
切入点(pointcut) :
定义了切入扩展业务逻辑的位置(哪些方法运行时切入扩展业务),一般会通过表达式进行相关定义,一个切面中可以定义多个切入点。
通知(Advice) :
内部封装扩展业务逻辑的具体方法对象,一个切面中可以有多个通知(在切面的某个特定位置上执行的动作(扩展功能)。
连接点(joinpoint) :
程序执行过程中,封装了某个正在执行的目标方法信息的对象,可以通过此对象获取具体的目标方法信息,甚至去调用目标方法。
四. 通知类型
Spring框架AOP模块定义通知类型,有如下几种:
@Around (优先级最高的通知,可以在目标方法执行之前,之后灵活进行业务拓展.)
@Before (目标方法执行之前调用)
@AfterReturning (目标方法正常结束时执行)
@AfterThrowing (目标方法异常结束时执行)
@After(目标方法结束时执行,正常结束和异常结束它都会执行)
五. 操作流程
5.1 添加AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
5.2 创建注解类型,应用于切入点表达式的定义
package com.jt.aop;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
String value() default "";
}
5.3 创建切面对象,用于做日志业务增强
package com.jt.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @Aspect 注解描述的类称为切面对象,此对象中负责定义切入点和通知
* 1.切入点(在哪些方法执行时,进行功能扩展)
* 2.通知(所有的扩展逻辑,都写到通知方法中)
*
*/
@Aspect
@Component
public class LogAspect {
/**
* @Pointcut定义切入点,表达式多种,常用注解表达式
* 当使用自己的写的注解对方法描述时,这个方法就是切入点方法.
*/
@Pointcut("@annotation(com.jt.aop.RequiredLog)")
public void doLog(){} //这个方法主要承载切入点
/**
* @Around注解描述的方法,可以在切入点执行之前和之后进行业务拓展,
* @param joinPoint 连接点对象,此对象封装了要执行的目标方法信息.
* @return
* @throws Throwable
*/
@Around("doLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("Before:"+System.currentTimeMillis());
Object result = joinPoint.proceed();//执行链(其它切面,目标对象方法)
System.out.println("After:"+System.currentTimeMillis());
return result;
}
}
5.4 通过注解RequiredLog注解描述日志查询或删除业务相关方法,此时这个方法为日志切入点方法
package com.jt.web.controller;
import com.jt.aop.RequiredLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
public class DemoController {
@RequiredLog //由次注解描述的方法是切入点方法
@GetMapping
public String doSayHello(){
return "hello spring";
}
}
六. FAQ分析
AOP中切面的定义需要注意什么?(@Aspect,@Component)
AOP 切面中都需要定义什么?(切入点,通知方法)
AOP中切面可以有多个吗?(可以)
AOP中多个切面可以作用于同一个切入点方法吗?(可以)
AOP中通知方法的应用类型哪些,分别是什么时间点执行?
SpringBoot中默认AOP代理对象创建方式?(CGLIB-更加灵活)