首页 > 代码库 > spring AOP和通知

spring AOP和通知

1.  spring的通知

1.1.  AOP的概念

切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于注解的方式来实现。

连接点(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框架一样,在运行时完成织入。

1.2.  通知

通知类型

接口

描述

Around

org.aopalliance.intercept.

MethodInterceptor

拦截对目标方法调用

Before

Org.springframework.aop.

MethodBeforeAdvice

在目标方法调用前调用

After

Org.springframework.aop.

AfterReturningAdvice

在目标方法调用后调用

Throws

Org.springframework.aop.

ThrowsAdvice

当目标方法抛出异常时调用

1.2.1.  前置通知

Org.springframework.aop.MethodBeforeAdvice接口的代码如下:

public abstract interface MethodBeforeAdviceextends BeforeAdvice {

         public abstract void before(Method paramMethod,

                            Object[] paramArrayOfObject, ObjectparamObject) throws Throwable;

}

注意返回值的类型是void。前置通知可以在连接点执行之前插入自定义行为,但是不能修改连接点的返回值。如果一个前置通知抛出异常,这将中止拦截器链的进一步执行。异常将沿着拦截器链向回传播。如果异常是非强制检查的(unchecked)或者已经被包含在被调用方法的throws声明中,它将被直接返回给客户端;否则它将由AOP代理包装在一个非强制检查异常中返回。

1.2.2.  后置通知

Org.springframework.aop.AfterReturningAdvice接口的代码如下:

public abstract interface AfterReturningAdvice extends AfterAdvice {

         public abstract void afterReturning(Object paramObject1,

                            Method paramMethod, Object[] paramArrayOfObject, Object paramObject2)

                            throws Throwable;

}

后置通知可以访问返回值(但不能进行修改),被调用方法,方法参数以及目标对象。

1.2.3.  环绕通知

public abstract interface MethodInterceptor extends Callback {

         public abstract Object intercept(Object paramObject, Method paramMethod,

                            Object[] paramArrayOfObject, MethodProxy paramMethodProxy)

                            throws Throwable;

}

invoke()方法的MethodInvocation参数暴露了被调用的方法,目标连接点,AOP代理以及传递给方法的参数。invoke()方法应该返回调用的结果:即连接点的返回值。注意对MethodInvocation中proceed()方法的调用。这个方法继续运行指向连接点的拦截器链并返回proceed()的结果。大多数拦截器会调用这个方法,返回一个值。然而,一个类似任意环绕通知的MethodInterceptor,可以返回一个不同的值或者抛出一个异常而不是调用proceed方法。

1.2.4.  异常通知

如果连接点抛出异常,异常通知(throwsadvice)将在连接点返回后被调用。 Spring提供类型检查的异常通知(typed throws advice),这意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法: 它只是一个标记接口用来标识所给对象实现了一个或者多个针对特定类型的异常通知方法。这些方法应当满足下面的格式:

afterThrowing([Method, args, target], subclassOfThrowable)

只有最后一个参数是必须的。根据异常通知方法对方法及参数的需求,方法的签名可以有一个或者四个参数。下面是一个异常通知的例子。

当一个RemoteException(包括它的子类)被抛出时,下面的通知会被调用:

public class RemoteThrowsAdvice implements ThrowsAdvice {

 

    public void afterThrowing(RemoteException ex) throws Throwable {

                // Do something with remote exception

    }

            }

1.2.5.  通知使用实例

1)        使用新建eclipse新建一个java web工程,引入spring jar包

2)        新建一个实现了前置通知接口的类BeforeAdvice类

package com.morris.spring.aop;

import java.lang.reflect.Method;

import java.util.Arrays;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice{

 

         public void before(Method paramMethod, Object[] paramArrayOfObject,

                            Object paramObject) throws Throwable {

 

                   System.out.println("。。。。前置通知开始。。。。");

                   System.out.println("要执行的方法:"+paramMethod.getName());

                   System.out.println("参数:"+Arrays.toString(paramArrayOfObject));

                   System.out.println("目标对象:"+paramObject);

                   System.out.println("。。。。前置通知结束。。。。");

 

         }

 

}

3)        新建一个实现了后置通知接口的类AfterAdvice

package com.morris.spring.aop;

import java.lang.reflect.Method;

import java.util.Arrays;

import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice{

 

         public void afterReturning(Object paramObject1, Method paramMethod,

                            Object[] paramArrayOfObject, Object paramObject2) throws Throwable {

                  

                   System.out.println("后置通知开始。。。。");

                   System.out.println("返回值:"+paramObject1);

                   System.out.println("执行方法:"+paramMethod.getName());

                   System.out.println("参数:"+Arrays.toString(paramArrayOfObject));

                   System.out.println("目标对象:"+paramObject2);

                   System.out.println("后置通知结束。。。。");

 

         }

 

}

4)        新建一个实现了环绕通知接口的类RoundAdvice

 

package com.morris.spring.aop;

 

import java.lang.reflect.Method;

import java.util.Arrays;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

public class RoundAdvice implements MethodInterceptor {

 

         public Object invoke(MethodInvocation invocation) throws Throwable {

                   System.out.println("--环绕通知开始------");

                   Method method = invocation.getMethod();

                   System.out.println("执行的方法:" + method.getName());

                   Object[] parameters = invocation.getArguments();

                   System.out.println("参数:" + Arrays.toString(parameters));

                   Object obj = invocation.proceed();

                   System.out.println("--环绕通知结束-----");

                   return obj;

 

         }

}

5)        新建目标对象所需实现的接口IHello

package com.morris.spring.aop;

public interface IHello {

         String sayHello(String name);

}

6)        新建一个目标对象HelloImpl

package com.morris.spring.aop;

public class HelloImpl implements IHello {

         public String sayHello(String name) {

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

                   return "Hello " + name;

         }

}

7)        编写spring的配置文件

 

<?xml version="1.0" encoding="UTF-8"?>

<!-- - Middle tier application context definition for the image database. -->

<beans xmlns="http://www.springframework.org/schema/beans"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"

         xmlns:tx="http://www.springframework.org/schema/tx"

         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

                                     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

                                     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

         <!-- 前置对象 -->

         <bean id="beforeAdvice" class="com.morris.spring.aop.BeforeAdvice"></bean>

         <!-- 后置对象 -->

         <bean id="afterAdvice" class="com.morris.spring.aop.AfterAdvice"></bean>

         <!-- 环绕通知 -->

         <bean id="roundAdvice" class="com.morris.spring.aop.RoundAdvice"></bean>

         <!-- 目标对象 -->

         <bean id="hello" class="com.morris.spring.aop.HelloImpl"></bean>

         <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">

                   <!-- 引用目标对象 -->

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

                   <!-- 要代理的接口,目标对象实现的接口 -->

                   <property name="proxyInterfaces">

                            <list>

                                     <value>com.morris.spring.aop.IHello</value>

                            </list>

                   </property>

                   <!-- 拦截器,所有的通知 -->

                   <property name="interceptorNames">

                            <list>

                                     <value>beforeAdvice</value>

                                     <value>afterAdvice</value>

                                     <value>roundAdvice</value>

                            </list>

                   </property>

 

         </bean>

</beans>

8)        编写测试类Test

package com.morris.spring.aop;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

         public static void main(String[] args) {

                   // 加载spring配置文件

                   ApplicationContext applicationContext = new ClassPathXmlApplicationContext(

                                     "spring.xml");

                   // 获得目标对象

                   IHello hello = (IHello) applicationContext.getBean("proxy");

                   // 执行目标对象方法

                   hello.sayHello("morris");

         }

}

运行结果如下:

。。。。前置通知开始。。。。

要执行的方法:sayHello

参数:[morris]

目标对象:com.morris.spring.aop.HelloImpl@1bb7be

。。。。前置通知结束。。。。

--环绕通知开始------

执行的方法:sayHello

参数:[morris]

********执行目标方法***********

--环绕通知结束-----

后置通知开始。。。。

返回值:Hello morris

执行方法:sayHello

参数:[morris]

目标对象:com.morris.spring.aop.HelloImpl@1bb7be

后置通知结束。。。。

 

spring AOP和通知