Spring对AOP的支持

本文深入介绍了Spring框架中AOP(面向切面编程)的核心概念,包括切面、连接点、通知、切入点等,并通过示例演示了前置通知、后置通知及环绕通知的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  SpringAOP的支持<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

4.1 AOP介绍

首先让我们从一些重要的AOP概念和术语开始。这些术语不是Spring特有的。不过AOP术语并不是特别的直观,如果Spring使用自己的术语,将会变得更加令人困惑。
·         切面(Aspect :一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。

·         连接点(Joinpoint :在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。

·         通知(Advice :在切面的某个特定的连接点上执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

·         切入点(Pointcut :匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

·         引入(Introduction :用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。

·         目标对象(Target Object : 被一个或者多个切面所通知的对象。也被称做被通知(advised对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied对象。

·         AOP 代理(AOP Proxy AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

·         织入(Weaving :把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

通知类型:

  • 前置通知(Before advice:在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。

  • 后置通知(After returning advice:在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

  • 异常通知(After throwing advice:在方法抛出异常退出时执行的通知。

  • 最终通知(After (finally) advice:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

  • 环绕通知(Around Advice:包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

    环绕通知是最常用的通知类型。和AspectJ一样,Spring提供所有类型的通知,我们推荐你使用尽可能简单的通知类型来实现需要的功能。例如,如果你只是需要一个方法的返回值来更新缓存,最好使用后置通知而不是环绕通知,尽管环绕通知也能完成同样的事情。用最合适的通知类型可以使得编程模型变得简单,并且能够避免很多潜在的错误。比如,你不需要在JoinPoint上调用用于环绕通知的proceed()方法,就不会有调用的问题。在Spring 2.0中,所有的通知参数都是静态类型,因此你可以使用合适的类型(例如一个方法执行后的返回值类型)作为通知的参数而不是使用Object数组。 通过切入点匹配连接点的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得通知可以独立对应到面向对象的层次结构中。例如,一个提供声明式事务管理的环绕通知可以被应用到一组横跨多个对象的方法上(例如服务层的所有业务操作)。

    4.2 创建通知

       我们通过一个简单的例子来理解AOP
       代码清单1
public class Foo {

    public void printName(String name){

       System. out .println( "The Name is : " + name);

    }

    public void printAge(String age){

       System. out .println( "The Age is : " + age);

    }

}

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class FooBeforeAdvice implements MethodBeforeAdvice{

    @Override

    public void before(Method method, Object[] args, Object object)

           throws Throwable {

       // 打印出类的名称

       System. out .print( "Class Name is " +

                     object.getClass().getSimpleName() + " " );

       // 打印出参数的值

       System. out .println( "arg is " +(String)args[0] + " " );

    }

}

import org.springframework.aop.framework.ProxyFactory;

public class Test {

    public static void main(String[] args) {

       Foo foo = new Foo();

       FooBeforeAdvice advice = new FooBeforeAdvice();

       //Spring 提供的代理工厂

       ProxyFactory pf = new ProxyFactory();

       // 设置代理目标

       pf.setTarget( foo );

       // 为代理目标添加增强

       pf.addAdvice(advice);

       // 生成代理实例

       Foo proxy = (Foo)pf.getProxy();

       proxy.printName( "Tony" );

       proxy.printAge( "27" );

    }  

}

控制台输出信息

Class Name is Foo arg is Tony

The Name is : Tony

Class Name is Foo arg is 27

The Age is : 27

代码清单 1 中我们的 Foo.java 类有两个方法,我们创建了 FooBeforeAdvice 继承前置增强接口 MethodBeforeAdvice ,在 before 方法中我同通过参数 object 获得代理的对象信息,通过参数 args 获得代理方法的参数值,最后我们通过 ProxyFactory 将目标类和增强类融合,生成了代理实例并调用代理实例的方法。而在 Spring 中又如何配置 AOP 呢?

4.3 前置增强

代码清单 2

< bean id = "foo" class = "com.tony.test.Foo" scope = "singleton" />

< bean id = "fooBeforeAdvice" class = "com.tony.test.FooBeforeAdvice" />

< bean id = "proxy"

       class = "org.springframework.aop.framework.ProxyFactoryBean" >

    < property name = "interceptorNames" > <!-- 指定增强 -->

       < list >

           < value > fooBeforeAdvice </ value >

       </ list >

    </ property >

    <!-- 指定目标代理 Bean -->

    < property name = "target" ref = "foo" />

</ bean >

import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.xml.XmlBeanFactory;

import org.springframework.core.io.ClassPathResource;

public class Test {

    public static void main(String[] args) {

       ClassPathResource resource = new

                  ClassPathResource( "spring-config-beans.xml" );

       // 实例化 BeanFactory

       BeanFactory factory = new XmlBeanFactory(resource);

       Foo foo = (Foo)factory.getBean( "proxy" );

       foo.printName( "Tony" );

       foo.printAge( "27" );

    }  

}

控制台信息

Class Name is Foo arg is Tony

The Name is : Tony

Class Name is Foo arg is 27

The Age is : 27

代码清单 2 中我们只需修改 Spring 的配置文件,将增强类和目标类装配起来就可以了,我们在 Test.java 就像正常的调用 Foo 一样,可是控制台取已经是被拦截了。

4.4 后置增强

代码清单 1

public class Foo {

    public String printName(String name){

       return "The Name is : " + name;

    }

    public String printAge(String age){

       return "The Age is : " + age;

    }

}

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class FooAfterAdvice implements AfterReturningAdvice{

    @Override // 参数分别是代理方法的返回值,被代理的方法,方法的参数,代理对象

    public void afterReturning(Object returnValue, Method method,

                   Object[] args,Object object) throws Throwable {

       // 打印出类的名称

       System. out .print( "Class Name is " +

                         object.getClass().getSimpleName() + " " );

       // 打印出参数的值

       System. out .println( "arg is " +

                          (String)args[0] + " " );

       // 打印出返回值图

       System. out .println( "ReturnValue is " +

                            returnValue.toString());

    }

}

< bean id = "foo" class = "com.tony.test.Foo" scope = "singleton" />

< bean id = "fooAfterAdvice" class = "com.tony.test.FooAfterAdvice" />

< bean id = "proxy"

       class = "org.springframework.aop.framework.ProxyFactoryBean" >

    < property name = "interceptorNames" > <!-- 指定增强 -->

       < list >

           < value > fooAfterAdvice </ value >

       </ list >

    </ property >

    <!-- 指定目标代理 Bean -->

    < property name = "target" ref = "foo" />

</ bean >

控制台输出

Class Name is Foo arg is Tony

ReturnValue is The Name is : Tony

Class Name is Foo arg is 27

ReturnValue is The Age is : 27

代码清单中我们修改了 Foo.java 类两个方法都返回 String 类型的参数,定义了一个 FooAfterAdvice.java 类这个类实现了 AfterReturningAdvice 接口,分别打印出被代理类的名称,方法参数值和返回值。我们查看控制台输出的信息,发现在目标方法执行结束后还打印出增强类输出的信息。

4.5 环绕增强

代码清单 1

public class Foo {

    public void printName(String name){

       System. out .println( "The Name is : " + name);

    }

    public void printAge(String age){

       System. out .println( "The Age is : " + age);

    }

}

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

public class FooInterceptor implements MethodInterceptor{

    @Override

    public Object invoke(MethodInvocation invocation) throws Throwable {

       // 目标方法入参

       Object[] args = invocation.getArguments();

       // 打印方法入参

       System. out .println( " 准备执行目标方法 " );

       // 执行目标方法

       Object obj = invocation.proceed();

       System. out .println( " 目标方法执行完成 " );

       // 返回方法返回值

       return obj;

    }

}

< bean id = "foo" class = "com.tony.test.Foo" scope = "singleton" />

< bean id = "fooInterceptor" class = "com.tony.test.FooInterceptor" />

< bean id = "fooAfterAdvice" class = "com.tony.test.FooAfterAdvice" />

< bean id = "proxy"

       class = "org.springframework.aop.framework.ProxyFactoryBean" >

    < property name = "interceptorNames" > <!-- 指定增强 -->

       < list >

           < value > fooInterceptor </ value >

       </ list >

    </ property >

    <!-- 指定目标代理 Bean -->

    < property name = "target" ref = "foo" />

</ bean >

控制台输出

准备执行目标方法

The Name is : Tony

目标方法执行完成

准备执行目标方法

The Age is : 27

目标方法执行完成

代码清单 1 中我们定义了 FooInterceptor.java 实现 MethodInterceptor 接口对目标方法进行环绕增强,查看控制台我们就能看出 FooInterceptor 在每次方法的执行前后都进行了处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值