在 Spring AOP 中返回通知后 – @AfterReturning

在 Spring AOP 中返回通知后 – @AfterReturning

简介

在匹配的方法完成执行并返回一个值后,返回通知被调用。您已经知道,这种匹配方法的名称是 Joinpoint。您可以使用 @AfterReturning 注释声明 After 返回通知。

您已经了解了上一篇文章中列出的 5 种建议类型,如下所示。

  • Before advice – @Before
  • After returning – @AfterReturning
  • After throwing – @AfterThrowing
  • After (finally) advice – @After
  • Around advice – @Around

@AfterReturning Advice 的执行

@AfterReturning 建议在 Joinpoint 方法正常返回后执行。我们只是指方法执行正常返回,匹配的方法可以有任何返回类型。

@Configuration
@Aspect
public class UppercaseAspect {
  @AfterReturning("c.jbd.saop.ar.aspect.DaoExpressions.findMethod()")
  public void uppercaseUsername(JoinPoint joinPoint, User result){
  //advice body
  }
}

访问返回值

您可能需要访问从 Joinpoint 返回的实际值。您可以使用绑定返回值的@AfterReturning 来获得该访问权限。下面的示例显示了访问返回值和 Joinpoint。

@Configuration
@Aspect
public class UppercaseAspect {
  @AfterReturning(
      pointcut = "c.jbd.saop.ar.aspect.DaoExpressions.findMethod()",
      returning = "result")
  public void uppercaseUsername(JoinPoint joinPoint, User result){
   //Advice body goes here
  }
}

返回属性中使用的名称必须与通知方法中的参数名称相对应。

当方法执行返回时,返回值被传递给通知方法。

 

返回通知后的示例 – @AfterReturning

问题陈述——假设我们有一个用户管理应用程序。我们需要确保大写的用户名总是从 userDao 返回。将使用 @AfterReturning 来操作来自 UserRepository:find() 方法的实际返回值。

步骤 1:添加依赖项

创建一个maven项目,在pom.xml中添加如下依赖

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <!-- Step-1 Add the dependencies -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
    </dependency>
<dependencies>

步骤 2:创建用户存储库

我们将方面应用到 DAO 类。所以我们创建了示例代码——UserRepository、User 和 Java 配置来注册这个类,如下所示。正如我们之前讨论过的,注解@EnableAspectJAutoProxy 用于启用Spring-Aop。

UserRepository.java

package c.jbd.saop.ar.dao;
import c.jbd.saop.ar.pojo.User;
import org.springframework.stereotype.Repository;
//A very stupid demo repository
@Repository
public class UserRepository {
  //find an User
  public User find(String username) {
    if (username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    return new User(username, "hello@world.com");
  }
  //Update an user
  public UserRepository update(String username, String email) {
    System.out.println("Update email: " + email);
    return this;
  }
  //Delete an user
  public boolean delete(String username) {
    if (username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    System.out.println("User deleted: " + username);
    return true;
  }
}

User.java

package c.jbd.saop.ar.pojo;
import java.util.StringJoiner;
//The User pojo
public class User {
  private String username;
  private String email;
  public User(String username, String email){
    this.email = email;
    this.username = username;
  }
  public String getUsername() {
    return username;
  }
  public User setUsername(String username) {
    this.username = username;
    return this;
  }
  public String getEmail() {
    return email;
  }
  public User setEmail(String email) {
    this.email = email;
    return this;
  }
  @Override
  public String toString() {
    return new StringJoiner(", ", User.class.getSimpleName() + "[", "]")
        .add("username='" + username + "'")
        .add("email='" + email + "'")
        .toString();
  }
}

ApplicationConfig.java

package c.jbd.saop.ar;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("c.jbd.saop.ar")
public class ApplicationConfig {
}

步骤 3:创建切入点表达式和advice

我会将 Pointcut 表达式和 Advice 方法保存在单独的类中。我只是试图向您展示表达式的组合和共享。整个表达式可以简化为“execution(* c.jbd.saop.ar.dao.UserRepository.find(..))”。

DaoExpressions.java

package c.jbd.saop.ar.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class DaoExpressions {
  //Match with only UserRepository
  @Pointcut("within(c.jbd.saop.ar.dao.UserRepository)")
  public void userDao() {}
  //Match with any find() method
  @Pointcut("execution(* find(..))")
  public void findMethod() {}
  @Pointcut("userDao() && findMethod()")
  public void userDaoFind(){}
}

UppercaseAspect.java

package c.jbd.saop.ar.aspect;
import c.jbd.saop.ar.pojo.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
@Configuration
@Aspect
public class UppercaseAspect {
  @AfterReturning(
      pointcut = "c.jbd.saop.ar.aspect.DaoExpressions.findMethod()",
      returning = "result")
  public void uppercaseUsername(JoinPoint joinPoint, User result){
    System.out.println("After method - " +
        joinPoint.getSignature().toShortString());
    System.out.println("original result:" + result);
    if (result.getUsername() != null){
      result.setUsername(result.getUsername().toUpperCase());
    }
    System.out.println("final result: " + result);
  }
}

第 4 步:测试示例

最后,我们将测试该示例以查看其输出。在运行测试用例之前,在 pom.xml 中添加测试依赖项。

<dependencies>
  ... 
   <!-- Test Dependencies-->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.2.RELEASE</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
...
<!-- Important for Jupiter-engine -->
<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M3</version>
      </plugin>
    </plugins>
  </build>
</project>

永远记得在使用 Junit-jupiter-* 时将 maven-surefire-plugin 升级到 3.0.0-M3 或更高版本。

添加测试依赖项后,在 src\test 中创建 Test 类。

package c.jbd.saop.ar;
import c.jbd.saop.ar.dao.UserRepository;
import c.jbd.saop.ar.pojo.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@SpringJUnitConfig(ApplicationConfig.class)
public class TestAfterReturning {
  @Autowired
  private UserRepository userRepository;
  @Test
  public void checkNull(){
    Assertions.assertNotNull(userRepository);
  }
  @Test
  public void testFindUser(){
    String username = "JsTobigdata";
    User user = userRepository.find(username);
    Assertions.assertEquals(user.getUsername(), username.toUpperCase());
    Assertions.assertNotEquals(user.getUsername(), username);
  }
}

测试应通过控制台中的以下输出。

After method - UserRepository.find(..)
original result:User[username='JsTobigdata', email='hello@world.com']
final result: User[username='JSTOBIGDATA', email='hello@world.com']

版权声明:著作权归作者所有。

相关推荐

Python解析http请求返回的JSON响应

python解析JSON响应可以使用以下两种方式:1、json.loadsimport json import requests response = requests.get(...) json_data = json.loads(response.text) 这种方法会把字符串转换为字典类型,这样就可以向json一样访问对象。2、r

Django响应http请求返回JSON数据

首先使用字典结构存放数据,例如返回错误信息:import json from django.http import HttpResponse response_data = {} response_data['result'] = 'error' response_data['message'] =&nb

Swift 3从异步调用返回数据

是不能直接在异步调用返回数据,一种替代的方案是向异步调用的函数里传入回调函数,当异步任务完成后,使用回调函数处理结果。Swift 3示例:class func getData(completionHandler: @escaping (data: NSArray) -> ()) {...let task = session.dataTask(with:url) { data, resp

Flask返回静态文件

可以使用flask的send_from_directory方法来发送静态文件,相当简单。send_from_directory使用示例from flask import Flask, request, send_from_directory# 设置项目的根目录作为静态文件的文件夹。你可以根据具体使用改变app = Flask(__name__, static_url_path='')@app.ro