介绍
这是Spring1.2历史用法,现在(V4.0)仍然支持
这是SpringAOP基础,不得不了解
现在的用法也是基于历史的,只是更简便了
Pointcut
Pointcut作为一个接口,它有几个实现类,这里以其中一个来介绍,他就是NameMatchMethodPointcut。它根据方法名字进行匹配。
它有一个成员变量mappedNames,匹配的方法名集合。
来看一下下面这个配置文件的例子:
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
定义一个bean,class就是刚才提到的。property是里边的属性,里边用一个list,list表示当前这个属性是一个集合,list里边就是value,这里写了一个,也可以写多个,这里的意思是sa开头的所有方法。
然后看一个例子。
首先是配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="bizLogicImplTarget" class="com.imooc.aop.api.BizLogicImpl"></bean>
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
</beans>
这里看到有一个bean,class为BizLogicImpl。另一个bean是定义好了一个切入点。
首先我们有一个BizLogic接口:
public interface BizLogic {
String save();
}
然后是它的一个实现类
public class BizLogicImpl implements BizLogic {
public String save() {
System.out.println("BizLogicImpl : BizLogicImpl save.");
return "BizLogicImpl save.";
// throw new RuntimeException();
}
}
xml文件中配置了这个bean,并且id是bizLogicImplTarget,至于为什么是Target,后边的例子里会有说明。
Before Advice
一个简单的通知类型
只是在进入方法之前被调用,不需要MethodInvocation对象(和基于配置中讲到的是差不多的)
前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
接着刚才的例子,我们新建一个类MoocBeforeAdvice类实现MethodBeforeAdvice接口,里边输出内容即自定义行为:
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("MoocBeforeAdvice : " + method.getName() + " " +
target.getClass().getName());
}
}
这样一个前置通知就定义好了。暂时不去单元测试类执行它,因为执行它需要用到后边讲到的内容。
Throw advice
如果连接点抛出异常,throws advice在连接点返回后被调用
如果throws-advice的方法抛出异常,那么它将覆盖原有异常
接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法:
void afterThrowing([Method,args,target],ThrowableSubclass);
我们来看一下它的说明
这是几种形式,有一个特点就是异常是必须有的,前边的内容可有可无。然后图中代码的例子就是这样。
继续刚才的例子,在代码中定义一个类MoocThrowsAdvice实现接口ThrowsAdvice
public class MoocThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex) throws Throwable {
System.out.println("MoocThrowsAdvice afterThrowing 1");
}
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
System.out.println("MoocThrowsAdvice afterThrowing 2 : " + method.getName() + " " +
target.getClass().getName());
}
}
只是完成了定义,具体使用依赖后边的内容。
After Returning advice
后置通知必须实现org.springframework.aop.AfterReturningAdvice接口
可以访问返回值(但不能进行修改)、被调用的方法、方法的参数和目标
如果抛出异常,将会抛出拦截器链,替代返回值
继续刚才的例子,我们自己定义一个类MoocAfterReturningAdvice继承AfterReturningAdvice接口:
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("MoocAfterReturningAdvice : " + method.getName() + " " +
target.getClass().getName() + " " + returnValue);
}
}
Interception around advice
Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点。这个和之前基于xml配置文件的AOP实现方式类似,切入点可以在外面单独配置,每一个advice都可以使用相同的切入点。
定义一个类MoocMethodInterceptor继承MethodInterceptor接口:
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("MoocMethodInterceptor 1 : " + invocation.getMethod().getName() + " " +
invocation.getStaticPart().getClass().getName());
Object obj = invocation.proceed();
System.out.println("MoocMethodInterceptor 2 : " + obj);
return obj;
}
}
返回的是invocation的proceed方法的返回值。在方法执勤啊和之后都进行输出,我们可以通过invication可以得到method,进一步得到方法的名称。
Introduct advice
和在xml配置中实现的Introduction advice中实现的是一样的
Spring把引入通知作为一种特殊的拦截通知
需要IntroductionAdvisor和IntroductionInterceptor两个接口
仅适用于类,不能和任何切入点一起使用
然后来看一下这两个接口的内容:
IntroductionInterceptor里边有一个implementsInterface,就像我们在前边的配置中配置的那样,使它实现了哪个接口。还有IntroductionAdvisor里的内容。这些都对应了我们基于xml配置的SpringAOP实现里的一些内容。
然后看一下一个Introduction advice的小例子。这个例子实在Spring test suit里边提供的一个例子。用法:
如果调用lock()方法,希望所有的setter方法抛出LockedException异常(如使物体不可变,这是AOP典型例子)
需要一个完成繁重任务的IntroductionInterceptor,这种情况下,可以使用org.springframework.aop.support.DelegatingIntroductionInterceptor,而不是去实现这个接口
public interface Lockable{
void lock();
void unlock();
boolean locked();
}
来看一下代码
接口
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
实现类
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private static final long serialVersionUID = 6943163819932660450L;
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new RuntimeException();
}
return super.invoke(invocation);
}
}
invoke方法里的意思是,如果当前是被锁住,并且方法的名称中包含set,那么不希望执行set方法去改变物体的属性。如果符合这样的内容就抛出一个异常,否则正常执行。
里边做了一点修改,返回的是RuntimeException,而不是LockedException
introduction advisor比较简单,持有独立的LockMixin实例:
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
private static final long serialVersionUID = -171332350782163120L;
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
这里实现的是spring提供的用来简化操作的DefaultIntroductionAdvisor 类。
Advisor API in Spring
Advisor是仅包含一个切入点表达式关联的单个通知的方面
除了introductions,advisors可以用于任何通知
org.springframework,aop.support.DefaultPointcutAdvisor是最常用的advisor类,它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用
它可以混合在Spring同一个AOP代理的advisor和advice