`

Spring基本用法7——AOP的支持(一)

阅读更多

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

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

  • AOP的使用场景
  • AOP的基本概念
  • Spring对AOP的支持

1. AOP的使用场景

         AOP 广泛应用于处理一些具有横切性质的系统级服务,何为横切性质?可以把它理解为各业务流程共有的模块,如下图所示:

       日志服务、安全服务就属于横切性质的系统级服务,此服务为各业务流程都需要的服务。常规以OOP方式讲,需要实现上图的功能,各业务流程需要三段完全相似的方法段,如下图所示:

        很明显,此方法较low,若果哪天需要切换安全策略,或是修改日志记录细节,就需要维护多处代码(成千上百处),这是非常令人沮丧的事。AOP就是为弥补OOP处理这些问题而生的,如下图所示:

        通过抽象分析出各模块、各方法的交叉关注点,将其抽离出来在一处处理,这种方式无疑非常优雅,维护起来也很方便。

2. AOP基本概念

        AOP从程序运行角度考虑程序的流程,提取业务处理过程的切面。Aop面向的是程序运行中的各个步骤,希望以更好的方式来组合业务处理的各个步骤。先来简单介绍下关于面向切面编程的一些术语:

  • 切面(Aspect):切面用于组织多个Advice,Advice放在切面中定义;
  • 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或异常的抛出。在SpringAOP中,连接点总是方法的调用;
  • 增强处理(Advice):AOP框架在特定的切入点执行的增强处理。如“around”、“before”等
  • 切入点(Pointcut):可以插入增加处理的连接点。简言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。
  • 引入:将方法或字段添加到被处理的类中。(比如,可以使用一个引入,是任何对象实现isModified接口)
  • 目标对象:被AOP框架进行增强处理的对象,也被称为被增强对象。
  • AOP代理:AOP框架创建的对象,简单的说,代理就是对目标对象的加强。
  • 织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象(AOP代理)的过程。
        总结AOP的任务:AOP框架动态生成一个AOP代理对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异——AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。运行原理如下图所示:

3. Spring对AOP的支持

       Spring中的AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。(也支持cglib)

       通过分析上面AOP相关概念,不难发现,如果不深入研究AOP实现原理,只是单纯使用AOP进行开发是件很简单的事情。纵观AOP编程,其中需要程序员参与的只有三部分:

  1. 定义普通业业务组件
  2. 定义切入点,一个切入点可能横切多个业务组件
  3. 定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。
       第一点是正常开发的业务组件,无需额外说明。AOP编程的关键就是定义切入点和定义增强处理。一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理。AOP代理的方法可描述为:
       AOP代理的方法 = 目标对象的方法 + 增强处理

3.1 准备配置环境

       测试项目目录如下图:

3.1.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.aop">
        <context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
    </context:component-scan>
    
    <!-- 启动@AspectJ支持 -->
    <aop:aspectj-autoproxy/>
    
</beans>

     说明:

3.1.2 准备业务环境

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

     AddService:

package com.wj.chapter6.aop.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();
    }
}

      DeleteService

package com.wj.chapter6.aop.service;

import org.springframework.stereotype.Component;

@Component("deleteService")
public class DeleteService implements IDeleteService {

    @Override
    public void deleteProduct(String name) {
        System.out.println("【DeleteService.deleteProduct】删除商品:" + name);
    }

    @Override
    public boolean deleteUser(String name) {
        System.out.println("【DeleteService.deleteUser】删除用户:" + name);
        return true;
    }
}

     TxService

package com.wj.chapter6.aop.service;

import org.springframework.stereotype.Component;

/**
 * 测试Around增强, 为当前加法运算添加事物
 */
@Component
public class TxService implements ITxService {

    @Override
    public int add(int arg1, int arg2) {
        int result = arg1 + arg2;
        System.out.println("【TxService.add】加法运算结果应该是:" + result);
        return result;
    }

}

3.2 before增强处理

        before增强处理:在目标方法(Target)执行之前织入增强处理(Advice)。在一个切面类里使用@Before来修饰一个方法时,该方法将作为Before增强处理。使用@Before修饰时,通常需要指定一个value属性值,作为切入点表达式,用于指定该增强处理将被织入哪些切入点。下面java类中定义了一个before增强处理:

package com.wj.chapter6.aop.aspect;

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

/**
 * 定义一个切面,验证before增强
 */
@Aspect
public class AuthAspect {
    // 匹配com.wj.chapter6.aop.service包下所有类的、
    // 所有方法的执行作为切入点
    @Before("execution(* com.wj.chapter6.aop.service.*.*(..))")
    public void authority() {
        System.out.println("【before增强】模拟执行权限检查");
    }
}

       @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

   

 

  • 大小: 45.2 KB
  • 大小: 15.5 KB
  • 大小: 13.7 KB
  • 大小: 13.9 KB
  • 大小: 21.6 KB
  • 大小: 89.3 KB
分享到:
评论

相关推荐

    Spring 入门案例——AOP

    本入门案例将帮助你理解并掌握Spring AOP的基本概念和使用方法。 在Spring AOP中,我们首先需要了解的是"切面"(Aspect)的概念。切面是封装了横切关注点的代码模块,它可以包含通知(Advice)、切点(Pointcut)和...

    五、Spring源码分析——Spring Aop

    Spring支持三种织入时机:编译时(使用AspectJ编译器)、加载时(使用AspectJ LTW)和运行时(Spring容器初始化时)。 7. **源码分析**: 在Spring AOP的源码中,`org.springframework.aop.framework....

    Spring——aop

    下面我们将深入探讨Spring AOP的相关概念和使用方法。 ### 1. AOP的基本概念 - **切面(Aspect)**:切面是关注点的模块化,它结合了通知(Advice)和切点(Pointcut)定义。 - **通知(Advice)**:在特定连接点...

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...

    spring-springMVC开发文档和AOP详解

    本压缩包文件主要涵盖了Spring框架的核心部分——Spring MVC和AOP(面向切面编程)的详细知识,对于想要深入理解和运用Spring框架的开发者来说,是一份非常宝贵的资源。 首先,我们来看看《Spring3.20权威开发指南...

    struts2+spring集成bug——使用AOP时可能遇到的问题分析

    比如,可能在定义AOP通知(advises)或切点(pointcuts)时,使用了错误的表达式或者没有正确地指定目标方法。 2. **类加载问题**:由于Struts2和Spring使用不同的类加载机制,可能会导致类找不到或者重复加载的...

    spring之AOP(动态代理)

    在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点分离,将横切关注点(如日志、事务管理、权限检查等)与核心业务逻辑解耦。AOP的核心概念是切面、通知、连接点、切入点和织入。在...

    spring-aop-jar

    "spring-aop-jar"这个主题涉及到Spring框架中的核心组件之一——Spring AOP。这里我们将深入探讨Spring AOP、相关jar文件以及它们在实际开发中的作用。 首先,我们来看一下提供的文件: 1. aopalliance.jar:这是一...

    spring aop依赖jar包

    现在,我们回到主题——"springaop依赖的jar包"。在Spring 2.5.6版本中,使用Spring AOP通常需要以下核心jar包: - `spring-aop.jar`:这是Spring AOP的核心库,包含了AOP相关的类和接口。 - `spring-beans.jar`:...

    征服Spring AOP—— Schema

    本文将深入探讨“Spring AOP——Schema”,这是Spring AOP的一种配置方式,通过XML schema定义切面和通知。 首先,我们需要理解AOP的基本概念。面向切面编程是一种编程范式,旨在提高软件的模块化程度,将关注点...

    java课件-14-Spring-AOP

    Spring容器简介——AOP AOP核心概念 Spring AOP的使用 Spring AOP常见用法 示例代码

    spring security 2.0 的简单配置使用(补)——用aop控制method级权限

    在本文中,我们将深入探讨如何在Spring Security 2.0版本中配置并使用AOP(面向切面编程)来实现方法级别的权限控制。首先,我们需要理解Spring Security的基础概念。 1. **Spring Security基本架构** Spring ...

    征服Spring AOP—— @AspectJ

    在IT行业中,Spring框架是Java企业级应用开发的首选,而Spring AOP(面向切面编程)则是其核心特性之一,用于实现横切关注点的模块化,如日志、事务管理等。@AspectJ是Spring AOP的一种注解驱动方式,它极大地简化了...

    7Spring AOP盗梦空间之二——获得返回值AfterReturnning

    在本篇博文中,我们将深入探讨Spring AOP(面向切面编程)中的一个重要概念——AfterReturning通知,这是Spring框架提供的一种强大的功能,允许我们在方法成功执行并返回后执行额外的操作。我们将通过源码分析和实际...

    spring3.0.5的aop使用

    首先,我们来理解Spring AOP的基本概念。在Spring中,切面由通知(advises)和切点(pointcuts)组成。通知是在特定的程序执行点执行的代码,例如方法调用前或后。切点则是定义这些执行点的规则,通常基于方法签名或...

    spring-aop.jar

    《深入解析Spring AOP框架——以spring-aop.jar为例》 Spring AOP,全称Aspect Oriented Programming(面向切面编程),是Spring框架的核心部分之一,它为Java开发者提供了强大的面向切面的编程能力。本文将围绕...

    SpringAOP测试Demo

    Spring AOP,全称Aspect Oriented Programming(面向切面编程),是Spring框架的重要组成部分,它为应用程序提供了一种模块化和声明式的方式来处理交叉关注点,如日志、事务管理、性能监控等。通过AOP,我们可以将...

    Spring3.1AOP简单例子

    5. **启用AOP**:为了使AOP生效,我们需要在Spring配置中启用AOP支持,这可以通过在XML配置中添加`&lt;aop:aspectj-autoproxy&gt;` 或者在Java配置中使用`@EnableAspectJAutoProxy` 注解来实现。 在"Spring3.1.0Demo2-AOP...

    springAOP-dome

    在传统编程中,这些关注点通常被分散到各个类的方法中,而AOP则允许我们把这些关注点抽取出来,作为一个独立的模块——切面(Aspect)来处理。 在Spring框架中,AOP通过代理模式实现。有两种类型的代理:JDK动态...

Global site tag (gtag.js) - Google Analytics