Spring Aspect Oriented Programming (AOP)

1. What is AOP

Aspect-Oriented Programming ( AOP ) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. In addition to classes (classes), AOP provides aspects . Aspects modularize concerns, such as transaction management across multiple types and objects. (These concerns are often called  crosscutting  concerns.)

A key component of Spring is the  AOP framework . Nevertheless, the Spring IoC container does not depend on AOP, which means that you can freely choose whether to use AOP. AOP provides a powerful middleware solution, which makes the Spring IoC container more complete.

 

2. Important concepts related to AOP (must be understood)

  • Aspect: A modularity of concerns that may cut across multiple objects. Transaction management is a good example of cross-cutting concerns in J2EE applications. In Spring AOP, aspects can be implemented using generic classes (schema-based style) or with  @Aspect  annotations ( @AspectJ style) in ordinary classes.

  • Joinpoint: A specific point in the execution of a program, such as when a method is called or when an exception is handled. In Spring AOP, a join point always represents the execution of a method. By declaring a  parameter of type org.aspectj.lang.JoinPoint  , the body part of the Advice can obtain the join point information.

  • Advice: An action performed at a particular Joinpoint of an aspect. There are various types of notifications, including "around", "before", and "after". Types of notifications are discussed in a later section. Many AOP frameworks, including Spring, use interceptors as their advice model and maintain a joinpoint-centric chain of interceptors.

  • Pointcut: An assertion that matches a Joinpoint. Advice is associated with a pointcut expression and runs on join points that satisfy this pointcut (for example, when a method of a particular name is executed). How a pointcut expression matches a join point is at the heart of AOP: Spring uses the AspectJ pointcut syntax by default.

  • Introduction: (also known as an inter-type declaration). Declare additional methods or fields of a certain type. Spring allows the introduction of new interfaces (and a corresponding implementation) to any proxied object. For example, you can use an import to make the bean implement the  IsModified  interface in order to simplify the caching mechanism.

  • Target Object: An object advised by one or more aspects. Some people call it an  advised  object. Since Spring AOP is implemented through runtime proxies, this object is always a  proxied  object.

  • AOP Proxy (AOP Proxy): An object created by the AOP framework to implement the aspect contract (including functions such as informing method execution). In Spring, AOP proxies can be JDK dynamic proxies or CGLIB proxies. Note: With the newly introduced schema-based style and @AspectJ annotation style aspect declarations in Spring 2.0, proxy creation is transparent to users of these styles.

  • Weaving: Connect aspects to other application types or objects and create an advised object. These can be done at compile time (e.g. with the AspectJ compiler), class loading time and runtime. Spring, like other pure Java AOP frameworks, does weaving at runtime.

Type of notification:

  • Before advice : Advice that executes before a join point, but which cannot prevent execution before the join point (unless it throws an exception).

  • After returning advice : Advice that executes after a join point completes normally: for example, a method returns normally without throwing any exceptions.

  • After throwing advice : Advice that executes when a method exits with an exception thrown.

  • After (finally) advice : Advice that is executed when a join point exits (whether it is a normal return or an abnormal exit).

  • Around Advice : Advice that surrounds a join point, such as a method call. This is the most powerful type of notification. Surround advice can be used to accomplish custom behavior before and after method calls. It also chooses whether to continue execution of the join point or simply return their own return value or throw an exception to end execution.

Third, the realization of spring Aop

Spring has the following two options for defining pointcuts and enhancement processing.

     1. Annotation-based "zero configuration" method: use @Aspect, @Pointcut and other Annotations to mark entry points and enhance processing.

  •        2. Management based on XML configuration files: Use Spring configuration files to define entry points and enhancement points.

 

Annotation-based "zero configuration" approach.

(1) First enable Spring's support for @AspectJ aspect configuration.

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xmlns:aop="http://www.springframework.org/schema/aop"        
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
           http://www.springframework.org/schema/aop  
       http://www.springframework.org/schema/beans/spring-aop-3.0.xsd">  
        <!-- Enable support for @AspectJ annotation -->  
        <aop:aspectj-autoproxy/>  
</beans>  

 If you don't plan to use Spring's XML Schema configuration, you should add the following snippet to your Spring configuration file to enable @AspectJ support.

<!-- Enable @AspectJ support -->  
<bean class="org.springframeword.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />  

 

(2), define the aspect Bean.

        When @AspectJ support is enabled, as long as a bean annotated with @Aspect is configured in the Spring container, Spring will automatically recognize the bean and process it as an aspect.

// Define an aspect class using @Aspect  
@Aspect  
public class LogAspect {  
        // define the rest of the class  
        ...  
}

 

(3), define Before enhancement processing.

// define a slice  
@Aspect  
public class BeforeAdviceTest {  
    // Match all methods of all classes under the com.wicresoft.app.service.impl package as entry points  
    @Before("execution(* com.wicresoft.app.service.impl.*.*(..))")  
    public void authorith(){  
        System.out.println("Simulate permission check.");  
    }  
}  

 

上面使用@Before Annotation 时,直接指定了切入点表达式,指定匹配 com.wicresoft.app.service.impl包下所有类的所有方法执行作为切入点。

关于这个表达式的规则如下图。

(4)、定义 AfterReturning 增强处理。

// 定义一个切面  
@Aspect  
public class AfterReturningAdviceTest {  
    // 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点  
    @AfterReturning(returning="rvt", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")  
    public void log(Object rvt) {  
        System.out.println("模拟目标方法返回值:" + rvt);  
        System.out.println("模拟记录日志功能...");  
    }  
}  

 

(5)、定义 AfterThrowing 增强处理。

// 定义一个切面  
@Aspect  
public class AfterThrowingAdviceTest {  
    // 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点  
    @AfterThrowing(throwing="ex", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")  
    public void doRecoverActions(Throwable ex) {  
        System.out.println("目标方法中抛出的异常:" + ex);  
        System.out.println("模拟抛出异常后的增强处理...");  
    }  
}  

 

 

(6)、定义 After 增强处理。

 

After 增强处理与AfterReturning 增强处理有点相似,但也有区别:

 

  • AfterReturning 增强处理处理只有在目标方法成功完成后才会被织入。
  • After 增强处理不管目标方法如何结束(保存成功完成和遇到异常中止两种情况),它都会被织入。
  • // 定义一个切面  
    @Aspect  
    public class AfterAdviceTest {  
        // 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点  
        @After("execution(* com.wicresoft.app.service.impl.*.*(..))")  
        public void release() {  
            System.out.println("模拟方法结束后的释放资源...");  
        }  
    } 
     

    (7)、Around 增强处理

    Around 增强处理近似等于 Before 增强处理和  AfterReturning 增强处理的总和。它可改变执行目标方法的参数值,也可改变目标方法之后的返回值。

  • // 定义一个切面  
    @Aspect  
    public class AroundAdviceTest {  
        // 匹配 com.wicresoft.app.service.impl 包下所有类的所有方法作为切入点  
        @Around("execution(* com.wicresoft.app.service.impl.*.*(..))")  
        public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable {  
            System.out.println("执行目标方法之前,模拟开始事物...");  
            // 执行目标方法,并保存目标方法执行后的返回值  
            Object rvt = jp.proceed(new String[]{"被改变的参数"});  
            System.out.println("执行目标方法之前,模拟结束事物...");  
            return rvt + "新增的内容";  
        }  
    }  
     

    (8)、访问目标方法的参数。

     

    访问目标方法最简单的做法是定义增强处理方法时将第一个参数定义为 JoinPoint 类型,当该增强处理方法被调用时,该 JoinPoint 参数就代表了织入增强处理的连接点。JoinPoint 里包含了如下几个常用方法。

     

    • Object[] getArgs(): 返回执行目标方法时的参数。
    • Signature getSignature(): 返回被增强的方法的相关信息。
    • Object getTarget(): 返回被织入增强处理的目标对象。
    • Object getThis(): 返回 AOP 框架为目标对象生成的代理对象。

     

    提示当时使用 Around 处理时,我们需要将第一个参数定义为 ProceedingJoinPoint 类型,该类型是 JoinPoint 类型的子类

     

    (9)、定义切入点。

    所谓切入点,其实质就是为一个切入点表达式起一个名称,从而允许在多个增强处理中重用该名称。

    Spring 切入点定义包含两个部分:

     

    • 一个切入点表达式。
    • 一个包含名字和任意参数的方法签名。
    • // 使用@Pointcut Annotation 时指定切入点表达式  
      @pointcut("execution * transfer(..)")  
      // 使用一个返回值为void,方法体为空的方法来命名切入点  
      private void anyOldTransfer(){}  
        
      // 使用上面定义的切入点  
      @AfterReturning(pointcut="anyOldTransfer()", returning="reVal")  
      public void writeLog(String msg, Object reVal){  
          ...  
      }  
       
    • 2、基于 XML 配置文件的管理方式。

    • 不配置切入点
    • <?xml version="1.0" encoding="UTF-8"?>  
      <beans xmlns="http://www.springframework.org/schema/beans"  
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
             xmlns:aop="http://www.springframework.org/schema/aop"        
             xsi:schemaLocation="http://www.springframework.org/schema/beans  
                 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                 http://www.springframework.org/schema/aop  
             http://www.springframework.org/schema/beans/spring-aop-3.0.xsd">  
              <aop:config>  
                  <!-- 将 fourAdviceBean 转换成切面 Bean, 切面 Bean 的新名称为:fourAdviceAspect,指定该切面的优先级为2 -->  
                  <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2">  
                      <!-- 定义个After增强处理,直接指定切入点表达式,以切面 Bean 中的 Release() 方法作为增强处理方法 -->  
                      <aop:after pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" />  
                        
                      <!-- 定义个Before增强处理,直接指定切入点表达式,以切面 Bean 中的 authority() 方法作为增强处理方法 -->  
                      <aop:before pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="authority" />  
                        
                      <!-- 定义个AfterReturning增强处理,直接指定切入点表达式,以切面 Bean 中的 log() 方法作为增强处理方法 -->  
                      <aop:after-returning pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="log" />  
                        
                      <!-- 定义个Around增强处理,直接指定切入点表达式,以切面 Bean 中的 processTx() 方法作为增强处理方法 -->  
                      <aop:around pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="processTx" />  
                        
                  </aop:aspect>  
              </aop:config>  
                
              <!-- 省略各个Bean 的配置 -->  
              <!-- ... -->  
                
      </beans> 
       
    • 配置切入点
    • <?xml version="1.0" encoding="UTF-8"?>  
      <beans xmlns="http://www.springframework.org/schema/beans"  
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
             xmlns:aop="http://www.springframework.org/schema/aop"        
             xsi:schemaLocation="http://www.springframework.org/schema/beans  
                 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                 http://www.springframework.org/schema/aop  
             http://www.springframework.org/schema/beans/spring-aop-3.0.xsd">  
              <aop:config>  
                  <!-- 定义一个切入点,myPointcut,直接知道它对应的切入点表达式 -->  
                  <aop:pointcut id="myPointcut" expression="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" />  
                  <aop:aspect id="afterThrowingAdviceAspect" ref="afterThrowingAdviceBean" order="1">  
                      <!-- 使用上面定于切入点定义增强处理 -->  
                      <!-- 定义一个AfterThrowing 增强处理,指定切入点以切面 Bean 中的 doRecovertyActions() 方法作为增强处理方法 -->  
                      <aop:after-throwing pointcut-ref="myPointcut" method="doRecovertyActions" throwing="ex" />  
                  </aop:aspect>  
              </aop:config>  
                
              <!-- 省略各个Bean 的配置 -->  
              <!-- ... -->  
                
      </beans>  
       

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327069077&siteId=291194637