前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot基础篇AOP之基本使用姿势小结

SpringBoot基础篇AOP之基本使用姿势小结

作者头像
一灰灰blog
发布2019-05-26 20:40:26
1.9K0
发布2019-05-26 20:40:26
举报
文章被收录于专栏:小灰灰小灰灰

原文:190301-SpringBoot基础篇AOP之基本使用姿势小结

一般来讲,谈到Spring的特性,绕不过去的就是DI(依赖注入)和AOP(切面),在将bean的系列中,说了DI的多种使用姿势;接下来看一下AOP的玩法

<!-- more -->

I. 背景知识

在实际使用之前有必要了解一下什么是AOP,以及AOP的几个基本概念

1. advice

  • before: 在方法执行之前被调用
  • after: 在方法执行之后调用
  • after returning: 方法执行成功之后
  • after throwing: 方法抛出异常之后
  • around: 环绕,自己在内部决定方法的执行时机,因此可以在之前之后做一些业务逻辑

2. join point

连接点,比如方法调用,方法执行,字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点

但 Spring AOP 目前仅支持方法执行 (method execution)

简单来说,Spring AOP中,PointCut就是那个被拦截的方法

3. pointcut

切点,用来描述满足什么规则的方法会被拦截

  • 正则表达式 : @Before("execution(public * com.git.hui.demo.base.bean.*.*(..))")
  • 注解拦截方式 :@Around("@annotation(parameterCheck)")

4. aspect

切面是切点和通知的结合。通知和切点共同定义了关于切面的全部内容,它是什么时候,在何时和何处完成功能

5. introduction

引入允许我们向现有的类添加新的方法或者属性

6. weaving

组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

简单来讲就是生成一个代理类,在调用被拦截的方法时,实际上执行的是代理类,这个代理类内部执行切面逻辑

II. 使用说明

1. 基本配置

首先是基本环境的搭建, 先贴上必要的xml配置, 使用aop需要引入包: spring-boot-starter-aop

代码语言:javascript
复制
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.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>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <java.version>1.8</java.version>
</properties>


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

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

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

2. 代码准备

首先创建一个被拦截的bean: com.git.hui.boot.aop.demo.DemoBean,如下

代码语言:javascript
复制
@Component
public class DemoBean {

    /**
     * 返回随机的字符串
     *
     * @param time
     * @return
     */
    public String randUUID(long time) {
        try {
            System.out.println("in randUUID before process!");
            return UUID.randomUUID() + "|" + time;
        } finally {
            System.out.println("in randUUID finally!");
        }
    }
}

接着在启动类中,执行

代码语言:javascript
复制
@SpringBootApplication
public class Application {
    
    public Application(DemoBean demoBean) {
        String ans = demoBean.randUUID(System.currentTimeMillis());
        System.out.println("----- ans: " + ans + "---------");
    }

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

3. AOP使用

在实际使用之前,需要创建一个切面,用@Aspect声明,其次切面也需要作为bean托付给Spring容器管理

代码语言:javascript
复制
@Aspect
@Component
public class AnoAspcet {
}
a. before

在方法调用之前,需要执行一些操作,这个时候可以使用 @Before 注解来声明before advice

一种可使用姿势如下,我们的切点直接在注解中进行定义,使用正则表达式的方式

代码语言:javascript
复制
@Before("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
public void doBefore(JoinPoint joinPoint) {
    System.out.println("do in Aspect before method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
}
b. after

在方法调用完毕之后,再执行一些操作,这个时候after就可以派上用场,为了考虑切点的通用性,我们可以考虑声明一个切点,使用@Pointcut注解

代码语言:javascript
复制
@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
public void point() {
}

使用pointcut的方式也比较简单,如下

代码语言:javascript
复制
@After("point()")
public void doAfter(JoinPoint joinPoint) {
    System.out.println("do in Aspect after method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
}
c. after returning

在正常返回结果之后,再次执行,这个也挺有意思的,通常使用这个advice时,一般希望获取返回结果,那么应该怎么处理呢?

  • org.aspectj.lang.annotation.AfterReturning#returning 指定返回结果对应参数name
  • 返回结果作为参数传入,要求类型一致,否则不生效
代码语言:javascript
复制
/**
 * 执行完毕之后,通过 args指定参数;通过 returning 指定返回的结果,要求返回值类型匹配
 *
 * @param time
 * @param result
 */
@AfterReturning(value = "point() && args(time)", returning = "result")
public void doAfterReturning(long time, String result) {
    System.out.println("do in Aspect after method return! args: " + time + " ans: " + result);
}
d. around

这个也比较常见,在方法执行前后干一些事情,比如常见的耗时统计,日志打印,安全控制等,很多都是基于around advice实现的

使用这个advice需要注意的是传入参数类型为 ProceedingJoinPoint,需要在方法内部显示执行org.aspectj.lang.ProceedingJoinPoint#proceed()来表示调用方法

代码语言:javascript
复制
@Around("point()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("do in Aspect around ------ before");
    Object ans = joinPoint.proceed();
    System.out.println("do in Aspect around ------- over! ans: " + ans);
    return ans;
}
e. 输出

执行之后输出如下

代码语言:javascript
复制
do in Aspect around ------ before
do in Aspect before method called! args: [1551433188205]
in randUUID before process!
in randUUID finally!
do in Aspect around ------- over! ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205
do in Aspect after method called! args: [1551433188205]
do in Aspect after method return! args: 1551433188205 ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205
----- ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205---------

从输出结果上,可以看到每个advice的使用范围,当然也带来了一些疑问

  • 可以存在多个同类型的advice,拦截同一个目标吗?(如两个around都拦截methodA方法,那么methodA方法被调用时,两个around advice是否都会执行)
  • 多个advice之间的优先级怎么定义?
  • aop拦截的目标方法有没有限制(对非public的方法可以拦截么?)
  • 被拦截的方法中存在相互调用的时候,会怎样?(如methodA,methodB都可以被拦截,且methodA中调用了methodB,那么在执行methodA时,methodB的各种advice是否会被触发?)
  • 基于注解的aop方式可以怎样用

以上这些问题留在下一篇进行介绍

III. 其他

0. 项目

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • I. 背景知识
    • 1. advice
      • 2. join point
        • 3. pointcut
          • 4. aspect
            • 5. introduction
              • 6. weaving
              • II. 使用说明
                • 1. 基本配置
                  • 2. 代码准备
                    • 3. AOP使用
                      • a. before
                      • b. after
                      • c. after returning
                      • d. around
                      • e. 输出
                  • III. 其他
                    • 0. 项目
                    相关产品与服务
                    容器服务
                    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档