AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的另一个重要特征。AOP把一个业务流程分成几部分,例如权限检查,业务处理,日志记录,每个部分单独处理,然后把它们组装成完整的业务流程。每个部分被称为切面(Aspect)或者关注点。
实例:使用拦截器拦截方法
AOP有一些重要的概念,包括切面,连接点(Joinpoint),通知(Advice),切入点(Pointcut),引用(Introduction)等。这些概念非常抽象,往往很难理解。
抛开这些抽象的概念,先来看一个AOP例子。前面的章节中层介绍了一个AOP的例子。下面看一下稍微复杂的例子。本例中,AOP的规则要稍微复杂一些。
(一) Service接口
Spring推荐使用接口编程。该接口定义了两个方法。下面将使用拦截器其中的withAop()方法,而另一个方法withoutAop()将不会被拦截。接口代码如下:
package com.zhangjie.spring.aop;
public interface IAopService {
//定义两个方法
public void withAop() throws Exception; //将会被拦截
public void withoutAop() throws Exception; //不会被拦截
}
(二) Service实现代码
IaopService实现类中定义了一个name属性,以及对应的getter,setter方法。Name属性值将通过getter,setter方法被Spring容器设置进来。两个接口方法中将输出name属性值。实现类代码如下:
package com.zhangjie.spring.aop;
import javax.security.auth.login.AccountException; public class AopServiceImpl implements IAopService { //Service实现
private String name; //name属性
public void withAop() throws Exception { //withAop()方法实现
System.out.println("有AOP的函数运行。name:"+name);
if(name.trim().length() == 0) //如果name为空
throw new AccountException("name属性不能为空"); //则抛出异常
}
public void withoutAop() throws Exception { //withoutAop()方法实现
System.out.println("没有AOP的函数运行。");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(三) 方法前拦截器检查name是否为null
下面是一个方法前拦截器,实现自MethodBeforeAdvice接口。方法前拦截器在执行指定方法前被调用,参数分别为被调用的方法,执行时被传入的参数,被拦截的Bean。被拦截的执行方法在applicationContext.xml中配置。拦截器代码如下:
package com.zhangjie.spring.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MethodBeforeInterceptor implements MethodBeforeAdvice { //方法前拦截器
//调用对象的方法将执行该方法。参数分别为被调用的方法,被调用方法的参数,对象。
public void before(Method method, Object[] args, Object instance)
throws Throwable {
System.out.println("即将要执行方法:"+method.getName());
if(instance instanceof AopServiceImpl){ //如果是service
String name = ((AopServiceImpl)instance).getName(); //获取name
if(name == null)
throw new NullPointerException("name属性不能为null"); //抛出异常
}
}
}
(四) 返回后拦截器输出返回值
返回后拦截器,实现自AfterReturningAdvice接口。返回后拦截器在执行完指定方法,并返回之后被调用。如果有返回值,可以取到返回值,否则为null。参数分别为方法返回值,被调用的方法,执行时被传入的参数,被拦截的Bean,代码如下:
package com.zhangjie.spring.aop;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MethodAfterInterceptor implements AfterReturningAdvice{
public void afterReturning(Object value, Method method, Object[] args, Object instance) throws Throwable {//方法后拦截器
System.out.println("方法 "+method.getName()+"运行完毕,返回值为:"+value);
}
}
(五) 异常拦截器捕获异常
异常抛出拦截器,实现自ThrowsAdvice接口。如果指定的方法中有异常被抛出,则其被调用。与上两个拦截器接口不同,ThrowsAdvice接口没有定义任何方法,因此不需要实现任何方法。但是如果定义了形如
afterThrowing([Method],[args],[target],subclassOfThrowable)
的方法,这些方法将会调用。参数分别为:被调用的方法,方法的参数,被拦截的Bean以及抛出的异常类。这里面只有最后一个参数是必须的。Spring之所以这样设计,是为了使开发者可以灵活地定义多个捕捉不同的异常,代码如下:
package com.zhangjie.spring.aop;
import java.lang.reflect.Method;
import javax.security.auth.login.AccountException;
import org.springframework.aop.ThrowsAdvice;
public class ThrowsInterceptor implements ThrowsAdvice { //异常拦截器
public void afterThrowing(Method method,Object[] args,Object instance,AccountException ex) throws Throwable{ //方法一
System.out.println("方法"+method.getName()+"抛出了异常:"+ex); //方法及异常
}
public void afterThrowing(NullPointerException ex) throws Throwable { //方法二
System.out.println("抛出了异常:"+ex);
}
}