注册 | 登录 忘记密码? 51cto首页 | 博客 | 论坛 | 招聘
热点文章 我很郁闷,19岁的工程师找..
 帮助

Spring对AOP的支持


2008-07-03 17:13:41
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://tonyaction.blog.51cto.com/227462/85504
 SpringAOP的支持
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 ProxyAOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在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;