AOP
Yinzhi Geng
AOP应用
主讲人:Alex Zheng
内容提要
1 3
Before通知 Throws通知 After returning通知 切入点
2
3 4
AOP介绍
? 面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对 象编程(OOP). 面向对象将应用程序分解成 各个层次的对象,而AOP将 程序分解成各个方面 或者说 关注点 . 这使得可以模块化诸如事务管 理等这些横切多个对象的关注点.(这些关注点术语称作 横切关注点.)
? Spring的一个关键组件就是AOP框架. Spring IoC容器(BeanFactory 和 ApplicationContext)并不依赖于AOP, 这意味着如果你不需要使用,AOP 你可以不用,AOP完善了Spring IoC,使之成为一个有效的中间件解决方 案. ? 简单来说,AOP就是实现”横切”的工具,通过AOP,让我们的设计更加 的模块化,对于那些需要反复进行的处理,可以使用AOP进行”一刀切”
AOP概念
? 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多 个对象.事务管理是J2EE应用中一个很好的横切关注点例子.方面用 Spring的 Advisor或拦截器实现. ? 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调 用或特定的 异常被抛出.
? 通知(Advice): 在特定的连接点,AOP框架执行的动作.各种类 型的通知 包括“around”、“before”和“throws”通知.通知类型将在下面讨论. 许多AOP框架 包括Spring都是以拦截器做通知模型,维护一个“围绕” 连接点的拦截器 链. ? 切入点(Pointcut): 指定一个通知将被引发的一系列连接点 的集 合.AOP框架必须允许开发者指定切入点:例如,使用正则表达式. ? 目标对象(Target Object): 包含连接点的对象.也被称作 被通知或被代 理对象 ? 织入(Weaving): 组装方面来创建一个被通知对象
用ProxyFactoryBean创建AOP代理
? ProxyFactoryBean,和其他Spring的 FactoryBean实现一样,引入一 个间接的层次。如果你定义一个名字为myfactory的ProxyFactoryBean, 引用myfactory的对象所看到的不是ProxyFactoryBean 实例本身,而 是由实现ProxyFactoryBean的类的 getObject()方法所创建的对象
? 通常,我们不需要ProxyFactoryBean的全部功能,因为我们常常只对 一个方面感兴趣: 例如,事务管理。当我们仅仅对一个特定的方面感 兴趣时,我们可以使用许多便利的工厂来创建AOP代理,如: TransactionProxyFactoryBean
Before通知
? Before通知是一种简单的通知类型. 这个通知不需要一个 MethodInvocation对象,因为它只在进入一个方法 前被调用. ? Before通知的主要优点是它不需要调用proceed() 方法, 因此没有无意 中忘掉继续执行拦截器链的可能性. ? Before会在方法调用之前被呼叫,通过MethodBeforeAdvice接口可以实 现Before Advice的逻辑.
目标接口
package org.nitpro.aop.before; public interf
ace ILogin { public void login(); }
目标对象
package org.nitpro.aop.before; public class Login implements ILogin{ public void login() { System.out.println("正在登陆..."); } }
MethodBeforeAdvice定义
package org.nitpro.aop.before; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class LoginBeforeAdvice implements MethodBeforeAdvice{ int count = 0; public void before(Method method, Object[] obj, Object obj2) throws Throwable { ++count; System.out.println(“本站点被访问”+(count-1)+“次,您是第 "+count+"位访问者"); } }
客户端测试
Resource resource = new ClassPathResource("applicationContext.xml"); XmlBeanFactory factory = new XmlBeanFactory(resource); ILogin login = (ILogin)factory.getBean("loginProxy"); login.login(); //第一次调用 login.login(); //第二次调用 login.login(); //第三次调用
文件配置
<bean id="advice" class="org.nitpro.aop.before.LoginBeforeAdvice"> </bean> <bean id="login" class="org.nitpro.aop.before.Login"> </bean> <bean id="loginProxy" class="org.springframework.aop.framework.ProxyFactoryBean" > <property name="proxyInterfaces"> <value>org.nitpro.aop.before.ILogin</value> </property> <property name="target"> <ref bean="login" /> </property> <property name="interceptorNames"> <value>advice</value> </property> </bean>
Throws通知
? 如果连接点抛出异常,Throws通知 在连接点返回后被调用.Spring提供 强类型的throws通知.注意这意味着 org.springframework.aop.ThrowsAdvice接口不包含任何方法: 它是 一个标识接口,标识给定的对象实现了一个或多个强类型的throws通知 方法.这些方法形式 如下: afterThrowing([Method], [args], [target], subclassOfThrowable) ? 只有最后一个参数是必需的. 这样从一个参数到四个参数,依赖于通知 是否对方法和方法 的参数感兴趣
定义通知类
package org.nitpro.aop.throwsadvice; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; public class UserAccountThrowAdvice implements ThrowsAdvice{ public void afterThrowing(Method m, Object[] args, Object target, Throwable ex) throws Throwable { System.out.println("消息:"+ex.getMessage()); } }
文件配置
<bean id="throwadvice" class="org.nitpro.aop.throwsadvice.UserAccountThrowAdvice"> </bean> <bean id="useraccount" class="org.nitpro.aop.throwsadvice.UserAccount"> </bean> <bean id="accountProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.nitpro.aop.throwsadvice.IUserAccount</value> </property> <property name="target"> <ref bean="useraccount" /> </property> <property name="interceptorNames"> <value>throwadvice</value> </property> </bean>
多异常处理
任意个数的throws方法可以被组合在一个类中,如果要在一个类中使用两 个方法来同时处理 RemoteException和ServletException 异常 class CombinedThrowsAdvice implements ThrowsAdvice { public void
afterThrowing(RemoteException ex) throws Throwable { // Do something with remote exception } public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) { // Do something will all arguments } }
After Returning通知
? Spring中的after returning通知必须实现 org.springframework.aop.AfterReturningAdvice 接口 public interface AfterReturningAdvice extends Advice { void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable; } ? After returning通知可以访问返回值(不能改变)、被调用的方法、方法 的参数和 目标对象
定义After Returning通知
package org.nitpro.aop.after; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class RegisterAfterAdvice implements AfterReturningAdvice{ public void afterReturning(Object returnValue, Method method, Object[] args,Object target) throws Throwable { System.out.println("有新会员加入,欢迎 "+returnValue); } }
文件配置
<bean id="regadvice" class="org.nitpro.aop.after.RegisterAfterAdvice"> </bean> <bean id="register" class="org.nitpro.aop.after.Register"> </bean> <bean id="registerProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.nitpro.aop.after.IRegister</value> </property> <property name="target"> <ref bean="register" /> </property> <property name="interceptorNames"> <value>regadvice</value> </property> </bean>
Interception around advice
? Spring使用方法拦截器的around通知是和AOP联盟接口兼容的.实现 around通知的 类需要实现接口MethodInterceptor
使用MethodInterceptor
public class DebugInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("Before: invocation=[" + invocation + "]"); Object returnValue = invocation.proceed(); System.out.println("Invocation returned"); return returnValue; } }
注意MethodInvocation的proceed()方法的调用. Proceed()方法用来执行 目标对象中的方法,proceed()会回传方法执行后的Object执行结果,这个调 用会应用到目标连接点的拦截器链中的每一个拦截器,开发者可以自由决 定调用proceed()方法的时机和位置.大部分拦截器会调用这个方法,并返 回它的返回值
Spring的切入点
? Spring的切入点模型能够使切入点独立于通知类型被重用。 同样的切 入点有可能接受不同的 通知 ? org.springframework.aop.Pointcut 接口是重要的接口, 用来指定通 知到特定的类和方法目标
? PointCut定义了Advice的应用时机.,在Spring中,使用PointcutAdvisor将 Pointcut和Advisor结合为一个对象,Spring内建的Pointcut都有对应的 PointcutAdvisor
名称选择切入点
org.springframework.aop.support.NameMatchMethodPointcutAdvisor 编写Bean定义文件,使用NameMatchMethodPointcutAdvisor将Pointcut和 Advisor结合起来
目标对象接
口
package org.nitpro.aop.pointcut; public interface IUserDao { public void updateUser(); public void deleteUser(); public void findUserList(); }
目标对象
package org.nitpro.aop.pointcut; public class UserDaoImpl implements IUserDao{ public void deleteUser() { System.out.println("正在删除..."); } public void findUserList() { System.out.println("正在查询..."); } public void updateUser() { System.out.println("正在更新..."); } }
定义通知
package org.nitpro.aop.pointcut; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class UserDaoImplAdvice implements AfterReturningAdvice{ public void afterReturning(Object returnValue, Method method, Object[] args,Object target) throws Throwable { System.out.println(method.getName()+"方法被截取 "); } }
文件配置
<bean id="userdaoimplAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcut Advisor"> <property name="mappedNames"> <list> <value>updateUser</value> <value>delete*</value> </list> </property> <property name="advice"> <ref bean="userdaoimpladvice" /> </property> </bean>
文件配置
<bean id="userdaoimplProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.nitpro.aop.pointcut.IUserDao</value> </property> <property name="target"> <ref bean="userdaoimpl" /> </property> <property name="interceptorNames"> <value>userdaoimplAdvisor</value> </property> </bean>
方法映射
userdaoimplAdvisor映射了多个文件名称,包括updateUser方法和所有以 delete方法开头的方法. 如果名称映射只有一项;则可以写成如下方式: <property name="mappedName"> <value>delete*</value> </property>
正则表达式切入点
Spring提供的 org.springframework.aop.support.RegexpMethodPointcutAdvisor可 以让您使用Regular expression来定义Pointcuts,它是Spring中静态 Pointcut的实例,在符合Regular expression的情况下使用advice,你可以使 用以下符号: . + * \ 符合任何一个单字符 符合前一个字符出现一次或多次 符合前一个字符零次或多次 转义字符
使用RegexpMethodPointcutAdvisor
<bean id="userdaoimplAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>org\.nitpro\.aop\.pointcut\.IUserDao\.updateUser.*</value> <value>org\.nitpro\.aop\.pointcut\.IUserDao\.delete.*</value> </list> </property> <property name="advice"> <ref bean="userdaoimpladvice" /> </property> </bean>
Introduction
Spring将introduction通知看作一种特殊类型的拦截通知. Introduction需要实现IntroductionAdvisor, 和IntroductionInterceptor 接口: public interface IntroductionInterceptor extends MethodInterceptor { boolean implementsInterface(Class intf); }
实现IntroductionInterceptor
//这里必须实现两个接口 public class Other implements IOther,IntroductionInterceptor{ public void doOther() { System.out.println("执行doOhter..."); } public
Object invoke(MethodInvocation invoc) throws Throwable { Method method = invoc.getMethod(); Class clz = method.getDeclaringClass(); boolean flag = this.implementsInterface(clz); if(flag){ return invoc.getMethod().invoke(this,invoc.getArguments()); } return invoc.proceed(); } //是否实现了某个接口中的方法 public boolean implementsInterface(Class clz) { return clz.isAssignableFrom(IOther.class); }
将Introduction织入到some对象
<bean id="otherAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor"> <constructor-arg index="0" ref="other"></constructor-arg> <constructor-arg index="1" value="org.nitpro.aop.introduction.IOther"></constructor-arg> </bean> <bean id="someProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.nitpro.aop.introduction.ISome</value> </property> <property name="target"> <ref bean="some" /> </property> <property name="interceptorNames"> <value>otherAdvisor</value> </property> </bean>
DelegatingIntroductionInterceptor
? DelegatingIntroductionInterceptor类是Spring AOP中为 IntroductionAdvisor接口所提供的实现类,您可以直接继承本类,添加你 希望为目标对象增加的行为. ? DelegatingIntroductionInterceptor的设计是将导入 委托到真正实现 导入接口的接口,隐藏完成这些工作的拦截器
DelegatingIntroductionInterceptor示例
public class Other extends DelegatingIntroductionInterceptor implements IOther{ public void doOther() { System.out.println("执行doOhter..."); } public Object invoke(MethodInvocation invoc) throws Throwable { //直接使用继承来的父类实现,改动只此一处,其他文件无需任 //何改动 return super.invoke(invoc); }
}
DelegatingIntroductionInterceptor的比较
? 改造后的Other类不再需要实现IntroductionInterceptor类,至于如何判 断调用了新增加的方法,如果调用该方法等等事宜,不再劳您大驾,委派 给DelegatingIntroductionInterceptor
自动代理
? 在应用较小时,只有很少类需要被通知的时,ProxyFactoryBean 可以 很好的工作。当有许多类需要通知的时,显示的创建每个代理就显得 很繁琐。幸运的是Spring提供了是使用自动通过容器来创建代理。这 时,就只需要配置一个Bean来做繁琐的工作
? Spring提供了两个类实现自动代理:BeanNameAutoProxyCreator和 DefaultAdvisorAutoProxyCreator。BeanNameAutoProxyCreator为匹 配名字的Bean产生代理,它可以使用在将一个或者多个方面应用在命 名相似的Bean中
目标对象接口
package org.nitpro.aop.before; public interface ILogin { public void login(); }
目标对象
package org.nitpro.aop.before; public class Login implements ILogin{ public void login() { System.out.println("正在登陆..."); } }
目标对象通知
package org.nitpro.aop.before; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class Login
BeforeAdvice implements MethodBeforeAdvice{ int count = 0; public void before(Method method, Object[] obj, Object obj2) throws Throwable { ++count; System.out.println(“本站点被访问”+(count-1)+“次,您是第 "+count+"位访问者"); } }
使用BeanNameAutoProxyCreator
<bean id="advice" class="org.nitpro.aop.before.LoginBeforeAdvice"> </bean> <bean id="loginService" class="org.nitpro.aop.before.Login"></bean> <bean id="autoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAut oProxyCreator"> <property name="beanNames"> <list> <value>*Service</value> </list> </property> <property name="interceptorNames"> <value>advice</value> </property> </bean>
使用BeanNameAutoProxyCreator注意
? 1. 加载Spring时,使用到了ApplicationContext,而没有使用BeanFactory ? 2.查询的服务为loginService,而非autoProxy
使用DefaultAdvisorAutoProxyCreator
? DefaultAdvisorAutoProxyCreator是一个更通用,更强大的自动代理生 成器。它将 自动应用于当前上下文的符合条件的advisor,而不需要 在自动代理advisor的bean定义中包含特定的bean名字。 它有助于配 置的一致性,并避免象BeanNameAutoProxyCreator一样重复配置
? 使用这个机制包括: ? 指定一个DefaultAdvisorAutoProxyCreator的bean定义 ? 在相同或相关上下文中指定任何数目的Advisor
使用DefaultAdvisorAutoProxyCreator
? DefaultAdvisorAutoProxyCreator会自动计算每个advisor包含的的切入 点,看看 是否有什么通知应该被引用到每个业务对象(比如例子中的 “businessObject1”和“businessObject2”) ? 这意味着任何数目的advisor都可以自动应用到每个业务对象。如果 advisor中没有任何切入点符合业务对象的 方法,这个对象就不会被 代理。因为会为新的业务对象添加bean定义,如果必要,它们会自动 被代理 ? DefaultAdvisorAutoProxyCreator 和BeanNameAutoProxyCreator不同 的是,DefaultAdvisorAutoProxyCreator只和Advisor 匹配
文件配置
<bean id="userdaoimplAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcut Advisor"> <property name="mappedName"> <value>delete*</value> </property> <property name="advice"> <ref bean="userdaoimpladvice" /> </property> </bean> <bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisor AutoProxyCreator"> </bean>
使用DefaultAdvisorAutoProxyCreator注意
? 1.DefaultAdvisorAutoProxyCreator只和Advisor 匹配, 而不仅仅是拦截 器或其它通知 ? 2.使用ApplicationContext加载.而不能使用BeanFactory方式.
谢谢