【使用篇】springboots整合AOP

springboots整合AOP

用过Spring都知道两个最主要的东西:AOP和IOC,我的这篇文章主要是说AOP在springboot中的使用,都是干活,直接使用,原理就先不说了,后续我的学习博客中慢慢更新,一步步来,先会使用再知道原理,深入了解。

什么是AOP?

原理在网上说的很多,我自己理解就是从这个AOP字面来理解,面向切面编程,先说一下java,java我们知道是面向对象编程,就是我的运行代码都是一步步按照我们的编写的代码来执行,因为我们写的代码都是一个对象一个对象的,然后就是对象之间的逻辑处理,比如:一个controller对象里,去到我们service层处理逻辑,再dao层拿数据,一个对象一个对象进行的,那个环节出问题都会导致程序报错,不能执行。

那什么是AOP面向切面编程呢,我在网上找了两个图,这个是我们数学写的切面图,我感觉这个能直观给我们看到这个切面在哪里。从下面两个图,我们首先把他当成我们的两个方法,一个方法是球形方法,一个方法是圆锥方法。方法里有两个面,对准了球和圆锥一个点,我们可以看到这个面是不会影响球和圆锥的形状,是独立的一个面,他存不存在都不会影响球和圆锥这个两个主体,就是像我现在要说的AOP切面,AOP就相当于我们球和圆锥上的这个面,完成是一个独立的东西,也可以说的我们方法的增强,就是不影响主体的情况增加一个逻辑处理,比如我们最常见的写日志,这个逻辑又是和业务不相干的,这个时候我们AOP就很完美的解决这个问题,而且AOP比这个两个面的功能更加强大,我在下面的介绍AOP的使用上会看到。

execution表示式

通过举例来说明一下,表达式举例:execution(* com.example.demo..*.*(..))

1) 执行 execution(xxx)表达式的主体,xxx是表达式的规则;
2) 第一个"*"符号  表示返回值的类型任意; * 表示所有类型(就是方法的返回参数没有限制), string 表示string类型(只有方法的返回参数是string才生效)
3) com.example.demoAOP所切的包,就是在哪个包下生效,也可以用*(代表所有包,但是一般不会用*,不然一启动项目你就会产生大量的切面执行)
4) 包名后面的".."表示当前包及子包
5) 第二个"*"表示类名,*即所有类, *Controller2 表示类名后缀是Controller2的类
6) .*(..) 表示任何方法名,括号表示参数,两个点表示任何参数类型, .*Test1(..)    表示方法名后缀是Test1的所有方法,参数无限制,
比如: .*Test(*)    表示方法名后缀是Test1的所有方法,参数只能是一个的,
比如: .*Test(*,*)    表示方法名后缀是Test1的所有方法,参数只能是两个的。


 注解说明

注解
注解名称说明
@Pointcut切入点
@Before在目标方法被调用之前执行,定义可以用切入点方法
@After在目标方法完成之后执行,定义可以用切入点方法
@AfterThrowing在目标方法出现异常时执行,定义可以用切入点方法
@AfterReturning在目标方法正常完成后执行,定义可以用切入点方法
@Around环绕

注:具体使用可以看我下面的整合使用

springboot整合AOP使用

先创建一个简单的springboot工程,还不会的同学可以看我的《小白篇springboot干货使用-我的第一个springboot

先给出我的代码目录结构

1.在pom加入依赖

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

完整的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 https://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.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springbootaop</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_aop</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</artifactId>
        </dependency>
        <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>
        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

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

</project>

2.在controller层创建一个AspectController类,给我们的切面使用

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AspectController {

    private static final Logger LOG = LoggerFactory.getLogger(AspectController.class);

    @RequestMapping("/testAsperctNoParameter")
    public String testAsperct() {
        LOG.info("我在controller层,没有参数");
        return "success";
    }

    @RequestMapping("/testAsperctOneParameter")
    public String testAsperct(String parameter) {
        LOG.info("我在controller层,一个参数:{}", parameter);
        int i = 1 / 0;
        return "success";
    }

    @RequestMapping("/testAsperctTwoParameter")
    public String testAsperct(String parameter1, String parameter2) {
        LOG.info("我在controller层,两个参数:{},{}", parameter1, parameter2);
        return "success";
    }


    @RequestMapping("/testAsperctAround")
    public String testAsperctAround(String parameter) {
        LOG.info("我在controller层,参数:{}", parameter);
        return "success";
    }

}

3.创建我们的切面MyAspect类,要我们的切面生效在类上加上注解@Aspect和 @Component,我们不需求去激活,springboot默认已经帮我们注册了,直接使用就行

package com.example.demo.aspectLogic;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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;

@Aspect
@Component
public class MyAspect {
    private static final Logger LOG = LoggerFactory.getLogger(MyAspect.class);

    /**
     * 定义切入点
     *
     * 表达式举例说明:execution(* com.example.demo..*.*(..))
     * 1) 执行 execution(xxx)	表达式的主体,xxx是表达式的规则;
     * 2) 第一个"*"符号	        表示返回值的类型任意; * 表示所有类型(就是方法的返回参数没有限制), string 表示string类型(只有方法的返回参数是string才生效)
     * 3) com.example.demo	    AOP所切的包,就是在哪个包下生效,也可以用*(代表所有包,但是一般不会用*,不然一启动项目你就会产生大量的切面执行)
     * 4) 包名后面的".."	        表示当前包及子包
     * 5) 第二个"*"	            表示类名,*即所有类, *Controller2 表示类名后缀是Controller2的类
     * 6) .*(..)	            表示任何方法名,括号表示参数,两个点表示任何参数类型, .*Test1(..)	表示方法名后缀是Test1的所有方法,参数无限制,
     *                          .*Test(*)	表示方法名后缀是Test1的所有方法,参数只能是一个的,
     *                          .*Test(*,*)	表示方法名后缀是Test1的所有方法,参数只能是两个的。
     */
    @Pointcut("execution(String com.example.demo.controller..*Asperct(..))")
    public void MyPointcut(){
    }

    /**
     * @Before 在目标方法被调用之前执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
//    @Before("execution(* com.example.demo.controller..*Test(..))")
    @Before("MyPointcut()")
    public void doBefore(JoinPoint joinPoint){
        LOG.info("我在AOP层,我在方法之前执行");

        //获取参数值
        Object[] args = joinPoint.getArgs();
        if (args.length > 0){
            for (int i = 0; i < args.length; i++) {
                LOG.info("参数" + i + ":" + args[i]);
            }
        }
        LOG.info("类的路径:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //request获取
        HttpServletRequest request = attributes.getRequest();
    }

    /**
     * @After 在目标方法完成之后执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
    @After("MyPointcut()")
    public void doAfter(JoinPoint joinPoint){
        LOG.info("我在AOP层,我在方法之后执行");
    }

    /**
     * @AfterThrowing 在目标方法出现异常时执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
    @AfterThrowing("MyPointcut()")
    public void doAfterThrowing(JoinPoint joinPoint){
        LOG.info("我在AOP层,我在异常时执行");
    }

    /**
     * @AfterReturning 在目标方法正常完成后执行,定义可以用切入点方法,也可以自己用execution()表达式
     * @param joinPoint
     */
    @AfterReturning("MyPointcut()")
    public void doAfterReturning(JoinPoint joinPoint){
        LOG.info("我在AOP层,我在返回一个结果后执行");
    }

    /**
     *
     * @param pJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(String com.example.demo.controller.AspectController.testAsperctAround(..))")
    public Object  doAround(ProceedingJoinPoint pJoinPoint) throws Throwable {
        LOG.info("我在AOP层");
        LOG.info("我在AOP层,@Around 我在方法之前执行");
        Object[] args = pJoinPoint.getArgs();
        args[0] = "修改参数值";
        Object proceed = pJoinPoint.proceed(args);
        LOG.info("我在AOP层,@Around 我在方法之后执行");
        return proceed;
    }

}

整个代码已经完成,我们的切面已经可以使用了,我们启动项目,通过在浏览器上,输入localhost:8080/testAsperctNoParameter就可以访问了

我们看回自己的控制台日志输出,已经成功使用到了切面的方法。

其实大家有没有发现,我们现在的idea工具已经超级强大的,方法是否没切面使用,在方法的傍边都是有一个标志的

然后我们到我们的切面类看,细心的同学,我们发现,这个标记有一个不同的标识来表明是上切,下切,四周。

到这里我们已经完全把AOP使用起来了,大家把这个代码手打一下,使用起来,有什么不懂,都可以提出来,大家一起学习,谢谢大家

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

binggoling

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值