基础

Spring 系列:Spring AOP 中@Pointcut的用法(多个Pointcut)

 

 

 

Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议优先使用JDK的动态代理)

  • 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。
  • 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以。但是需要考虑以下问题: 
  1. 无法通知(advise)final方法,因为他们不能被覆写。
  2. 代理对象的构造器会被调用两次。因为在CGLIB代理模式下每一个代理对象都会 产生一个子类。每一个代理实例会生成两个对象:实际代理对象和它的一个实现了通知的子类实例 而是用JDK代理时不会出现这样的行为。通常情况下,调用代理类型的构造器两次并不是问题, 因为除了会发生指派外没有任何真正的逻辑被实现。
  3. CGLib的效率没有使用JDK代理机制高,速度平均要慢8倍左右。 

 

 

Demo

AopLogApplication 

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AopLogApplication {

	public static void main(String[] args) {
		SpringApplication.run(AopLogApplication.class, args);
	}

}

 

AopLogController 

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;



@RestController
public class AopLogController {
    @GetMapping("/a1")
    public String aoptest1(){
        return " aop test1";
    }

    @GetMapping(value = "/a2")
    public String aoptest2(){
        return " aop test2";
    }


}

AopLog 

package com.example.demo.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.Arrays;

@Aspect
@Component
public class AopLog {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     *  定义切入点:在哪些类、哪些方法切入
     *  拦截 com.example所有类的,所有方法
     *  AOP只能匹配public方
     */
    @Pointcut("execution(  * com.example..*.*( .. ) )")
    public void aopWebLogAll() {
        System.out.printf("--enter---");
    }

        /**
         *  定义切入点:在哪些类、哪些方法切入
         *  拦截 com.example下AopLogController类的*est1结尾的所有public方法
         */
    @Pointcut("execution( public * com.example..AopLogController.*est1( .. ) )")
    private void aopWebLog() {
        System.out.printf("--enter---");
    }

    @Before("aopWebLogAll()")
    public void doBefore2(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        System.out.println("========doBefore All==============");
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP方法 : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("类的方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
        logger.info("参数 : " + request.getQueryString());
    }

    @AfterReturning(pointcut = "aopWebLogAll()",returning = "retObject")
    public void doAfterReturning2(Object retObject) throws Throwable {
        System.out.println("========doAfterReturning All=============");
        // 处理完请求,返回内容
        logger.info("应答值 : " + retObject);
        logger.info("用时: " + (System.currentTimeMillis() - startTime.get()));
    }

    //抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
    @AfterThrowing(pointcut = "aopWebLogAll()", throwing = "ex")
    public void addAfterThrowingLogger2(JoinPoint joinPoint, Exception ex) {
        System.out.println("========addAfterThrowingLogger All==============");
        logger.error("执行 " + " 异常", ex);
    }


    @Before("aopWebLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());
        // 接收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        System.out.println("========doBefore==============");
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP方法 : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("类的方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
        logger.info("参数 : " + request.getQueryString());
    }

    @AfterReturning(pointcut = "aopWebLog()",returning = "retObject")
    public void doAfterReturning(Object retObject) throws Throwable {
        System.out.println("========doAfterReturning==============");
        // 处理完请求,返回内容
        logger.info("应答值 : " + retObject);
        logger.info("用时: " + (System.currentTimeMillis() - startTime.get()));
    }

    //抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
    @AfterThrowing(pointcut = "aopWebLog()", throwing = "ex")
    public void addAfterThrowingLogger(JoinPoint joinPoint, Exception ex) {
        System.out.println("========addAfterThrowingLogger==============");
        logger.error("执行 " + " 异常", ex);
    }

}
pom
<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>AopLog</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.8.1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

验证

Spring 系列:AOP理解、AOP例子Demo_maven

http://localhost:8080/a1

========doBefore==============
2020-07-18 15:53:02.110  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : URL : http://localhost:8080/a1
2020-07-18 15:53:02.110  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : HTTP方法 : GET
2020-07-18 15:53:02.110  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : IP : 0:0:0:0:0:0:0:1
2020-07-18 15:53:02.110  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 类的方法 : com.example.demo.controller.AopLogController.aoptest1
2020-07-18 15:53:02.110  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 参数 : []
2020-07-18 15:53:02.110  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 参数 : null
========doBefore All==============
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : URL : http://localhost:8080/a1
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : HTTP方法 : GET
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : IP : 0:0:0:0:0:0:0:1
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 类的方法 : com.example.demo.controller.AopLogController.aoptest1
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 参数 : []
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 参数 : null
========doAfterReturning==============
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 应答值 :  aop test1
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 用时: 1
========doAfterReturning All=============
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 应答值 :  aop test1
2020-07-18 15:53:02.111  INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog              : 用时: 1



http://localhost:8080/a2 


========doBefore All==============
2020-07-18 15:53:24.462  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : URL : http://localhost:8080/a2
2020-07-18 15:53:24.462  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : HTTP方法 : GET
2020-07-18 15:53:24.462  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : IP : 0:0:0:0:0:0:0:1
2020-07-18 15:53:24.462  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : 类的方法 : com.example.demo.controller.AopLogController.aoptest2
2020-07-18 15:53:24.462  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : 参数 : []
2020-07-18 15:53:24.462  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : 参数 : null
========doAfterReturning All=============
2020-07-18 15:53:24.463  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : 应答值 :  aop test2
2020-07-18 15:53:24.463  INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog              : 用时: 1