SpringBoot: AOP的用法

217 阅读2分钟

一、基本使用背景:需要对一批方法进行增强处理的情形。比如,某个service中有100个方法用来给controller层进行调用。现在的需求要给这100个方法上增加如下功能: 1)记录方法被调用的时间和参数 2)记录方法调用的返回时间和返回值 如果按照传统的做法,可能要对这100个方法分别进行修改,这种做法显然是不合理的。而使用AOP(Aspect Oriented Programming),则可以容易实现。 二、Spring Boot AOP的基本用法: 1.在项目的pom.xml中增加相关的依赖( spring-boot-starter-aop aspectj ),完整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>

    <groupId>cn.edu.tju</groupId>
    <artifactId>springaop</artifactId>
    <version>1.0.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    <!-- Additional lines to be added here... -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.6</version>
        </dependency>
    </dependencies>

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

            </plugin>
        </plugins>
    </build>


</project>

2.在启动类添加@EnableAspectJAutoProxy注解(如果在application.properties中未显式配置spring.aop.auto=false的话,@EnableAspectJAutoProxy注解也可以省略)

package cn.edu.tju;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class Start {
    public static void main(String[] args) {
        SpringApplication.run(Start.class,args);
    }
}

3.创建service类:

package cn.edu.tju.service;

import org.springframework.stereotype.Service;

@Service
public class InfoService {
    public String getHelloInfo(String str){
        return "hello,"+str;
    }
    public String getHiInfo(String str){
        return "hi,"+str;
    }
}


4.创建controller类,并注入上述InfoService

package cn.edu.tju.controller;

import cn.edu.tju.service.InfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @Autowired
    private InfoService infoService;



    @RequestMapping("/hello/{name}")
    public String getHello(@PathVariable("name")String name){
        //System.out.println(infoService.getClass());
        String result=infoService.getHelloInfo(name);
        return result;
    }

    @RequestMapping("/hi/{name}")
    public String getHi(@PathVariable("name")String name){
        //System.out.println(infoService.getClass());
        String result=infoService.getHiInfo(name);
        return result;
    }
}


5.创建AOP类,用来对service类中被调用的方法进行增强处理:

package cn.edu.tju.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@Aspect
public class MyAspect {
    //execution里的写法表示cn.edu.tju.service包下所有类的所有方法都将被增强处理
    //方法调用之前会调用这里定义的这个方法
    @Before("execution(* cn.edu.tju.service.*.*(..))")
    public void logCallingTime(JoinPoint joinPoint){
        System.out.print(new Date().toLocaleString()+" "+joinPoint.getSignature()+" 被调用,参数为: ");
        Object[] argList=joinPoint.getArgs();
        for(Object object: argList){
            System.out.print(object);
            System.out.print(" ");

        }
        System.out.println();
    }

    //execution里的写法表示cn.edu.tju.service包下所有类的所有方法都将被增强处理
    //方法返回时会调用这里定义的这个方法
    @AfterReturning(returning = "returnValue",pointcut="execution(* cn.edu.tju.service.*.*(..))")
    public void logReturningInfo(JoinPoint joinPoint,Object returnValue){
        System.out.println(new Date().toLocaleString()+" " +joinPoint.getSignature()+" 返回 "+returnValue);
    }
}

6.运行程序,分别访问:

http://localhost:8093/hi/amadeus
http://localhost:8093/hello/amadeus

可以看到控制台的输出 在这里插入图片描述 7.spring boot AOP,默认使用cglib动态代理来实现,无论被代理的类是否实现了某个接口。如果需要让实现了某个接口的被代理类使用jdk动态代理,则需要在application.properties中配置

spring.aop.proxy-target-class=false