aop中joinpoint
Spring Framework is developed on two core concepts – Dependency Injection and Aspect Oriented Programming ( Spring AOP).
Spring Framework是基于两个核心概念开发的: 依赖注入和面向方面的编程(Spring AOP)。
SpringAOP (Spring AOP)
We have already seen how Spring Dependency Injection works, today we will look into the core concepts of Aspect-Oriented Programming and how we can implement it using Spring Framework.
我们已经了解了Spring Dependency Injection的工作原理,今天我们将研究面向方面的编程的核心概念以及如何使用Spring Framework来实现它。
Spring AOP概述 (Spring AOP Overview)
Most of the enterprise applications have some common crosscutting concerns that are applicable to different types of Objects and modules. Some of the common crosscutting concerns are logging, transaction management, data validation, etc.
大多数企业应用程序都具有一些常见的横切关注点,适用于不同类型的对象和模块。 一些常见的横切关注点是日志记录,事务管理,数据验证等。
In Object Oriented Programming, modularity of application is achieved by Classes whereas in Aspect Oriented Programming application modularity is achieved by Aspects and they are configured to cut across different classes.
在面向对象编程中,应用程序的模块化是通过类实现的,而在面向方面编程中,应用程序的模块化是由Aspects实现的,并且它们被配置为跨越不同的类。
Spring AOP takes out the direct dependency of crosscutting tasks from classes that we can’t achieve through normal object oriented programming model. For example, we can have a separate class for logging but again the functional classes will have to call these methods to achieve logging across the application.
Spring AOP从我们无法通过常规的面向对象的编程模型实现的类中获取横切任务的直接依赖关系。 例如,我们可以有一个单独的日志记录类,但是功能类也必须调用这些方法来实现整个应用程序的日志记录。
面向方面的编程核心概念 (Aspect Oriented Programming Core Concepts)
Before we dive into the implementation of Spring AOP implementation, we should understand the core concepts of AOP.
在深入探讨Spring AOP实现的实现之前,我们应该了解AOP的核心概念。
- Aspect: An aspect is a class that implements enterprise application concerns that cut across multiple classes, such as transaction management. Aspects can be a normal class configured through Spring XML configuration or we can use Spring AspectJ integration to define a class as Aspect using
@Aspect
annotation. 方面 :方面是一个实现企业应用程序关注点的类,该关注点跨多个类,例如事务管理。 Aspects可以是通过Spring XML配置配置的普通类,也可以使用Spring AspectJ集成使用@Aspect
批注将类定义为Aspect。 - Join Point: A join point is a specific point in the application such as method execution, exception handling, changing object variable values, etc. In Spring AOP a join point is always the execution of a method. 连接点 :连接点是应用程序中的特定点,例如方法执行,异常处理,更改对象变量值等。在Spring AOP中,连接点始终是方法的执行。
- Advice: Advices are actions taken for a particular join point. In terms of programming, they are methods that get executed when a certain join point with matching pointcut is reached in the application. You can think of Advices as Struts2 interceptors or Servlet Filters. 建议 :建议是针对特定连接点采取的操作。 就编程而言,它们是在应用程序中达到具有匹配切入点的特定连接点时执行的方法。 您可以将Advices视为Struts2拦截器或Servlet过滤器 。
- Pointcut: Pointcut is expressions that are matched with join points to determine whether advice needs to be executed or not. Pointcut uses different kinds of expressions that are matched with the join points and Spring framework uses the AspectJ pointcut expression language. 切入点(Pointcut) :切入点是与连接点匹配的表达式,用于确定是否需要执行建议。 Pointcut使用与联接点匹配的不同类型的表达式,Spring框架使用AspectJ Pointcut表达式语言。
- Target Object: They are the object on which advices are applied. Spring AOP is implemented using runtime proxies so this object is always a proxied object. What is means is that a subclass is created at runtime where the target method is overridden and advice are included based on their configuration. 目标对象 :它们是应用建议的对象。 Spring AOP是使用运行时代理实现的,因此此对象始终是代理对象。 这意味着将在运行时创建一个子类,在该子类中将覆盖目标方法,并根据其配置包含建议。
- AOP proxy: Spring AOP implementation uses JDK dynamic proxy to create the Proxy classes with target classes and advice invocations, these are called AOP proxy classes. We can also use CGLIB proxy by adding it as the dependency in the Spring AOP project. AOP代理 :Spring AOP实现使用JDK动态代理来创建具有目标类和建议调用的Proxy类,这些被称为AOP代理类。 我们还可以通过将CGLIB代理添加为Spring AOP项目中的依赖项来使用它。
- Weaving: It is the process of linking aspects with other objects to create the advised proxy objects. This can be done at compile time, load time or at runtime. Spring AOP performs weaving at the runtime. 编织 :这是将各方面与其他对象链接以创建建议的代理对象的过程。 这可以在编译时,加载时或运行时完成。 Spring AOP在运行时执行编织。
AOP咨询类型 (AOP Advice Types)
Based on the execution strategy of advice, they are of the following types.
根据建议的执行策略,它们属于以下类型。
- Before Advice: These advices runs before the execution of join point methods. We can use
@Before
annotation to mark an advice type as Before advice. 咨询之前 :这些咨询在执行连接点方法之前运行。 我们可以使用@Before
注释将建议类型标记为Before建议。 - After (finally) Advice: An advice that gets executed after the join point method finishes executing, whether normally or by throwing an exception. We can create after advice using
@After
annotation. 之后(最终)建议 :在连接点方法完成执行之后(无论是正常还是引发异常)执行的建议。 我们可以使用@After
注释创建事后通知。 - After Returning Advice: Sometimes we want advice methods to execute only if the join point method executes normally. We can use
@AfterReturning
annotation to mark a method as after returning advice. 返回建议后 :有时,我们希望建议方法仅在联接点方法正常执行时才执行。 我们可以使用@AfterReturning
批注将方法标记为返回建议之后。 - After Throwing Advice: This advice gets executed only when join point method throws exception, we can use it to rollback the transaction declaratively. We use
@AfterThrowing
annotation for this type of advice. 投掷建议后 :仅在连接点方法抛出异常时才执行此建议,我们可以使用它以声明方式回滚事务。 对于此类建议,我们使用@AfterThrowing
批注。 - Around Advice: This is the most important and powerful advice. This advice surrounds the join point method and we can also choose whether to execute the join point method or not. We can write advice code that gets executed before and after the execution of the join point method. It is the responsibility of around advice to invoke the join point method and return values if the method is returning something. We use
@Around
annotation to create around advice methods. 围绕建议 :这是最重要和最有力的建议。 该建议围绕着连接点方法,我们还可以选择是否执行连接点方法。 我们可以编写在执行连接点方法之前和之后执行的建议代码。 周围建议的责任是调用连接点方法并在该方法返回某些值时返回值。 我们使用@Around
注释创建围绕建议的方法。
The points mentioned above may sound confusing but when we will look at the implementation of Spring AOP, things will be more clear. Let’s start creating a simple Spring project with AOP implementations. Spring provides support for using AspectJ annotations to create aspects and we will be using that for simplicity. All the above AOP annotations are defined in org.aspectj.lang.annotation
package.
上面提到的几点听起来可能令人困惑,但是当我们看一下Spring AOP的实现时,情况将会更加清楚。 让我们开始创建一个带有AOP实现的简单Spring项目。 Spring提供了对使用AspectJ注释来创建方面的支持,为简单起见,我们将使用它。 以上所有AOP注释都在org.aspectj.lang.annotation
包中定义。
Spring Tool Suite provides useful information about the aspects, so I would suggest you use it. If you are not familiar with STS, I would recommend you to have a look at Spring MVC Tutorial where I have explained how to use it.
Spring Tool Suite提供了有关各方面的有用信息,所以我建议您使用它。 如果您不熟悉STS,建议您看一看Spring MVC教程 ,那里已经解释了如何使用它。
Spring AOP示例 (Spring AOP Example)
Create a new Simple Spring Maven project so that all the Spring Core libraries are included in the pom.xml files and we don’t need to include them explicitly. Our final project will look like the below image, we will look into the Spring core components and Aspect implementations in detail.
创建一个新的Simple Spring Maven项目,以便所有Spring Core库都包含在pom.xml文件中,而我们不需要显式地包含它们。 我们的最终项目将如下图所示,我们将详细研究Spring核心组件和Aspect实现。
Spring AOP AspectJ依赖关系 (Spring AOP AspectJ Dependencies)
Spring framework provides AOP support by default but since we are using AspectJ annotations for configuring aspects and advice, we would need to include them in the pom.xml file.
Spring框架默认情况下提供AOP支持,但是由于我们使用AspectJ注释来配置方面和建议,因此我们需要将它们包括在pom.xml文件中。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringAOPExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
<!-- AspectJ -->
<aspectj.version>1.7.4</aspectj.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- AspectJ dependencies -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
Notice that I have added aspectjrt
and aspectjtools
dependencies (version 1.7.4) in the project. Also I have updated the Spring framework version to be the latest one as of date i.e 4.0.2.RELEASE.
请注意,我已经加入aspectjrt
和aspectjtools
项目依赖(1.7.4版本)。 另外,我已经将Spring框架版本更新为最新版本,即4.0.2.RELEASE。
型号类别 (Model Class)
Let’s create a simple java bean that we will use for our example with some additional methods.
让我们创建一个简单的Java bean,并将其用于示例以及其他一些方法。
Employee.java code:
Employee.java代码:
package com.journaldev.spring.model;
import com.journaldev.spring.aspect.Loggable;
public class Employee {
private String name;
public String getName() {
return name;
}
@Loggable
public void setName(String nm) {
this.name=nm;
}
public void throwException(){
throw new RuntimeException("Dummy Exception");
}
}
Did you noticed that setName() method is annotated with Loggable
annotation. It is a custom java annotation defined by us in the project. We will look into it’s usage later on.
您是否注意到setName()方法使用Loggable
注释进行了注释。 这是我们在项目中定义的自定义Java注释 。 稍后我们将研究它的用法。
服务等级 (Service Class)
Let’s create a service class to work with the Employee bean.
让我们创建一个与Employee bean一起使用的服务类。
EmployeeService.java code:
EmployeeService.java代码:
package com.journaldev.spring.service;
import com.journaldev.spring.model.Employee;
public class EmployeeService {
private Employee employee;
public Employee getEmployee(){
return this.employee;
}
public void setEmployee(Employee e){
this.employee=e;
}
}
I could have used Spring annotations to configure it as a Spring Component, but we will use XML based configuration in this project. EmployeeService class is very standard and just provides us an access point for Employee beans.
我本可以使用Spring批注将其配置为Spring Component,但是在该项目中我们将使用基于XML的配置。 EmployeeService类是非常标准的,仅为我们提供了Employee bean的访问点。
带有AOP的Spring Bean配置 (Spring Bean Configuration with AOP)
If you are using STS, you have the option to create “Spring Bean Configuration File” and chose AOP schema namespace but if you are using some other IDE, you can simply add it in the spring bean configuration file.
如果使用的是STS,则可以选择创建“ Spring Bean配置文件”并选择AOP模式名称空间,但是如果使用的是其他IDE,则只需将其添加到spring Bean配置文件中即可。
My project bean configuration file looks like below.
我的项目bean配置文件如下所示。
spring.xml:
spring.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy />
<!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="com.journaldev.spring.model.Employee">
<property name="name" value="Dummy Name"></property>
</bean>
<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.journaldev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.journaldev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.journaldev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.journaldev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.journaldev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.journaldev.spring.aspect.EmployeeAnnotationAspect" />
</beans>
For using Spring AOP in Spring beans, we need to do the following:
为了在Spring bean中使用Spring AOP,我们需要执行以下操作:
- Declare AOP namespace like xmlns:aop=”https://www.springframework.org/schema/aop” 声明AOP命名空间,例如xmlns:aop =” https://www.springframework.org/schema/aop”
- Add aop:aspectj-autoproxy element to enable Spring AspectJ support with auto proxy at runtime 添加aop:aspectj-autoproxy元素以在运行时启用具有自动代理的Spring AspectJ支持
- Configure Aspect classes as other Spring beans 与其他Spring Bean一样配置Aspect类
You can see that I have a lot of aspects defined in the spring bean configuration file, it’s time to look into them one by one.
您可以看到,我在spring bean配置文件中定义了很多方面,是时候一个个地研究它们了。
在Aspect示例之前进行Spring AOP (Spring AOP Before Aspect Example)
EmployeeAspect.java code:
EmployeeAspect.java代码:
package com.journaldev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAspect {
@Before("execution(public String getName())")
public void getNameAdvice(){
System.out.println("Executing Advice on getName()");
}
@Before("execution(* com.journaldev.spring.service.*.get*())")
public void getAllAdvice(){
System.out.println("Service method getter called");
}
}
Important points in above aspect class are:
上述方面类中的重要点是:
- Aspect classes are required to have
@Aspect
annotation. 方面类必须具有@Aspect
批注。 - @Before annotation is used to create Before advice @Before注释用于创建Before建议
- The string parameter passed in the
@Before
annotation is the Pointcut expression@Before
批注中传递的字符串参数是Pointcut表达式 - getNameAdvice() advice will execute for any Spring Bean method with signature
public String getName()
. This is a very important point to remember, if we will create Employee bean using new operator the advices will not be applied. Only when we will use ApplicationContext to get the bean, advices will be applied. 对于具有签名public String getName()
任何Spring Bean方法,都会执行getNameAdvice()通知。 要记住这一点很重要,如果我们将使用新的运算符创建Employee bean,则建议将不适用。 只有当我们将使用ApplicationContext获取bean时,才会应用建议。 - We can use asterisk (*) as wild card in Pointcut expressions, getAllAdvice() will be applied for all the classes in
com.journaldev.spring.service
package whose name starts withget
and doesn’t take any arguments. 我们可以在Pointcut表达式中使用星号(*)作为通配符, getAllAdvice()将应用于com.journaldev.spring.service
包中所有名称以get
开头且不带任何参数的类。
We will look at the advice in action in a test class after we have looked into all the different types of advices.
在研究了所有不同类型的建议之后,我们将在测试课程中查看实际的建议。
Spring AOP切入点方法和重用 (Spring AOP Pointcut Methods and Reuse)
Sometimes we have to use same Pointcut expression at multiple places, we can create an empty method with @Pointcut
annotation and then use it as an expression in the advices.
有时我们必须在多个地方使用相同的Pointcut表达式,我们可以使用@Pointcut
注释创建一个空方法,然后将其用作建议中的表达式。
EmployeeAspectPointcut.java code:
EmployeeAspectPointcut.java代码:
package com.journaldev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class EmployeeAspectPointcut {
@Before("getNamePointcut()")
public void loggingAdvice(){
System.out.println("Executing loggingAdvice on getName()");
}
@Before("getNamePointcut()")
public void secondAdvice(){
System.out.println("Executing secondAdvice on getName()");
}
@Pointcut("execution(public String getName())")
public void getNamePointcut(){}
@Before("allMethodsPointcut()")
public void allServiceMethodsAdvice(){
System.out.println("Before executing service method");
}
//Pointcut to execute on all the methods of classes in a package
@Pointcut("within(com.journaldev.spring.service.*)")
public void allMethodsPointcut(){}
}
Above example is very clear, rather than expression we are using method name in the advice annotation argument.
上面的示例非常清楚,我们在通知注释参数中使用的是方法名称,而不是表达式。
Spring AOP JoinPoint和建议参数 (Spring AOP JoinPoint and Advice Arguments)
We can use JoinPoint as a parameter in the advice methods and using it get the method signature or the target object.
我们可以在咨询方法中使用JoinPoint作为参数,并使用它获取方法签名或目标对象。
We can use args()
expression in the pointcut to be applied to any method that matches the argument pattern. If we use this, then we need to use the same name in the advice method from where the argument type is determined. We can use Generic objects also in the advice arguments.
我们可以在切入点中使用args()
表达式,以将其应用于与参数模式匹配的任何方法。 如果使用此选项,则需要在确定参数类型的地方的advice方法中使用相同的名称。 我们也可以在建议参数中使用通用对象 。
EmployeeAspectJoinPoint.java code:
EmployeeAspectJoinPoint.java代码:
package com.journaldev.spring.aspect;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAspectJoinPoint {
@Before("execution(public void com.journaldev.spring.model..set*(*))")
public void loggingAdvice(JoinPoint joinPoint){
System.out.println("Before running loggingAdvice on method="+joinPoint.toString());
System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));
}
//Advice arguments, will be applied to bean methods with single String argument
@Before("args(name)")
public void logStringArguments(String name){
System.out.println("String argument passed="+name);
}
}
建议后的Spring AOP示例 (Spring AOP After Advice Example)
Let’s look at a simple aspect class with an example of After, After Throwing and After Returning advice.
让我们看一个简单的方面类,其中包含After,Throwing和After Return建议的示例。
EmployeeAfterAspect.java code:
EmployeeAfterAspect.java代码:
package com.journaldev.spring.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EmployeeAfterAspect {
@After("args(name)")
public void logStringArguments(String name){
System.out.println("Running After Advice. String argument passed="+name);
}
@AfterThrowing("within(com.journaldev.spring.model.Employee)")
public void logExceptions(JoinPoint joinPoint){
System.out.println("Exception thrown in Employee Method="+joinPoint.toString());
}
@AfterReturning(pointcut="execution(* getName())", returning="returnString")
public void getNameReturningAdvice(String returnString){
System.out.println("getNameReturningAdvice executed. Returned String="+returnString);
}
}
We can use within
in pointcut expression to apply the advice to all the methods in the class. We can use @AfterReturning advice to get the object returned by the advised method.
We have throwException() method in the Employee bean to showcase the use of After Throwing advice.
我们可以使用within
的切入点表达意见适用于在类的所有方法。 我们可以使用@AfterReturning建议来获取由所建议方法返回的对象。
我们在Employee bean中有throwException()方法来展示After Throwing建议的用法。
围绕Aspect示例的Spring AOP (Spring AOP Around Aspect Example)
As explained earlier, we can use Around aspect to cut the method execution before and after. We can use it to control whether the advised method will execute or not. We can also inspect the returned value and change it. This is the most powerful advice and needs to be applied properly.
如前所述,我们可以使用Around方面来减少前后的方法执行。 我们可以使用它来控制建议的方法是否执行。 我们还可以检查返回的值并进行更改。 这是最有力的建议,需要正确应用。
EmployeeAroundAspect.java code:
EmployeeAroundAspect.java代码:
package com.journaldev.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EmployeeAroundAspect {
@Around("execution(* com.journaldev.spring.model.Employee.getName())")
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("Before invoking getName() method");
Object value = null;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("After invoking getName() method. Return value="+value);
return value;
}
}
Around advice are always required to have ProceedingJoinPoint as an argument and we should use it’s proceed() method to invoke the target object advised method.
始终需要在建议周围使用ProceedingJoinPoint作为参数,我们应该使用它的proceed()方法来调用目标对象的建议方法。
If advised method is returning something, it’s advice responsibility to return it to the caller program. For void methods, advice method can return null.
如果建议方法返回了某些内容,则建议职责是将其返回给调用程序。 对于void方法,advice方法可以返回null。
Since around advice cut around the advised method, we can control the input and output of the method as well as it’s execution behavior.
由于围绕建议的建议围绕建议的方法,因此我们可以控制方法的输入和输出及其执行行为。
带有自定义注释切入点的Spring建议 (Spring Advice with Custom Annotation Pointcut)
If you look at all the above advice pointcut expressions, there are chances that they get applied to some other beans where it’s not intended. For example, someone can define a new spring bean with getName() method and the advice will start getting applied to that even though it was not intended. That’s why we should keep the scope of pointcut expression as narrow as possible.
如果您查看以上所有建议切入点表达式,它们很可能会被应用到其他一些不希望使用的bean。 例如,某人可以使用getName()方法定义一个新的spring bean,即使该建议不是故意的,该建议也将开始应用。 这就是为什么我们应该使切入点表达式的范围尽可能地窄。
An alternative approach is to create a custom annotation and annotate the methods where we want the advice to be applied. This is the purpose of having Employee setName() method annotated with @Loggable annotation.
另一种方法是创建自定义注释,并在我们希望应用建议的地方注释方法。 这是使用@Loggable批注对Employee setName()方法进行批注的目的。
Spring Framework @Transactional annotation is a great example of this approach for Spring Transaction Management.
Spring Framework @Transactional注释是这种用于Spring事务管理的方法的很好的例子。
Loggable.java code:
Loggable.java代码:
package com.journaldev.spring.aspect;
public @interface Loggable {
}
EmployeeAnnotationAspect.java code:
EmployeeAnnotationAspect.java代码:
package com.journaldev.spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAnnotationAspect {
@Before("@annotation(com.journaldev.spring.aspect.Loggable)")
public void myAdvice(){
System.out.println("Executing myAdvice!!");
}
}
The myAdvice() method will advice only setName() method. This is a very safe approach and whenever we want to apply the advice on any method, all we need is to annotate it with Loggable annotation.
myAdvice()方法将仅建议setName()方法。 这是一种非常安全的方法,每当我们要将建议应用于任何方法时,我们所需要做的就是使用Loggable注释对其进行注释。
Spring AOP XML配置 (Spring AOP XML Configuration)
I always prefer annotation but we also have the option to configure aspects in the spring configuration file. For example, let’s say we have a class as below.
我总是喜欢注释,但是我们也可以选择在spring配置文件中配置方面。 例如,假设我们有一个如下的类。
EmployeeXMLConfigAspect.java code:
EmployeeXMLConfigAspect.java代码:
package com.journaldev.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class EmployeeXMLConfigAspect {
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");
Object value = null;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value="+value);
return value;
}
}
We can configure it by including the following configuration in the Spring Bean config file.
我们可以通过在Spring Bean配置文件中包含以下配置来配置它。
<bean name="employeeXMLConfigAspect" class="com.journaldev.spring.aspect.EmployeeXMLConfigAspect" />
<!-- Spring AOP XML Configuration -->
<aop:config>
<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
<aop:pointcut expression="execution(* com.journaldev.spring.model.Employee.getName())" id="getNamePointcut"/>
<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
</aop:aspect>
</aop:config>
AOP xml config elements purpose is clear from their name, so I won’t go into much detail about it.
AOP xml config元素的用途从它们的名称中可以清楚看出,因此我将不对其进行详细介绍。
Spring AOP示例 (Spring AOP Example)
Let’s have a simple Spring program and see how all these aspects cut through the bean methods.
让我们有一个简单的Spring程序,看看所有这些方面如何贯穿bean方法。
SpringMain.java code:
SpringMain.java代码:
package com.journaldev.spring.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.service.EmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);
System.out.println(employeeService.getEmployee().getName());
employeeService.getEmployee().setName("Pankaj");
employeeService.getEmployee().throwException();
ctx.close();
}
}
Now when we execute the above program, we get the following output.
现在,当我们执行上述程序时,我们将获得以下输出。
Mar 20, 2014 8:50:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Thu Mar 20 20:50:09 PDT 2014]; root of context hierarchy
Mar 20, 2014 8:50:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Service method getter called
Before executing service method
EmployeeXMLConfigAspect:: Before invoking getName() method
Executing Advice on getName()
Executing loggingAdvice on getName()
Executing secondAdvice on getName()
Before invoking getName() method
After invoking getName() method. Return value=Dummy Name
getNameReturningAdvice executed. Returned String=Dummy Name
EmployeeXMLConfigAspect:: After invoking getName() method. Return value=Dummy Name
Dummy Name
Service method getter called
Before executing service method
String argument passed=Pankaj
Before running loggingAdvice on method=execution(void com.journaldev.spring.model.Employee.setName(String))
Agruments Passed=[Pankaj]
Executing myAdvice!!
Running After Advice. String argument passed=Pankaj
Service method getter called
Before executing service method
Exception thrown in Employee Method=execution(void com.journaldev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exception
at com.journaldev.spring.model.Employee.throwException(Employee.java:19)
at com.journaldev.spring.model.Employee$$FastClassBySpringCGLIB$$da2dc051.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
at com.journaldev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)
at com.journaldev.spring.main.SpringMain.main(SpringMain.java:17)
You can see that advices are getting executed one by one based on their pointcut configurations. You should configure them one by one to avoid confusion.
您会看到建议正在根据切入点配置逐一执行。 您应该一个一个地配置它们,以避免混淆。
That’s all for Spring AOP Example Tutorial, I hope you learned the basics of AOP with Spring and can learn more from examples. Download the sample project from below link and play around with it.
这就是Spring AOP Example Tutorial的全部内容,希望您通过Spring学习了AOP的基础知识,并且可以从示例中学到更多。 从下面的链接下载示例项目并进行试用。
aop中joinpoint