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 注释以重用表达式。