AOP(Aspect Oriented Programming,面向切面编程)的主要目的是针对业务处理过程中的切面进行提取,所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。可以通过预编译方式和运行时动态代理,实现在不修改源代码的请你工况下,给程序动态添加功能的一种技术。在OOP中模块化的关键单元是类,而在AOP中模块化的单元是切面。切面能对关注点进行模块化,例如横切多个类型和对象的事务管理。在AIP属于中通常成为横切关注点。
1、AOP基本概念术语。切面、通知、切入点、引入、代理、织入。
切面,是指需要实现的交叉功能。是应用系统模块化的一个俄切面或领域。切面最常见的例子是日志记录。一个系统到处都需要日志记录,利用切面就能不侵入的情况下实现该功能。其实切面就是包含定义切面行为的通知和定义切面在什么地方织入的切入点组成。
通知,就是切面的具体实现,通知将应用系统新的行为加入到系统中。比如在日志例子中,日志通知包含了实现实际日志功能的代码,例如想日志文件写入日志,这个具体的行为就是通知。
切入点,定义了通知应该应用在那些地方,通知可以应用到系统的任何一个地方,如果想要过滤一些不想通知的地方,就要用到切入点,用来定义切面在什么地方切入。
引入,允许为已存在类添加新的方法和属性。例如,可以常见一个稽查通知来记录对象的最后修改时间。只要用一个方法setLastModified记忆一个保存状态的变量。可以在不改变已存在类的情况下将这个方法与变量引用,给他们新的行为和封装。即可以让已存在的类实现一个类,并实现其中的方法,从而用新的行为。
代理,将通知应用到目标对象后创建的对象,对和客户而言,目标对象(应用AOP之前的对象)和代理对象(应用AOP之后的对象)是一样的。就是代理真实主题,系统其他部分不用为了代理对象而改变。
织入,就是i将切面应用到目标对象从而创建一个新的代理对象的过程。
切面在指定连接点织入到目标对象中。
2、通知Advice。Spring可以在多个地方织入通知,所以有多种类型。BeforeAdvice前置通知,AfterReturningAdivce后置通知,AfterThrowingAdvice异常通知,AroundAdice环绕通知,IntroductionAdvice引入通知。前置通知接口的一个实现:MethodBeforeAdvice,其中有个方法如下:
void before(Method method,Object [] args, Object target);
代理工厂的配置代码如下:
<bean id="reception" class="org.framework.aop.framework.ProxyFactoryBean" >
<property name="proxyInterfaces" value="before.Reception" />
<property name="interceptorNames" value="greeting" />
<property name="target" ref="target /">
</bean>
后置通知和前置通知差不多一样的配置和是吸纳方法。
环绕通知比较特殊,有两个特殊的地方,1)该接口MethodInterceptor 能控制目标方法是否真被调用,可以通过MethodIncovation.proceed()方法来调用目标方法,这一点不同于MethodBeforeAdvice,目标方法总是被调用,除非抛出异常。2)MethodInterceptor可以控制返回的对象,也就是说可以返回一个与proceed()方法返回的对象完全不同的对象。而是用AfterReturningAdvice则不能返回一个不同的对象。
异常通知ThrowableAdivice是用来处理在方法调用过程中产生的错误或异常。
引入通知IntroductionInterceptor,可以进行混合业务逻辑,可以在原有的类的基础上进行扩展。例如在原有的People类中引入记录执行时间的功能。代码如下:
public class ExecuteTimeImpl implements InterductionInterceptor, ExecuteTimeInterface{
public boolean implementsInterface(Class in) {//用于实现制定的接口,实现则返回true
return in.isAssignableFrom(ExecuTimeInterface.class);
}
public Object invoke(MethodInvocation m) throws Throwable {
if(implementsInterface(m.getMethod().getDeclaringClass()))
return m.getMethod().invoke(this, m.getArguments());//调用引用的方法
else
return m.proceed();//委托其他方法
}
public void printExecuteTime() {
System.out.println("时间:"+new SimpleDateFormate("yyyy-MM-dd").format(new Date));
}
}
这样就在原来的基础上加入了记录时间的方法,对原有的类没进行修改,不造成影响。
3、切入点Pointcut。切入点可以确定在什么地方将通知织入到目标类中。Spring通过org.springframework.aop.Pointcut接口描述切点,该接口由ClassFilter和MethodMatcher构成。即Pointcut有两个方法:getClassFilter(), getMethodMatcher()。分别进行类过滤和方法过滤。类过滤:ClassFiler接口仅有一个方法Boolean matcher(Class clazz);用于过滤类,匹配过滤条件则返回true。方法过滤:MethodMatcher接口有三个方法,Boolean isRuntime(),判断MethodMatcher的类型是静态的还是动态的。Boolean matches(Method method, class targetClass); 用来过滤方法。
4、切面Advisor。切面是由通知和切点组成的。切面分为一般切面,切点切面和引入切面,较常用的是切点切面。静态切入点的实现类的一个例子如下所示:
public class PeopleAdvisor extends StaticMethodMatcherPointcutAdvisor{
public boolean matches(Method method, Class clazz) {
return "speaking".equals(method.getName);
}
public ClassFilter getClassFilter() {
return new ClassFilter(){
return new ClassFilter() {
public boolean matche(Class clazz){
return People.class.isAssignableFrom(clazz);//用于类过滤,判断是不是People即他的子类
}
};
}
}
配置如下:
<bean id="people" class="org.framework.aop.framework.ProxyFactoryBean" >
<property name="interceptorNames"><idref local="peopleAdvisor" /> </peoperty><!--定义的是切面而不是通知-->
<property name="target" ref="target /">
</bean>