Spring AOP 应用模型
一、概述
AOP是Aspect-oriented programming,中文翻译为面向切面编程。
面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足。
Spring的一个关键的组件就是 AOP框架。 尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善。
Spring中所使用的AOP:
-
提供声明式企业服务,特别是为了替代EJB声明式服务。 最重要的服务是 声明性事务管理(declarative transaction management) , 这个服务建立在Spring的抽象事务管理(transaction abstraction)之上。
-
允许用户实现自定义的切面,用AOP来完善OOP的使用。
这样你可以把Spring AOP看作是对Spring的一种增强,它使得Spring可以不需要EJB就能提供声明式事务管理; 或者也可以使用Spring AOP框架的全部功能来实现自定义的切面。
二、AOP术语
首先让我们从定义一些重要的AOP概念开始。这些术语不是Spring特有的。 不幸的是,Spring术语并不是特别的直观;如果Spring使用自己的术语,将会变得更加令人困惑。
-
切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
-
连接点(Joinpoint): 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。
-
通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。
-
切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
-
引入(Introduction): (也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。 例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
-
目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
-
AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。
-
织入(Weaving): 把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
通知的类型:
-
前置通知(Before advice): 在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
-
返回后通知(After returning advice): 在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
-
抛出异常后通知(After throwing advice): 在方法抛出异常退出时执行的通知。
-
后通知(After (finally) advice): 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
-
环绕通知(Around Advice): 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。
三、给个最简单的Demo看看
/**
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:19:15<br>
* <b>Note</b>: 公共接口,代理类和被代理类都实现此业务接口
*/
public interface IHello {
public void hello(String name);
}
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:19:15<br>
* <b>Note</b>: 公共接口,代理类和被代理类都实现此业务接口
*/
public interface IHello {
public void hello(String name);
}
/**
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:19:47<br>
* <b>Note</b>: 被代理类
*/
public class HelloSpeaker implements IHello {
public void hello(String name) {
System.out.println("Hello, " + name);
}
}
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:19:47<br>
* <b>Note</b>: 被代理类
*/
public class HelloSpeaker implements IHello {
public void hello(String name) {
System.out.println("Hello, " + name);
}
}
/**
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:44:39<br>
* <b>Note</b>: 测试类,客户端
*/
public class SpringAOPDemo {
public static void main(String args[]){
// ApplicationContext context = BeanContextHelper.getApplicationContext();
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IHello helloproxy = (IHello)context.getBean("helloProxy");
helloproxy.hello("lavasoft");
}
}
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:44:39<br>
* <b>Note</b>: 测试类,客户端
*/
public class SpringAOPDemo {
public static void main(String args[]){
// ApplicationContext context = BeanContextHelper.getApplicationContext();
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
IHello helloproxy = (IHello)context.getBean("helloProxy");
helloproxy.hello("lavasoft");
}
}
/**
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:22:22<br>
* <b>Note</b>: 在被代理方法执行前添加的动作
*/
public class LogBeforeAdvice implements MethodBeforeAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());
public void before(Method method, Object[] objects, Object o) throws Throwable {
logger.log(Level.INFO, "method start..." + method);
}
}
* Created by IntelliJ IDEA.<br>
* <b>User</b>: leizhimin<br>
* <b>Date</b>: 2008-4-21 10:22:22<br>
* <b>Note</b>: 在被代理方法执行前添加的动作
*/
public class LogBeforeAdvice implements MethodBeforeAdvice {
private Logger logger = Logger.getLogger(this.getClass().getName());
public void before(Method method, Object[] objects, Object o) throws Throwable {
logger.log(Level.INFO, "method start..." + method);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="logBeforeAdvice" class="com.lavasoft.springnote.ch02.aoptest.LogBeforeAdvice"/>
<bean id="helloSpeaker" class="com.lavasoft.springnote.ch02.aoptest.HelloSpeaker"/>
<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.lavasoft.springnote.ch02.aoptest.IHello</value>
</property>
<property name="target">
<ref bean="helloSpeaker"/>
</property>
<property name="interceptorNames">
<list>
<value>LogBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="logBeforeAdvice" class="com.lavasoft.springnote.ch02.aoptest.LogBeforeAdvice"/>
<bean id="helloSpeaker" class="com.lavasoft.springnote.ch02.aoptest.HelloSpeaker"/>
<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.lavasoft.springnote.ch02.aoptest.IHello</value>
</property>
<property name="target">
<ref bean="helloSpeaker"/>
</property>
<property name="interceptorNames">
<list>
<value>LogBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
运行结果:
log4j:WARN No appenders could be found for logger (org.springframework.core.CollectionFactory).
log4j:WARN Please initialize the log4j system properly.
2008-4-25 9:25:31 com.lavasoft.springnote.ch02.aoptest.LogBeforeAdvice before
信息: method start...public abstract void com.lavasoft.springnote.ch02.aoptest.IHello.hello(java.lang.String)
Hello, lavasoft
Process finished with exit code 0
log4j:WARN Please initialize the log4j system properly.
2008-4-25 9:25:31 com.lavasoft.springnote.ch02.aoptest.LogBeforeAdvice before
信息: method start...public abstract void com.lavasoft.springnote.ch02.aoptest.IHello.hello(java.lang.String)
Hello, lavasoft
Process finished with exit code 0
四、Advice层次结构
- org.springframework.aop.AfterReturningAdvice
- org.springframework.aop.BeforeAdvice
- org.springframework.aop.MethodBeforeAdvice
- org.springframework.aop.DynamicIntroductionAdvice
- org.springframework.aop.IntroductionInterceptor (also extends org.aopalliance.intercept.MethodInterceptor)
- org.aopalliance.intercept.Interceptor
- org.aopalliance.intercept.MethodInterceptor
- org.springframework.aop.IntroductionInterceptor (also extends org.springframework.aop.DynamicIntroductionAdvice)
- org.aopalliance.intercept.MethodInterceptor
- org.springframework.aop.ThrowsAdvice
Advice 是在切面的某个特定的连接点上执行的动作。
Advice是一个标识接口,有很多的子接口。BeforeAdvice就是在被代理方法调用前所执行的接口动作。
本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/73438