SpringBoot----AOP应用实践
一、Spring框架生态设计二、AOP 简介1、背景分析对于一个业务而言,我们如何在不修改源代码的基础上对对象功能进行拓展,例如现有一个公告(通知)业务接口及实现:pubic interface NoticeService{int deleteById(Integer…ids);}public class NoticeServiceImpl implements NoticeService{pub
一、Spring框架生态设计
二、AOP 简介
1、背景分析
对于一个业务而言,我们如何在不修改源代码的基础上对对象功能进行拓展,例如现有一个公告(通知)业务接口及实现:
pubic interface NoticeService{
int deleteById(Integer…ids);
}
public class NoticeServiceImpl implements NoticeService{
public int deleteById(Integer…ids){
System.out.println(Arrays.toString(ids));
return 0 ;
}
}
需求:基于OCP(开闭原则-对扩展开放对修改关闭)设计原则对NoticeServiceImpl类的功能进行扩展,例如在deleteById业务方法执行之前和之后输出一下系统时间.
方案1:基于继承方式实现其功能扩展,关键设计如下:
public class CglibLogNoticeService extends NoticeServiceImpl{
public int deleteById(Integer…ids){
System.out.println("Start:"+System.currentTimeMillis());
Int rows=super.deleteById(ids);
System.out.println("After:"+System.currentTimeMillis());
return rows;
}
}
测试类如下:
public class NoticeServiceTests{
public static void main(String[] args){
NoticeService ns=new CglibLogNoticeService();
ns.deleteById(10,20,30);
}
}
其中,基于继承方式实现功能扩展,代码简单,容易理解,但是不够灵活,耦合性比较强。
方案2:基于组合方式实现其功能扩展,关键代码设计如下:
public class JdkLogNoticeService implements NoticeService{
private NoticeService noticeService;//has a
public JdkLogNoticeService(NoticeService noticeService){
this.noticeService=noticeService;
}
public int deleteById(Integer…ids){
System.out.println("Start:"+System.currentTimeMillis());
int rows=this.noticeService.deleteById(ids);
System.out.println("After:"+System.currentTimeMillis());
return rows;
}
}
测试类如下:
public class NoticeServiceTests{
public static void main(String[] args){
NoticeService ns=
new JdkLogNoticeService(new NoticeServiceImpl());
ns.deleteById(10,20);
}
}
其中,基于组合方式实现功能扩展,代码比较灵活,耦合低,稳定性强,但理解相对比较困难。
总之,无论是继承,还是组合都是基于OCP方式实现了对象功能扩展,都有相应的优缺点,并且我们都要自己去写这些子类或兄弟类,在这些类中调用目标对象(父类或兄弟类对象)的方法以及扩展业务逻辑。对于这样的模板代码我们能否进行简化呢?例如:由框架实现其共性(创建目录类型的子类类型或兄弟类型),特性交给用户自己实现。
2、AOP概述
AOP(Aspect Orient Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善。实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面理解为一个动态过程(在对象运行时动态织入一些扩展功能或控制对象执行)。如图所示:
AOP 与 OOP 字面意思相近,但其实两者完全是面向不同领域的设计思想。实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面的运行期代理方式,理解为一个动态过程,可以在对象运行时动态织入一些扩展功能或控制对象执行。
3、实现原理
AOP可以在系统启动时为目标类型创建子类或兄弟类型对象,这样的对象我们通常会称之为动态代理对象.如图所示:
其中,为目标类型(XxxServiceImpl)创建其代理对象方式有两种(先了解):
第一种方式:借助JDK官方API为目标对象类型创建其兄弟类型对象,但是目标对象类型需要实现相应接口。
第二种方式:借助CGLIB库为目标对象类型创建其子类类型对象,但是目标对象类型不能使用final修饰.
4、相关术语分析
切面(aspect):横切面对象,一般为一个具体类对象。
切入点(pointcut):定义了切入扩展业务逻辑的位置(哪些方法运行时切入扩展业务),一般会通过表达式进行相关定义,一个切面中可以定义多个切入点。
通知(Advice): 内部封装扩展业务逻辑的具体方法对象,一个切面中可以有多个通知(在切面的某个特定位置上执行的动作(扩展功能)。
连接点(joinpoint):程序执行过程中,封装了某个正在执行的目标方法信息的对象,可以通过此对象获取具体的目标方法信息,甚至去调用目标方法。连接点与切入点定义如图所示:
说明:我们可以简单的将机场的一个安检口理解为连接点,多个安检口为切入点,安全检查过程看成是 通知。总之,概念很晦涩难懂,多做例子,做完就会清晰。先可以按白话去理解。
三、Spring AOP 快速入门
1、业务描述
在项目中定义一个日志切面,通过切面中的通知方法为目标业务对象做日志功能增强。
2、添加AOP依赖
项目中添加aop应用依赖,代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
说明:基于此依赖spring可以整合AspectJ框架快速完成AOP的基本实现。AspectJ 是一个面向切面的框架,他定义了 AOP 的一些语法,有一个专门的字节码生成器来生成遵守 java 规范的 class 文件。
3、业务切面对象设计
通过设计切面对象,为目标业务方法做功能增强,关键步骤如下:
第一步:创建注解类型,应用于切入点表达式的定义,关键代码如下:
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 operation() default "";
}
第二步:创建切面对象,用于做日志业务增强,关键代码如下:
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注解定义定义切入点表达式,表达式的写法有多种,比较常用
* 有注解方式的表达式(@annotation(你自己定义的注解)).当使用自己写
* 的注解对方法进行描述时,这个方法就是切入点方法.在这个方法上要锦上添花
*/
@Pointcut("@annotation(com.jt.aop.RequiredLog)")
public void doLog(){}//这个方法没有意义,主要用于承载切入点
/**
* Around通知,在此通知内部可以定义我们扩展业务逻辑,还可以调用目标执行链
* @param joinPoint 连接点(通知方法与目标方法的连接点对象),ProceedingJoinPoint
* 类型的连接点只能应用在Around通知方法中.
* @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;
}
}
第三步:通过注解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 {//--->Controller--Aop--->Service--->Dao
@RequiredLog //由此注解描述的方法就是切入点方法
@GetMapping
public String doSayHello(){
//System.out.println("Before:"+System.currentTimeMillis());
System.out.println("==doSayHello==");
String result="hello spring";
//System.out.println("After:"+System.currentTimeMillis());
return result;
}
}
第四步:测试通知业务方法,并检测日志输出以及了解其运行原理,如图所示:
4、AOP 执行流程分析
5、获取并记录详细日志
第一步:定义日志pojo对象,用于封装日志信息,例如:
第二步:修改日切面对象,获取并记录详细日志,关键代码如下:
第三步:进行日志业务查询,并检测是否有详细日志输出。
四、Spring AOP 技术进阶
1、通知类型
Spring框架AOP模块定义通知类型,有如下几种:
@Around (优先级最高的通知,可以在目标方法执行之前,之后灵活进行业务拓展.)
@Before (目标方法执行之前调用)
@AfterReturning (目标方法正常结束时执行)
@AfterThrowing (目标方法异常结束时执行)
@After (目标方法结束时执行,正常结束和异常结束它都会执行)
2、案例分析如下:(了解,可选择进行实现)
第一步:定义注解,代码如下:
第二步:定义时间切面对象对象演示通知执行,关键代码如下:
第三步:结合业务方法进行测试,检查通知方法执行顺序
3、切面执行顺序
切面的优先级需要借助@Order注解进行描述,数字越小优先级越高,默认优先级比较低。
例如:定义日志切面并指定优先级。
定义缓存切面并指定优先级:
四、总结(Summary)
1、重难点分析
AOP 是什么,解决了什么问题,应用场景?
AOP 编程基本步骤及实现过程(以基于 AspectJ 框架实现为例)。
AOP 编程中的核心对象及应用关系。(代理对象,切面对象,通知,切入点)
AOP 思想在 Spring 中的实现原理分析。(基于代理方式进行扩展业务的织入)
2、FAQ分析
AOP中切面的定义需要注意什么?(@Aspect,@Component)
AOP 切面中都需要定义什么?(切入点,通知方法)
AOP中切面可以有多个吗?(可以)
AOP中多个切面可以作用于同一个切入点方法吗?(可以)
AOP中通知方法的应用类型哪些,分别是什么时间点执行?
SpringBoot中默认AOP代理对象创建方式?(CGLIB-更加灵活)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)