静下心来的个人专栏
上一篇

Spring AOP 中的 @Before advice

广告
选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。
大纲

Spring AOP 中的 @Before advice

在本文中,您将了解 Spring AOP 中的 @Before 建议。在继续本教程之前,请确保您对 AOP 术语有很好的理解。您已经了解了上一篇文章中列出的 5 种advice类型,如下所示。

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

执行@Before Advice

@Before advice在方法执行之前执行。用技术术语来说,在执行匹配的连接点方法之前执行带有 @Before 注释的advice。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class BeforeExample {
    @Before("execution(* com.jbd.myapp.service.*.*(..)")
    public void doAccessCheck() {
        // ...
    }
}

使用@Before Advice 注解的步骤

将必要的依赖项添加到您的 pom.xml。从技术上讲,您只需要 aspectjweaver 和 spring-aop jar。由于这是一个 spring 应用程序,所以我们还需要 spring-core 和 spring-context jar。此外,您可以添加 JUnit 和 spring-test jar 来运行测试用例。

步骤 1:添加依赖项

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 步:创建示例类

在它们各自的包中创建示例类,以便我们有示例代码来应用之前的建议。基本上我们将创建 2 个存储库类,每个都有 add() 和 delete() 方法。

ActorRepository.java

package c.jbd.saop.gettingstarted.dao;
import org.springframework.stereotype.Repository;
@Repository
public class ActorRepository {
  //Add an actor
  public ActorRepository add(String actorName) {
    if (actorName == null) {
      throw new RuntimeException("actorName is null", new NullPointerException());
    }
    System.out.println("New Actor added: " + actorName);
    return this;
  }
  //Delete an actor
  public boolean delete(String actorName) {
    if (actorName == null) {
      throw new RuntimeException("actorName is null", new NullPointerException());
    }
    System.out.println("Actor deleted: " + actorName);
    return true;
  }
}

MovieRepository.java

package c.jbd.saop.gettingstarted.dao;
import org.springframework.stereotype.Repository;
@Repository
public class MovieRepository {
  //Add a movie method
  public MovieRepository add(String movieName) {
    if (movieName == null) {
      throw new RuntimeException("movieName is null", new NullPointerException());
    }
    System.out.println("New movie added: " + movieName);
    return this;
  }
  //Delete a movie
  public boolean delete(String movieName) {
    if (movieName == null) {
      throw new RuntimeException("movieName is null", new NullPointerException());
    }
    System.out.println("Movie deleted: " + movieName);
    return true;
  }
}

ApplicationConfig.java

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

步骤 3:启用 AspectJ

通过将 @EnableAspectJAutoProxy 添加到 ApplicationConfig 类来启用 AspectJ 自动代理,如下所示。基本上,这将在应用程序中启用 Spring-AOP。

package c.jbd.saop.gettingstarted.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
//Step-3 - Add the annotation
@EnableAspectJAutoProxy
@ComponentScan("c.jbd.saop.gettingstarted")
public class ApplicationConfig {
}

第 4 步:声明advice类

现在让我们创建一个名为 BeforeAdvice 的类,如下所示。使用 @Aspect 注释对方面类进行注释以将其标记为方面非常重要。我们还需要 Spring 选择它,所以使用 @Configurartion 注释。我们将重新审视@Before 注解中的表达式,也称为Pointcut。

package c.jbd.saop.gettingstarted.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Configuration;
@Configuration
@Aspect //Important to say this is an Aspect config class
public class BeforeAdvice {
  //execution(RETURN_TYPE PACKAGE.CLASS.METHOD(..PARAMETER_LIST))
  //execution(* PACKAGE.*.*(..))
  @Before("execution(* c.jbd.saop.gettingstarted.dao.*.add(..))")
  public void allDaoAddMethods(JoinPoint joinPoint) {
    System.out.println("Intercepted method: " + joinPoint);
    System.out.println("Arguments: " + joinPoint.getArgs());
    System.out.println(joinPoint.getTarget());
  }
  @Before("execution(* c.jbd.saop.gettingstarted.dao.*.*(..))")
  public void allDaoAnyMethod(JoinPoint joinPoint) {
    System.out.println("Intercepted method: " + joinPoint);
    System.out.println("Arguments: " + joinPoint.getArgs());
    System.out.println(joinPoint.getTarget());
  }
}
  • 我们创建了两个 Before 方面,每个方面都有单独的 Pointcut 表达式。
  • 第一个适用于所有存储库类的 add() 方法,第二个方面适用于所有存储库类的方法。
  • 换句话说,将为 add() 以及 ActorRepository 和 MovieRepository 的 delete() 方法调用第二个方面。

除了@Configuration 注解,我们还可以将@Component 用于切面类。但是,由于我们自己不需要这样的实例,所以 Spring 建议使用 @Configuration。

第 5 步:测试示例

最后,我们将测试该示例以查看其输出。将使用带有 junit-jupiter-api 的正常弹簧测试。因此,请确保您具有如下所示的测试依赖项。

<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>
package c.jbd.saop.gettingstarted;
import c.jbd.saop.gettingstarted.config.ApplicationConfig;
import c.jbd.saop.gettingstarted.dao.ActorRepository;
import c.jbd.saop.gettingstarted.dao.MovieRepository;
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 TestBeforeAdvice {
  @Autowired
  private ActorRepository actorRepository;
  @Autowired
  private MovieRepository movieRepository;
  @Test
  public void testAddAspects() {
    actorRepository.add("Hrithik Roshan");
    movieRepository.add("Sholey");
  }
}

输出:

Intercepted method: execution(ActorRepository c.jbd.saop.gettingstarted.dao.ActorRepository.add(String))
Arguments: Hrithik Roshan
c.jbd.saop.gettingstarted.dao.ActorRepository@6e521c1e
Intercepted method: execution(ActorRepository c.jbd.saop.gettingstarted.dao.ActorRepository.add(String))
Arguments: Hrithik Roshan
c.jbd.saop.gettingstarted.dao.ActorRepository@6e521c1e
New Actor added: Hrithik Roshan

Intercepted method: execution(MovieRepository c.jbd.saop.gettingstarted.dao.MovieRepository.add(String))
Arguments: Sholey
c.jbd.saop.gettingstarted.dao.MovieRepository@61078690
Intercepted method: execution(MovieRepository c.jbd.saop.gettingstarted.dao.MovieRepository.add(String))
Arguments: Sholey
c.jbd.saop.gettingstarted.dao.MovieRepository@61078690
New movie added: Sholey

您可以在上面的输出中看到,Repository BeforeAdvice:allDaoAddMethods() 和 BeforeAdvice:allDaoAnyMethod() 的 add() 方法在实际方法调用之前执行。

第 6 步:附加测试

现在让我们在 TestBeforeAdvice 类中添加另一个 @Testcase 并观察输出。

@SpringJUnitConfig(ApplicationConfig.class)
public class TestBeforeAdvice {  
...
...
  @Test
  public void testDeleteAspects() {
    actorRepository.delete("John Doe");
    movieRepository.delete("Abc");
  }
}
Intercepted method: execution(boolean c.jbd.saop.gettingstarted.dao.ActorRepository.delete(String))
Arguments: John Doe
c.jbd.saop.gettingstarted.dao.ActorRepository@6e521c1e
Actor deleted: John Doe
Intercepted method: execution(boolean c.jbd.saop.gettingstarted.dao.MovieRepository.delete(String))
Arguments: Abc
c.jbd.saop.gettingstarted.dao.MovieRepository@61078690
Movie deleted: Abc

对于这两个 delete() 调用,只有 BeforeAdvice:allDaoAnyMethod() 被执行,因为这是唯一匹配的方面。

总结

我试图通过一个简单的示例来了解 @Before 建议在 Spring AOP 中的工作原理。接下来,我们将探索 Pointcut 表达式和 @Pointcut 注释以重用表达式。

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