Spring AOP通俗易懂的各种术语

目录

基础概念

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]);
        //将结果放回pjp
        Object o;
        try {
             o= pjp.proceed(args);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        //返回改变后的object
        return o;
    }
}
 @ApiOperation("模糊查询加分页获取文章数据")
    @PostMapping("/getPageData")
    public Result getArticleByPageAndSearch(@RequestBody PageDto<Article, ArticleSearchDto> 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 {
    @Autowired
    public RedisTemplate<String,String> 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;
            //增加根据文章的点赞数据到redis
            RedisServiceBox.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;
            //删除文章的点赞数据在redis
            RedisServiceBox.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<Comment> commentList = (List<Comment>) (data);
            List<Long> 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 动态代理的代理对象创建速度慢,执行速度快
 

猜你喜欢

转载自blog.csdn.net/qq_53679247/article/details/129593309