Spring Basic Usage 7 - AOP Support (1)

        Foreword: AOP, that is, aspect-oriented programming, as a supplement to aspect-oriented programming, has been relatively mature. If OOP considers program structure from a static perspective, then AOP considers program operation from a dynamic perspective. This article aims to introduce Spring's support for AOP and briefly describe its usage.

This article focuses on the following issues:

  • AOP usage scenarios
  • Basic Concepts of AOP
  • Spring's support for AOP

1. AOP usage scenarios

         AOP is widely used to deal with some system-level services with cross-cutting nature. What is cross-cutting nature? It can be understood as a module common to all business processes, as shown in the following figure:

       Log service and security service are system-level services of cross-cutting nature, which are required by each business process. Generally speaking, in the OOP way, the functions in the above figure need to be realized, and each business process needs three completely similar method segments, as shown in the following figure:

        Obviously, this method is relatively low. If one day you need to switch the security policy or modify the logging details, you need to maintain multiple pieces of code (thousands of places), which is very frustrating. AOP was born to make up for OOP to deal with these problems, as shown in the following figure:

       By abstractly analyzing the cross concerns of various modules and methods, and extracting them for processing in one place, this method is undoubtedly very elegant and easy to maintain.

2. Basic concepts of AOP

        AOP considers the flow of the program from the perspective of program operation, and extracts aspects of the business processing process. Aop is oriented to the various steps in the operation of the program, hoping to combine the various steps of business processing in a better way. Let's briefly introduce some terms about aspect-oriented programming:

  • Aspect : Aspect is used to organize multiple Advice, and Advice is defined in the aspect;
  • Joinpoint : A clear point in the execution of a program, such as a method call or an exception thrown. In Spring AOP, a join point is always a method call;
  • Enhancement processing (Advice) : The AOP framework performs enhancement processing at a specific pointcut. Such as "around", "before", etc.
  • Pointcut : You can insert join points that increase processing. In short, when a join point meets the specified requirements, the join point will be augmented, and the join point becomes a pointcut.
  • Introduce : Add a method or field to the class being processed. (For example, an import can be used, which is any object that implements the isModified interface)
  • Target object : The object that is enhanced by the AOP framework, also known as the enhanced object.
  • AOP proxy : An object created by the AOP framework. Simply put, a proxy is an enhancement to the target object.
  • Weaving : The process of adding enhancement processing to a target object and creating an enhanced object (AOP proxy).
         To summarize the tasks of AOP: The AOP framework dynamically generates an AOP proxy object that can be used as a target object. The AOP proxy contains all the methods of the target object, but there are differences between the methods in the AOP proxy and the methods of the target object-AOP methods add enhanced processing at specific pointcuts and call back the methods of the target object. The operation principle is shown in the following figure:

3. Spring's support for AOP

       The AOP proxy in Spring is generated and managed by Spring's IOC container, and its dependencies are also managed by the IOC container. Therefore, AOP proxies can directly use other bean instances in the container as targets, and this relationship can be provided by the dependency injection of the IOC container. Spring uses Java dynamic proxies by default to create AOP proxies, so that proxies can be created for any interface instance. (also supports cglib)

       By analyzing the above AOP-related concepts, it is not difficult to find that it is very simple to simply use AOP for development without in-depth study of the AOP implementation principle. Throughout AOP programming, there are only three parts that require programmers to participate:

  1. Define common business components
  2. Define pointcuts, one pointcut may cut across multiple business components
  3. Define enhanced processing. Enhanced processing is the processing action woven into the AOP framework for common business components.
        The first point is a business component that is normally developed and needs no additional explanation. The key to AOP programming is to define pointcuts and define enhancements. Once the appropriate pointcuts and enhancements are defined, the AOP framework will automatically generate AOP proxies. The AOP proxy method can be described as:  AOP proxy method = target object method + enhanced processing 3.1 Prepare the configuration environment      

3.2 before enhancement processing

       before Enhancement processing: Weaving enhanced processing (Advice) before the target method (Target) is executed. When using @Before to decorate a method in an aspect class, the method will be enhanced as Before. When decorating with @Before, you usually need to specify a value attribute value as a pointcut expression to specify which pointcuts the enhancement processing will be woven into. A before enhancement process is defined in the following java class:

package com.wj.chapter6.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * Define a facet, verify before enhancement
 */
@Aspect
public class AuthAspect {
    // Match all classes under the com.wj.chapter6.aop.service package,
    // execution of all methods as entry points
    @Before("execution(* com.wj.chapter6.aop.service.*.*(..))")
    public void authority() {
        System.out.println("[before enhancement] Simulate execution permission check");
    }
}

       @Aspect修饰了AuthAspect类,表面这个类是一个切面类,在切面类中定义了一个AuthAspect方法,这个方法本来并不特殊之处,但因使用了@Before来标注,这就将该方法换成了一个before增强处理。

      @Before注解直接指定了切入点表达式,指定匹配wj.chapter6.aop.service包下的所有类的所有方法的执行作为切入点。测试代码最后给出。

3.3 AfterReturning增强处理

        AfterReturning增强处理:增强处理将在目标方法正常完成后被织入。其可指定两个参数:

  • pointcut/value:指定该切入点表达式(pointcut会覆盖value属性值)
  • returning:该属性指定一个形参名,用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法的返回值。同时,在Advice方法中定义该形参时指定类型,会限制目标方法返回指定类型的值或没有返回值
       下面定义一个AfterReturning增强处理:
package com.wj.chapter6.aop.aspect;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

/**
 * 定义一个切面,验证@AfterReturning增强
 */
@Aspect
public class LogAspect {
    // 匹配com.wj.chapter6.aop.service包下所有类的、
    // 所有方法的执行作为切入点
    @AfterReturning(returning="rvt" , pointcut="execution(* com.wj.chapter6.aop.service.*.*(..))")
    // 声明rvt时指定的类型会限制目标方法必须返回指定类型的值或没有返回值
    // 此处将rvt的类型声明为Object,意味着对目标方法的返回值不加限制
    public void log(Object rvt) {
        System.out.println("【AfterReturning增强】模拟记录日志功能, 获取目标方法返回值:" + rvt);
    }
}
        使用@AfterReturning注解时,指定了一个returning属性,该属性值为rvt,这表面允许在Advice方法(log())中定义名为rvt的形参,程序可通过rvt形参来访问目标方法的返回值。(虽然可以访问到返回值,但是不能改变返回值)

3.4 After增强处理

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

  • AfterReturning增强处理只有在目标方法成功完成后才会被织入;
  • After增强处理不管目标方法如何结束(成功完成或是异常终止),它都会被织入
       下面定义一个AfterReturning增强处理:
package com.wj.chapter6.aop.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

/**
 * 定义一个切面,验证After增强
 */
@Aspect
public class ReleaseAspect {
    
    @After("execution(* com.wj.chapter6.aop.service.*.*(..))")
    public void release() {
        System.out.println("【After增强】模拟方法结束后的释放资源...");
    }
}
      其实,After增强处理的作用非常类似于异常处理中的finally块的作用——无论如何,它总会在方法执行结束之后被织入,因此特别适合用于资源回收。

3.5 Around增强处理

        Around增强处理:功能较为强大,近似等于Before增强和AfterReturning增强处理的总和,Around增强处理既可在执行目标方法之前织入增强动作,也可在执行目标方法之后织入增强动作。此外,Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至完全阻止目标方法的执行。Around增强处理也可以改变目标方法的参数值,也可以改变执行目标方法之后的返回值。(功能虽强大,但必须在线程安全的环境下使用)

      下面定义一个Around增强处理:

package com.wj.chapter6.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

/**
 * 定义一个切面,验证Around增强
 */
@Aspect
public class TxAspect {
    // 匹配com.wj.chapter6.aop.service包下TxService类的所有方法的执行作为切入点
    @Around("execution(* com.wj.chapter6.aop.service.TxService.*(..))")
    public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable {
        System.out.println("【Around增强】执行目标方法之前,模拟开始事务...");
        Object rvt = jp.proceed(jp.getArgs());
        System.out.println("【Around增强】执行目标方法之后,模拟结束事务...");
        return rvt;
    }
}

        与其他增强处理不同的是,该方法的第一个形参必须是ProceedingJoinPoint类型(至少包含一个形参),在增强处理方法体类,调用ProceedingJoinPoint参数的proceed()方法才会执行目标方法——这就是Around增强处理可以完全控制目标方法的执行时机、如何执行的关键。

       调用ProceedingJoinPoint参数的proceed方法时,还可以传入一个Object[]对象作为参数,该数组中的值将被 传入目标方法作为执行方法的参数。

3.6 AfterThrowinf增强处理

        AfterThrowing增强处理:主要用于处理程序中未处理的异常。使用@AfterThrowing注解时可置顶如下两个常用属性:

  • pointcut/value:指定该切入点表达式(pointcut会覆盖value属性值)
  • returning:该属性指定一个形参名,用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。同时,在Advice方法中定义该形参时指定类型,会限制目标方法必须抛出指定类型的异常。
       下面定义一个AfterThrowing增强处理:
package com.wj.chapter6.aop.aspect;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

/**
 * 定义一个切面,验证AfterThrowing增强
 */
@Aspect
public class RepairExAspect {
    // 匹配com.wj.chapter6.aop.service包下所有类的、
    // 以Exception结尾的方法的作为切入点
    @AfterThrowing(throwing="ex" , pointcut="execution(* com.wj.chapter6.aop.service.*.*Exception(..))")
    // 声明ex时指定的类型会限制目标方法必须抛出指定类型的异常
    // 此处将ex的类型声明为Throwable,意味着对目标方法抛出的异常不加限制
    public void doRecoveryActions(Throwable ex) {
        System.out.println("【AfterThrowing增强】模拟Advice对异常的修复, 目标方法中抛出的异常:" + ex);
    }
}
        需要说明的是,AOP的AfterThrowing增强处理虽然可以对目标方法的异常进行处理,但这种异常处理与直接使用catch捕获不同——catch捕捉意味着完全处理该异常,如果catch块中没有重新抛出新异常,则该方法可以正常结束;而AfterThrowing处理虽然处理了该异常,但它不能完全处理该异常,该异常依然会传播到上一级调用者。

3.7 测试代码与结果

package com.wj.chapter6.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.wj.chapter6.aop.service.IAddService;
import com.wj.chapter6.aop.service.IDeleteService;
import com.wj.chapter6.aop.service.ITxService;

public class BeanTest {
    // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息
    private static final String PATH_XML = "com/wj/chapter6/aop/applicationContext.xml";
    
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        // 2.根据xml配置文件,创建Spring IOC容器的上下文
        ApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML);
        
        IAddService    addService    = ctx.getBean("addService" , IAddService.class);
        IDeleteService deleteService = ctx.getBean("deleteService" , IDeleteService.class);
        ITxService     txService     = ctx.getBean("txService" , ITxService.class);
        
        addService.addUser("熊燕子", "123456");
        System.out.println("---------------------- 方法调用分割线. --------------------  ");
        
        addService.addProduct("小苹果");
        System.out.println("---------------------- 方法调用分割线. --------------------  ");
        
        deleteService.deleteUser("熊燕子");
        System.out.println("---------------------- 方法调用分割线. --------------------  ");
        
        try {
            addService.addException();
        } catch (NullPointerException e) {
            /* 捕获抛出的空指针异常. */
            /* AfterThrowing处理虽然处理了该异常,但它不能完全处理该异常,该异常依然会传播到上一级调用者. */
        }
        System.out.println("---------------------- 方法调用分割线. --------------------  ");
        
        txService.add(3, 6);
    }
}

       测试结果为


 

       除此以外,SpringAOP还可以在切面中访问目标方法的参数,具体实现下一篇博文给出实现。

       代码下载地址:链接:http://pan.baidu.com/s/1miNvWy0 密码:w75k

   

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326301759&siteId=291194637