Spring AOP通俗易懂的各种术语
创始人
2025-05-29 07:30:47

目录

基础概念

Spring AOP 通知类型

AOP联盟定义的通知类型

AspectJ定义的通知类型

面试题 

讲一下spring中的AOP

讲一下如可控制切面的执行顺序

讲一下jdk动态代理和Cglib动态代理的对比


基础概念

实例代码,可以先不看

package com.scm.myblog.aop;import com.scm.myblog.utils.CharsetFilterUtils;
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
@Component
public class CharsetAspect {//定义字符过滤切点@Pointcut("execution(* com.scm.myblog.controller.ArticleController.getArticleDetail(..))")public void CharSetPoint(){}//字符过滤环绕切面@Around("CharSetPoint()")public Object CharsetAspects(ProceedingJoinPoint pjp){//获取所有请求参数Object[] args = pjp.getArgs();//过滤请求参数args[0]= CharsetFilterUtils.tranCharset((String) args[0]);//将结果放回pjpObject o;try {o= pjp.proceed(args);} catch (Throwable e) {throw new RuntimeException(e);}//返回改变后的objectreturn o;}
}
 @ApiOperation("模糊查询加分页获取文章数据")@PostMapping("/getPageData")public Result getArticleByPageAndSearch(@RequestBody PageDto pageData) {return articleService.getArticlePage(pageData,false);}@ApiOperation("获取详细文章内容")@GetMapping("/getArticles/{title}")public Result getArticleDetail(@PathVariable("title") String title, HttpServletRequest request) {//将文章标题放在session中备用WebUtils.setValBySession(request,"title",title);return articleService.getArticleData(title);}/*** 获取文章归档**/@ApiOperation("根据时间线获取文章")@GetMapping ("/getArticlesByTimeLine")public Result getArticlesByTimeLine(){return articleService.getArticlesByTimeLine();}

连接点:因为Spring只支持方法执行作为连接点,所有在spring中的连接点就是满足条件的各个方法。看如下的图中的箭头中指向的方法即是连接点。

 

切点:切点就是我们配置的满足我们条件的目标方法。切点一定是连接点。它是连接点位置的具体化就是了,看图。

我们通过切点表达式配置了一个具体的连接点(getArticleDetail(..)),它是一个具体的方法,可以对比上上图中的连接点,加深理解。

通知/增强(Advice: 就是我们编写的通过Aop去实现代码增强的方法。看图即可,图中的方框中即是一个通知,它通过@Around实现了对切点处的方法的增强。

 


切面:切面是切点和通知的组合称谓,就是变相给组合起了个名字,还是看图即可,方框中的就是一个切面。

 注意的是一个类中可能会有多个切面的。比如看代码即可。

我的代码中就出现了5个切面的。

package com.scm.myblog.aop;import com.scm.myblog.entity.Comment;
import com.scm.myblog.entity.VO.Result;
import com.scm.myblog.serviceUtils.AdminBlogUtils;
import com.scm.myblog.serviceUtils.RedisServiceBox;
import com.scm.myblog.serviceUtils.UserBlogUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.List;/*** web数据记录AOP** @author Lancer* @date 2022/12/09*/
@Aspect
@Component
@Slf4j
public class WebDataRecordAspect {@Autowiredpublic RedisTemplate rs;//设置浏览量增加切点@Pointcut("execution(public * com.scm.myblog.controller.ArticleController.getArticleDetail(..))")public void ViewAddPointcut() {}//设置文章增加切点@Pointcut("execution(public * com.scm.myblog.controller.AdminArticleController.addArticles(..))")public void AddArticlePointcut() {}//设置文章减少切点@Pointcut("execution(public * com.scm.myblog.controller.AdminArticleController.removeArticles(..))")public void RemoveArticlePointcut() {}//设置评论增加切点@Pointcut("execution(public * com.scm.myblog.controller.CommentController.setComment(..))")public void AddCommentPointcut() {}//设置评论减少切点@Pointcut("execution(public * com.scm.myblog.controller.AdminCommentController.removeComment(..))")public void RemoveCommentPointcut() {}/*** 浏览量增加切面** @param j j*/@Before("ViewAddPointcut()")public void ViewAdd(JoinPoint j){String sendTitle="";Object[] args = j.getArgs();if (args != null) {sendTitle=(String)args[0];}RedisServiceBox.addViewCount(sendTitle);log.info(sendTitle+"浏览加1");}//文章DP增加到redis@AfterReturning(pointcut ="AddArticlePointcut()",returning = "r")public void AddArticle(Object r){//获取方法返回值Object data=getResultData(r);if(data!=null){String title=(String)data;//增加根据文章的点赞数据到redisRedisServiceBox.addDataToRedis(title);log.info("新增文章成功!文章标题为 "+title);}}//文章DP减少redis@AfterReturning(pointcut ="RemoveArticlePointcut()",returning = "r")public void RemoveArticle(Object r){//获取方法返回值Object data=getResultData(r);if(data!=null){Long[] ids=(Long[])data;//删除文章的点赞数据在redisRedisServiceBox.deleteBatchDataToRedis(ids);log.info("删除文章成功!文章id为 "+ Arrays.toString(ids));}}//评论量增加在redis@AfterReturning(pointcut ="AddCommentPointcut()",returning = "r")public void AddComment(Object r){//获取方法返回值Object data=getResultData(r);if(data!=null){String title=(String)data;//更新Redis中的文章数据RedisServiceBox.addCommentCount(title);log.info("评论增加成功!文章标题为 "+title);}}//评论量减少在redis@AfterReturning(pointcut ="RemoveCommentPointcut()",returning = "r")public void RemoveComment(Object r){//获取方法返回值Object data=getResultData(r);if(data!=null){List commentList = (List) (data);List ids = AdminBlogUtils.getArticleIdByCommentList(commentList);//更新Redis中的文章数据RedisServiceBox.removeCommentCount(ids);log.info("评论删除成功!文章id为"+ids);}}public Object getResultData(Object r){//获取方法返回值Result r1 = (Result) r;return r1.getData();}
}

织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。也是创建AOP代理对象的过程。

Spring AOP 通知类型

AOP联盟定义的通知类型

通知类型接口描述
前置通知 org.springframework.aop.MethodBeforeAdvice在目标方法执行前实施增强。
后置通知org.springframework.aop.AfterAdvice在目标方法执行后实施增强。
后置返回通知org.springframework.aop.AfterReturningAdvice在目标方法执行完成,并返回一个返回值后实施增强。
环绕通知org.aopalliance.intercept.MethodInterceptor在目标方法执行前后实施增强。
异常通知org.springframework.aop.ThrowsAdvice在方法抛出异常后实施增强。
引入通知org.springframework.aop.IntroductionInterceptor在目标类中添加一些新的方法和属性。

AspectJ定义的通知类型

Before 前置通知:目标对象的方法调用之前触发
After 后置通知:目标对象的方法调用之后触发
AfterReturning 返回通知:目标对象的方法调用完成,在返回结果值之后触发
AfterThrowing 异常通知:目标对象的方法运行中抛出 / 触发异常后触发
注意一点,AfterReturning 与 AfterThrowing 两者是互斥的!如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
Around 环绕通知:编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
 

面试题 

讲一下spring中的AOP

AOP 即面向切面编程,全称 Aspect Oriented Programming ,它是 OOP 的补充。OOP 关注的核心是对象,AOP 的核心是切面(Aspect)。AOP 可以在不修改功能代码本身的前提下,使用运行时动态代理的技术对已有代码逻辑增强。AOP 可以实现组件化、可插拔式的功能扩展,通过简单配置即可将功能增强到指定的切入点。在开发中我常使用的是AspectJ提供的通知类型,实现代码的增强的,然后AOP 的使用范围还是非常广的比如
1 业务日志切面:可以记录业务方法调用的痕迹
2 事务控制:通过切面可以声明式控制事务
3 权限校验:执行方法之前先校验当前登录用户是否有权调用
4 数据缓存:执行方法之前先从缓存中取,取到则直接返回不走业务方法。

讲一下如可控制切面的执行顺序

1 显式使用 @Order 注解,或者 Ordered 接口,声明切面的执行顺序(默认 Integer.MAX_VALUE ),即值越小的先执行,值越大的后执行,同时先执行的会后结束
2  通过使用类名的 unicode 编码顺序,控制切面的执行顺序,unicode越小的越先执行的

讲一下jdk动态代理和Cglib动态代理的对比


jdk 动态代理要求被代理的对象所属类至少实现一个接口,它是 jdk 内置的机制
Cglib 动态代理无此限制,使用字节码增强技术实现,需要依赖第三方 Cglib 包
jdk 动态代理的代理对象创建速度快,执行速度慢;

Cglib 动态代理的代理对象创建速度慢,执行速度快
 

相关内容

热门资讯

cad打印线条粗细设置 cad... 004-线型(下)打印样式设置和线型文件使用一、线宽设置方法制图规范里边的线宽要求,我们已经定义好,...
荼蘼什么意思 岁月缱绻葳蕤生香... 感谢作者【辰夕】的原创独家授权分享编辑整理:【多肉植物百科】百科君坐标:云南 曲靖春而至,季节流转,...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...