首页 > 其他分享 >Spring Aop的学习:Spring Aop的简单入门

Spring Aop的学习:Spring Aop的简单入门

时间:2023-04-20 10:56:28浏览次数:31  
标签:joinPoint 入门 Spring Aop 切点 注解 import 执行 方法

1. 什么是AOP

AOP(Aspect Oriented Programming):面向切面编程,是OOP(面向对象编程)的一个延续,其和OOP一样,也是一种编程思想。不过AOP是一种横向开发模式。

 

2. AOP的作用及应用场景

  • 作用
    AOP的主要作用就是减少代码量,提高代码的可重用性,有利于未来的可操作性与可维护性。
    主要操作就是将所有模块中共同拥有的代码,单独抽取出来,放在一块地方,在主代码运行之前或之后,或主程序运行的其他时间点执行这块代码。也可以理解成把这些单独抽出来的代码封装成一个个单独的方法,但是这些方法的执行不需要我们在程序中进行显示的调用,而是通过动态代理(jdk动态代理和cglib动态代理)的方式来帮助我们执行这些方法。
  • 应用场景
    AOP的主要应用场景是一些相似性代码比较高的场景,比如:权限认证,日志,事务等。

3. AOP的术语

1.连接点(Joinepoint):是程序执行的某个特定位置,如类开始初始化前、类初始化后、类的某个方法调用前/后、方法抛出异常后。一个类或者一段程序代码拥有一些具有边界性质的特定点,这些特定点就称为 “连接点” 。这边有个注意的点就是spring的链接点只支持方法,也就是只能是方法在调用前,调用后,抛出异常后。
2.切点(Pointcut):切点就是能够定位到特定连接点的点。也就是能够通过切点知道程序在哪个连接点之前或之后执行。
3.增强(Advice):增强说白了就是你想要在切点上执行的代码逻辑,也就是在连接点之前或之后执行的那段代码。
4.目标对象(Target):就是你要插入代码的类,也就是连接点所在的类。
5.引介(Introduction):是一种特殊的增强,为类添加一些属性和方法。
6.织入(Weaving):是一个过程,就是将增强添加到目标类具体连接点上的过程。
7.代理(Proxy):就是融合了目标类和增强逻辑后生成的那个对象
8.切面(Aspect):由切点和增强组成,包含了切点的定义与增强的代码逻辑的定义

4. 如何使用AOP

使用AOP的方式主要有两种,一种是基于xml文件进行开发,另一种是基于注解进行开发,这边主要是讲的基于注解的进行开发的方式。

4.1. 引入依赖

        <!--AOP的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

4.2. 注解介绍

@Aspect:声明一个切面,标注在类,接口或枚举上。

 @Pointcut:声明切入点,即切入到哪些目标类的目标方法,标注在方法上。

 

value的值是指定切入点表达式,常用的主要有两种表达式,一种是execution(),另一种是annotation():

  • execution()表达式
    语法:execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选))
    参数部分允许使用通配符:
    * 匹配任意字符,但只能匹配一个元素。
    .. 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用。
    + 必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类
  • annotation()表达式
    此方式是针对某个注解来定义切面,一般对于自定义注解常使用annotation()表达式,比如我们对具有 @PostMapping 注解的方法做切面,可以如下定义切面:
    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")

@Before:前置通知,在切入点执行之前执行,也就是在执行切入点之前执行这个方法中的代码,但是如果这个方法中抛出了异常那么就不会再执行切入点的方法。和@Pointcut一样作用在方法上。

 其中value的值可以是指定切入点表达式,也可以是由@Pointcut注解标注的方法,如果是指定切入点表达式的话这边是和@Pointcut注解一样的。
@After:后置通知,在执行切入点方法之后执行,也就是执行了切入点的方法之后,会再执行@After注解标注的方法。其同样是作用在方法上的注解:

 其中value的值可以是指定切入点表达式,也可以是由@Pointcut注解标注的方法,如果是指定切入点表达式的话这边是和@Pointcut注解一样的。这个的value与@Before注解的value值一样。
@AfterReturning:返回通知,在切入点返回结果之后执行,这个结果是在@After注解之后执行的,其同样是作用在方法上:

 其中value的属性与@Before和@After注解的value一样。pointcut属性则是绑定同通知的契入点表达式,需要注意的是它的优先级高于value,也就是pointcut与value都填写的话会优先以pointcut的为准。
@AfterThrowing:异常通知,这个则是在抛出异常之后进行执行,另外,需要注意的是如果目标方法自己try-catch了异常,而没有继续往外抛出,则不会进入此回调函数。其同样是作用在方法上:

 他的属性值和@AfterReturning的属性值基本上一样。同样是pointcut的优先级高于value的
@Around:环绕通知,这个则是在切入点执行之前或执行之后都分别执行一些代码,还可以控制目标方法的执行。它是早于前置通知,晚于返回通知。其同样是作用在方法上的注解:

 

其中value的属性与@Before注解和@After注解的value属性一样。需要注意的是环绕通知标注的方法有一个必须的参数ProceedingJoinPoint,这个参数主要是用来执行目标方法,以及获取目标方法的一些信息。

4.3. 定义切点、切面以及通知

package com.mcj.music.aspect;

import com.mcj.music.utils.AspectUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author mcj
 * @date 2022/10/23 9:54
 * @description 日志的切面类,为controller类中的每个方法添加日志
 */
@Aspect                // 定义切面
@Component
@Slf4j
public class LogAspect {

    /**
     * execution函数用于匹配方法执行的连接点,语法为:
     * execution(方法修饰符(可选)  返回类型  方法名  参数  异常模式(可选))
     * 参数部分允许使用通配符:
     * *  匹配任意字符,但只能匹配一个元素
     * .. 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用
     * +  必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类
     * 此处表示返回类型、参数不限,只要是com.mcj.music.controller包中的任何类中的任何方法
     */
    @Pointcut("execution(* com.mcj.music.controller.*.*(..))")      // 定义切点
    public void logPointcut(){}

    /**
     * 环绕通知,在执行每个controller类中的方法之后打印相关的日志
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("logPointcut()")                                       // 进行环绕通知
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        String paramNameAndValue = AspectUtils.getParamNameAndValue(joinPoint);
        String methodName = AspectUtils.getMethod(joinPoint);
        Object proceed = joinPoint.proceed();
        log.info("当前请求方法:{},当前请求参数名与参数:{},当前请求结果:{}", methodName, paramNameAndValue, proceed);
        return proceed;
    }

    @Before("logPointcut()")
    public void doBefore() {
        System.out.println("这是前置通知");
        System.out.println("--------------------------------------------");
    }

    @After("logPointcut()")
    public void doAfter() {
        System.out.println("这是后置通知");
        System.out.println("--------------------------------------------");
    }

    @AfterReturning("logPointcut()")
    public void doAfterReturning() {
        System.out.println("这是返回通知");
        System.out.println("--------------------------------------------");
    }

    @AfterThrowing("logPointcut()")
    public void doAfterThrowing() {
        System.out.println("这是异常通知");
        System.out.println("--------------------------------------------");
    }

}

AspectUtils工具类

package com.mcj.music.utils;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.CodeSignature;

import java.util.HashMap;
import java.util.Map;

/**
 * @author mcj
 * @date 2022/10/23 10:15
 * @description
 */
@Slf4j
public class AspectUtils {

    /**
     * 获取当前切点的参数名称与值
     * @param joinPoint
     * @return
     */
    public static String getParamNameAndValue(ProceedingJoinPoint joinPoint) {
        Map<String, Object> paramNameAndValueMap = new HashMap<>(8);
        Object[] args = joinPoint.getArgs();
        String[] parameterNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < parameterNames.length; i++) {
            paramNameAndValueMap.put(parameterNames[i], args[i]);
        }
        return JSONObject.toJSONString(paramNameAndValueMap);
    }

    /**
     * 获取当前切点的全限定方法名
     * @param joinPoint
     * @return
     */
    public static String getMethod(ProceedingJoinPoint joinPoint) {
        // 获取当前切点的全限定类型
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
        // 获取当前切点的方法名
        String name = joinPoint.getSignature().getName();
        return declaringTypeName + "." + name;
    }


}

执行结果:

 由于方法在执行的过程中出现异常,根本没有返回值,所以也不会有返回通知了。

 

5. 总结

spring AOP的简单入门使用并不是很难,基本上最主要的就是切面与切点的定义,至于它的几种通知类型,最主要的就是@Around环绕通知

 

 

 

 

转: https://www.cnblogs.com/mcj123/p/16819370.html

 

标签:joinPoint,入门,Spring,Aop,切点,注解,import,执行,方法
From: https://www.cnblogs.com/fps2tao/p/17335989.html

相关文章

  • 小米AIoT SRE龚同学入职阅博笔记——SRE入门
    为了让团队同学对SRE有个统一的认识,有一些共同的套路和章法,尽量避免在工作中产生价值观和工作思路的矛盾,我一般会让新入职的同学读一下《入职必读》的几篇博客,1是提前对我们有个了解,2是告诉他们我们这的SRE要做什么和怎么做,3是便于入职后快速融入工作、团队,减少矛盾提高协作效率,最......
  • Thingboard入门
    新公司的工作主要是基于thingboard的开发,计划做一个使用,二次开发,源码解读的系列今天入门thingsboard的项目build,一般遇到两个问题ui-ngx的build,其实是有三个要去git网址的东西下载慢,可以进入ui-ngx的目录,yarninstall,或者看package的信息,直接在网上下载现成三个包,放到对应位置......
  • SOCKET(一):基本轮子详解与入门
    1.图解客户端与服务端交互流程上图轮子详解socket简单实践服务端#include<stdio.h>#include<string.h>#include<arpa/inet.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<netinet/ip.h>#include<fcn......
  • SpringBoot静态文件映射问题
    如果遇到这种情况,检查静态文件(js/css/img)是不是在默认的static路径下,在查看application配置中的static-path-pattern:是否和前端映射路径完全相同,要是不相同则会造成访问不到......
  • SpringMvc 原理解析
    springMVC源码流程第一步先来到DispatcherServlet()@SuppressWarnings("serial")publicclassDispatcherServletextendsFrameworkServlet来到doDispatch的方法doDispatch(HttpServletRequestrequest,HttpServletResponseresponse)进入//1.先检查时候文件上传请......
  • odoo 开发入门教程系列-准备一些操作(Action)?
    准备一些操作(Action)?到目前为止,我们主要通过声明字段和视图来构建模块。在任何真实的业务场景中,我们都希望将一些业务逻辑链接到操作按钮。在我们的房地产示例中,我们希望能够:取消或将房产设置为已售出接受或拒绝报价有人可能会说,我们已经可以通过手动更改状态来完成这些事情,但这并......
  • springboot 事件监听@EventListener注解用法
    前言关于@EventListener注解,百度了一下,网上的教程很多都是继承这个,实现那个的,其实根本用不着这么麻烦,所以就写了此文,如文章所讲有误,还请谅解更多详细用法请百度一下~作用关于事件监听,目前我的用法最多的就是记录日志之类的。在此之前我们记录日志一般都是先把日志的service注......
  • Spring05_Spring事务
    一、JdbcTemplate工具​ JdbcTemplate类是Spring框架提供一个用于操作数据库的模板类,JdbcTemplate类支持声明式事务管理。该类提供如下方法来执行数据库操作。​ 1、queryForObject查询单个对象​ queryForObject(Stringsql,RowMappermapper,Object[]args)​ 2、que......
  • how to inject <class> type in spring
      sample:ClassitemClass;publicClassgetItemClass(){returnitemClass}publicvoidsetItemClass(ClassitemClass){this.itemClass=itemClass;} NowinjectitemClasspropertyinspring:<beanid="shampoo"class="com.test.Product&......
  • Windows 10开发教程_编程入门自学教程_菜鸟教程-免费教程分享
    教程简介Windows10开发入门教程-从简单的步骤了解Windows10开发,从基本到高级概念,包括简介,UWP,第一个应用程序,商店,XAML控件,数据绑定,XAML性能,自适应设计,自适应UI,自适应代码,文件管理,SQLite数据库,应用程序到应用程序通信,应用程序本地化,应用程序生命周期,后台执行,应用服务,Web平台,连接......