欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring基本用法7——AOP的支持(二) 博客分类: Spring SpringAOP面向切面Web 

程序员文章站 2024-02-05 17:41:10
...

       前言:AOP,也就是面向切面编程,作为面向切面编程的一种补充,已经比较成熟,如果是OOP是从静态角度考虑程序结构,那么AOP则是从动态角度考虑程序运行。本文旨在介绍Spring对AOP的支持,简述其用法。

本篇文章重点关注以下问题:

  • SpringAOP访问目标方法的参数
  • 增强处理的执行顺序

1. SpringAOP访问目标方法的参数

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

  • Object[] getArgs:返回目标方法的参数
  • Signature getSignature:返回目标方法的签名
  • Object getTarget:返回被织入增强处理的目标对象
  • Object getThis:返回AOP框架为目标对象生成的代理对象
提示:当使用@Around处理时,我们需要将第一个参数定义为ProceedingJoinPoint类型,该类是JoinPoint的子类。

2. 输出DEMO

          测试项目目录如下图:

Spring基本用法7——AOP的支持(二)
            
    
    博客分类: Spring SpringAOP面向切面Web 

2.1 准备XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-4.3.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
    
    <!-- 指定自动搜索Bean组件、自动搜索切面类 -->
    <context:component-scan base-package="com.wj.chapter6.aop2">
        <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
    </context:component-scan>
    
    <!-- 启动@AspectJ支持 -->
    <aop:aspectj-autoproxy/>
    
</beans>

       说明:

2.2 编写切面类       

       下面的切面类中定义了Before、Around、AfterReturning和After 4中增强处理,并分别在4种增强处理中访问被织入增强处理的目标方法、目标方法的参数和被织入增强处理的目标对象等:

package com.wj.chapter6.aop2.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 定义一个切面,用于测试对目标方法参数的访问
 */
@Aspect
public class FourAdviceTest {
    
    @Around("execution(* com.wj.chapter6.aop2.service.*.*(..))")
    public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable {
        System.out.println("【Around增强】执行目标方法之前,模拟开始事务...");
        // 访问执行目标方法的参数
        Object[] args = jp.getArgs();
        // 当执行目标方法的参数存在,
        // 且第一个参数是字符串参数
        if (args != null && args.length > 0
            && args[0] instanceof String) {
            // 修改目标方法调用参数的第一个参数
            args[0] = "【增加的前缀】" + args[0];
        }
        //执行目标方法,并保存目标方法执行后的返回值
        Object rvt = jp.proceed(args);
        System.out.println("【Around增强】执行目标方法之后,模拟结束事务...");
        // 如果rvt的类型是Integer,将rvt改为它的平方
        if(rvt != null && rvt instanceof Integer)
            rvt = (Integer)rvt * (Integer)rvt;
        return rvt;
    }
    
    @Before("execution(* com.wj.chapter6.aop2.service.*.*(..))")
    public void authority(JoinPoint jp) {
        System.out.println("【Before增强】模拟执行权限检查");
        // 返回被织入增强处理的目标方法
        System.out.println("【Before增强】被织入增强处理的目标方法为:" + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("【Before增强】目标方法的参数为:" + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("【Before增强】被织入增强处理的目标对象为:" + jp.getTarget());
    }
    
    @AfterReturning(pointcut="execution(* com.wj.chapter6.aop2.service.*.*(..))", returning="rvt")
    public void log(JoinPoint jp , Object rvt) {
        System.out.println("【AfterReturning增强】获取目标方法返回值:" + rvt);
        System.out.println("【AfterReturning增强】模拟记录日志功能...");
        // 返回被织入增强处理的目标方法
        System.out.println("【AfterReturning增强】被织入增强处理的目标方法为:" + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("【AfterReturning增强】目标方法的参数为:" + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("【AfterReturning增强】被织入增强处理的目标对象为:" + jp.getTarget());
    }

    // 定义After增强处理
    @After("execution(* com.wj.chapter6.aop2.service.*.*(..))")
    public void release(JoinPoint jp) {
        System.out.println("【After增强】模拟方法结束后的释放资源...");
        // 返回被织入增强处理的目标方法
        System.out.println("【After增强】被织入增强处理的目标方法为:" + jp.getSignature().getName());
        // 访问执行目标方法的参数
        System.out.println("【After增强】目标方法的参数为:" + Arrays.toString(jp.getArgs()));
        // 访问被增强处理的目标对象
        System.out.println("【After增强】被织入增强处理的目标对象为:" + jp.getTarget());
    }
}

         从上面的代码可以看出,在Before、Around、AfterReturning、After四种增强处理中,其实都可以通过相同的代码访问被增强的对象、目标方法和方法的参数。但只有Around增强处理可以改变方法的参数和返回值。

2.3 编写业务类代码

       接口+实现,业务相关接口就不上代码了,可以通过附件查看,下面是业务实现类:

package com.wj.chapter6.aop2.service;

import org.springframework.stereotype.Component;

@Component("addService")
public class AddService implements IAddService {
    
    @Override
    public boolean addUser(String name , String pass){
        System.out.println("【AddService.addUser】添加用户:" + name);
        return true;
    }
    
    @Override
    public void addProduct(String name) {
        System.out.println("【AddService.addProduct】添加商品:" + name);
    }

    @Override
    public void addException() {
        throw new NullPointerException();
    }
}

 2.4 编写测试代码、查看运行结果

package com.wj.chapter6.aop2;

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

import com.wj.chapter6.aop2.service.IAddService;

public class BeanTest {
    // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息
    private static final String PATH_XML = "com/wj/chapter6/aop2/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);
        addService.addUser("熊燕子", "123456");
    }
}

         测试结果为:

Spring基本用法7——AOP的支持(二)
            
    
    博客分类: Spring SpringAOP面向切面Web 

3. 访问目标方法参数的简单方式

        我们可以在程序中使用args来绑定目标方法的参数。如果在一个 args表达式中指定了一个或多个参数,该切入点将只匹配具有对应形参的方法,且目标方法的参数值将被传入增强处理方法。下面辅以例子说明:

package com.wj.chapter6.aop3.aspect;

import java.util.Date;

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

@Aspect
public class AccessArgAspect {
    // 下面的args(arg0,arg1)会限制目标方法必须有2个形参
    @AfterReturning(returning="rvt" , pointcut="execution(* com.wj.chapter6.aop3.service.*.*(..)) && args(date,name,pass)")
    // 此处指定date为Date类型,name、pass为String类型
    // 则args(date,name,pass)还要求目标方法的第一个参数为Date类型,后两个形参都是String类型
    public void access(Object rvt, Date date, String name , String pass) {
        System.out.println("调用目标方法第1个参数为:" + date);
        System.out.println("调用目标方法第2个参数为:" + name);
        System.out.println("调用目标方法第3个参数为:" + pass);
        System.out.println("获取目标方法返回值:" + rvt);
        System.out.println("模拟记录日志功能...");
    }
}

       上面的程序中,定义pointcut时,表达式中增加了args(date,name,pass)部分,意味着可以在增强处理方法(access方法)中定义date,name,pass三个属性——这三个形参的类型可以随意指定,但一旦指定了这三个参数的类型,则这三个形参类型将用于限制该切入点只匹配第一个参数类型为Date,第二、三个参数类型为String的方法(方法参数个数和类型若有不同均不匹配)。

       args表达式有如下两个作用:

  • 提供了一种简单的方式来访问目标方法的参数

  • 可用于对切入点表达式作额外的限制

      测试结果为:
Spring基本用法7——AOP的支持(二)
            
    
    博客分类: Spring SpringAOP面向切面Web 

        测试代码见附件。

4. 织入增强处理的顺序

        Spring AOP和AspectJ一样,采用有限顺序来织入增强处理:

  • 在“进入”连接点时,最高优先级的增强处理将先被织入(所以给定的两个Before增强处理 中,优先级高的那个会先执行);
  • 在“退出”连接点时,最高优先级的增强处理会最后被织入(所以给定的两个After增强处理中,优先级高的那个会后执行);
  • 当不同的切面中的多个增强处理需要在同一个连接点被织入时,Spring AOP将以随机的顺序来织入这些增强处理。
       如果应用需要指定不同切面类里的增强处理的优先级,Spring提供了如下两种解决方案:
  • 让切面类实现org.springframework.core.Ordered接口:实现该接口只需要实现一个int getOrder()方法,该方法返回值越小,优先级越高;
  • 直接使用@Order注解来修饰一个切面类:使用这个注解时可以配置一个int类型的value属性,该属性值越小,优先级越高;(个人感觉更方便)

代码链接:http://pan.baidu.com/s/1hsafhPi 密码:d5z1

  • Spring基本用法7——AOP的支持(二)
            
    
    博客分类: Spring SpringAOP面向切面Web 
  • 大小: 20.5 KB
  • Spring基本用法7——AOP的支持(二)
            
    
    博客分类: Spring SpringAOP面向切面Web 
  • 大小: 53.1 KB
  • Spring基本用法7——AOP的支持(二)
            
    
    博客分类: Spring SpringAOP面向切面Web 
  • 大小: 6.9 KB