参考文章:简单的aop实现日志打印(切入点表达式)

@Pointcut语法详解

spring 注解aop切入点表达式怎么排除某些方法

目录

1.AOP核心概念

2.JoinPoint 对象

3.简单示例

4.切面文件--use

5.pom.xml文件:


1.AOP核心概念

#1、横切关注点
#  对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

#2、切面(aspect)
#  类是对物体特征的抽象,切面就是对横切关注点的抽象

#3、连接点(joinpoint)
#  被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方
#   法,实际上连接点还可以是字段或者构造器

#4、切入点(pointcut)
#  对连接点进行拦截的定义

#5、通知(advice)
#  所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

#6、目标对象
#  代理的目标对象

#7、织入(weave)
#  将切面应用到目标对象并导致代理对象创建的过程

#8、引入(introduction)
#  在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

2.JoinPoint 对象

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象. 

 常用api:

方法名

功能

Signature getSignature();

获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息

Object[] getArgs();

获取传入目标方法的参数对象

Object getTarget();

获取被代理的对象

Object getThis();

获取代理对象

3.简单示例

声明一个切面类,并把这个切面类加入到IOC容器中

@Component//加入IOC容器
@Aspect//表示这是一个切面类
public class LogAspect{
    // #====================spring 注解aop切入点表达式怎么排除某些方法=================#
    // @Pointcut("!execution(* aa.bb..*.set*(..)) && execution(* aa.bb..*.*(..)) ")
    // 如果是这种形式的法执行了很多其他方法,比如init。

    // @Pointcut("execution(* aa.bb..*.*(..)) && !execution(* aa.bb..*.set*(..)) ")
    // 这种形式还是都执行了 
    
    //value中为切入点表达式
    @Before(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//前置通知
    public void showBeginLog(){
        System.out.println("AOP日志开始");
    }
    @After(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//后置通知
    public void showReturnLog(){
        System.out.println("AOP方法结束");
    }
    @AfterThrowing(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//异常通知
    public void showExceptionLog(){
        System.out.println("AOP方法异常");
    }
    @AfterReturning(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//返回通知
    public void showAfterLog(){
        System.out.println("AOP方法最终返回");
    }
}

4.切面文件--use

package com.cloud.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

/**
 * 实现Web层的日志切面
 * 对于同一个代理对象,可以同时有多个切面共同对它进行代理。
 * 可以在切面类上通过@Order(value=50)注解来进行设置, 值越小优先级越高
 */
@Component
@Aspect
@Order(1) 
public class WebLogAspect {
    private Logger log = LoggerFactory.getLogger(getClass());
    private ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 定义一个切入点.
     * 解释下:
     * <p>
     * ~ 第一个 * 代表任意修饰符及任意返回值.
     * ~ 第二个 * 任意包名
     * ~ 第三个 * 定义在web包或者子包
     * ~ 第四个 * 任意方法
     * ~ .. 匹配任意数量的参数.
     */
    @Pointcut("(execution(public * com.cloud..*Controller.*(..)))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws UnsupportedEncodingException {
        startTime.set(System.currentTimeMillis());

        // 接收到请求,记录请求内容
        log.info("========================= before -- start =========================");
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;

        log.info("CLASS_METHOD : " + methodSignature.getDeclaringTypeName() + "." + methodSignature.getName());

        log.info("请求参数:    名称     值");
        String[] argsNameArray  = methodSignature.getParameterNames();
        Object[] argsValueArray = joinPoint.getArgs();
        if (argsNameArray == null || argsNameArray.length < 1) {
            log.info("args_name: null");
            log.info("args_value: null");
        } else {
            for (int i = 0; i < argsNameArray.length; i++) {
                log.info("args_name: " + argsNameArray[i]);

                String argValue = argsValueArray[i] != null ? argsValueArray[i].toString() : "";
                if (argsNameArray[i].contains("encode")) {
                    String str = URLDecoder.decode(argValue, "utf-8");
                    log.info("args_value: " + (str.length() > 200 ? str.substring(0, 200) + "..." : str));
                } else {
                    log.info("args_value: " + (argValue.length() > 200 ? argValue.substring(0, 200) + "..." : argValue));
                }
            }
        }

        // 记录下请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if(attributes == null){
            log.info("========================= before -- end =========================");
            return;
        }

        HttpServletRequest request = attributes.getRequest();
        log.info("IP : " + request.getRemoteAddr());

        log.info("========================= before -- end =========================");
    }

    @AfterReturning(returning="rvt", pointcut="webLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object rvt) {
        log.info("========================= after -- start =========================");
        log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName()
                + "." + joinPoint.getSignature().getName());

        if(rvt != null){
            String str = rvt.toString();
            if (str.length() > 200) {
                str = str.substring(0, 200) + "...";
            }

            log.info("return 返回值:");
            log.info(str);
        }

        log.info("耗时(毫秒) : " + (System.currentTimeMillis() - startTime.get()));

        log.info("========================= after -- end =========================");
    }
}

5.pom.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cloud</groupId>
    <artifactId>cloud-base-parent</artifactId>
    <!--0.0.1-SNAPSHOT-->
    <version>0.0.1-SNAPSHOT</version>

    <!-- 做为parent工程, 不必打jar包 -->
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!--与spring-cloud-netflix-zuul-2.0.2.RELEASE版本冲突-->
        <!--<version>2.1.1.RELEASE</version>-->

        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR2</spring-cloud.version>
    </properties>

    <dependencies>
        <!-- 包含 mvc,aop 等jar资源 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

     <!--依赖管理, 用于管理spring-cloud的依赖, 其中Camden.SR3是版本号-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
   
    <!--远程仓库-->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>