1、AOP中的术语
l 切面(Aspect): 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
l 连接点(Joinpoint): 在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。 在Spring AOP中,一个连接点 总是 代表一个方法的执行。 通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。
l 通知(Advice): 在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。
通知的类型:
ü 前置通知(Before advice): 在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
ü 返回后通知(After returning advice): 在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
ü 抛出异常后通知(After throwing advice): 在方法抛出异常退出时执行的通知。
ü 后通知(After (finally) advice): 当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
ü 环绕通知(Around Advice): 包围一个连接点(join point)的通知,如方法调 用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
l 切入点(Pointcut): 匹配连接点(Joinpoint)的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。 切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。
l 引入(Introduction): (也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。 Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。
l 目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
l AOP代理(AOP Proxy): AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。 注意:Spring 2.0最新引入的基于模式(schema-based)风格和@AspectJ注解风格的切面声明,对于使用这些风格的用户来说,代理的创建是透明的。
l 织入(Weaving): 把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象。 这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
2、实现切面的两种方式:
l 使用通用类(基于模式的风格)
切面:
public class ConfigSecurityHandler {
private void checkSecurity(Object retValue){
User user = (User)retValue;
System.out.println("-----checkSecurity------- "+user.getBirth());
}
}
目标对象:
public interface UserManager {
public void addUser(String username,Date birth);
public User findUser(int id);
}
Spring xml文件中的配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="userManager" class="com.taobao.spring.manager.impl.UserManagerImpl"/>
<bean id="configSecurityHandler" class="com.taobao.spring.util.ConfigSecurityHandler"/>
<aop:config>
<aop:aspect id="config" ref="configSecurityHandler">
<aop:pointcut id="find" expression="execution(* com.taobao.spring.manager.*.find*(..))"/>
<aop:after-returning pointcut-ref="find" method="checkSecurity" returning="retValue"/>
</aop:aspect>
</aop:config>
l 使用普通类中以 @Aspect 注解(@AspectJ风格)
切面:
@Aspect
public class AnnotationSecurityHandler {
@Pointcut("execution(* com.taobao.spring.manager.*.add*(..))")
private void pointCuts(){};
@Before("pointCuts()")
private void checkSecurity(){
System.out.println("------checkSecurity()--------");
}
}
目标对象:同上
Spring xml文件中的配置:
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean id="userManager" class="com.taobao.spring.manager.impl.UserManagerImpl"/>
<bean id="annotationSecurityHandler" class="com.taobao.spring.util.AnnotationSecurityHandler"/>
3、Spring的AOP代理
Spring缺省使用J2SE 动态代理(dynamic proxies)来作为AOP的代理。这样任何接口都可以被代理。
Spring也支持使用CGLIB代理. 对于需要代理类而不是代理接口的时候CGLIB代理是很有必要的。 如果一个业务对象并没有实现一个接口,默认就会使用CGLIB。 作为面向接口编程的最佳实践,业务对象通常都会实现一个或多个接口。但也有可能会强制使用CGLIB, 在这种情况(希望不常有)下,你可能需要通知一个没有在接口中声明的方法,或者需要传入一个代理对象给方法作为具体类型。
强制使用CGLIB的方法:
1) 当需要使用CGLIB代理和@AspectJ自动代理支持,设置 <aop:aspectj-autoproxy>
的 proxy-target-class
属性为true:<aop:aspectj-autoproxy proxy-target-class="true"/>
2)强制使用CGLIB代理需要将 <aop:config>
的 proxy-target-class
属性设为true:<aop:config proxy-target-class="true">
4、Spring实现声明式事务
l 事务代理上调用方法的工作过程如下:
l 配置:
1) 配置dataSource
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
2) 配置事务管理器
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
3) 配置事务传播特性
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
4) 配置哪些类的哪些方法参与事务
<aop:config>
<aop:advisor pointcut="execution(* com.taobao.spring.manager.*.*(..))" advice-ref="txAdvice"/>
</aop:config>