初见Spring之AOP编程
Spring其中一个主要特性就是支持AOP编程,AOP编程可以将多个类或者方法中的重复逻辑抽取出来,提高了代码重用性和可维护性质,AOP编程是对面向对象编程的延伸和补充。本文不介绍AOP编程的概念,主要描述Spring对AOP编程的支持。
Spring对AOP编程提供了两种支持,第一种是Spring AOP也就是Spring自己实现的AOP编程,第二中是AspectJ,Spring的核心库集成了AspectJ,AspectJ也是Spring建议使用的AOP框架,本文主要介绍Spring对AspectJ的支持。
一丶Spring AOP编程基本概念
AOP编程主要涉及如下几个基本的概念:
(1)pointcut(切入点),通俗的来说就是待增强的方法,严格指向待增强方法的位置。
(2)adavice(通知),通知就是增强的方法,对目标对象增强代码的实现都是写在通知中,AspectJ中提供了五种通知:
1.before(前置通知):该通知在切入点之前执行。
2.after-returning(后置通知):该通知在切入点方法执行完之后执行,如果切入点方法抛出了异常,该通知不会执行。
3.around(环绕通知):该通知在切入点方法执行前后执行
4.after-throwing(异常通知):在切入点方法抛出异常之后执行该通知
5.after(最终通知):在切入点方法执行完毕后指向,假如切入点方法抛出了异常该方法照样执行
(3) Aspect(切面):从编程的角度来看,切面就是所有增强方法的集合,在切面里面定义和实现了各种增强方法。
二丶基于XML的AspectJ AOP编程
Spring提供两种方法对AspectJ的支持,第一个便是利用xml配置的方法来完成AspectJ AOP编程,下面举例在说明这个方法。
定义一个Teacher类和一个切面类,Teacher类代表待增强的对象,切面类里面实现由所有的通知,下面来探讨下Spring基于XML的方法如何实现AOP编程。
Teacher类:
public class TeacherDaoImpl implements TeacherDao{
@Override
publicvoid login() {
// TODO Auto-generated method stub
System.out.println("teacher登录完成");
}
}
切面类:
publicclass TeacherAspect{
/**
* @param point 保存有切入点信息
*/
publicvoidbeforeNotification(JoinPoint point){
System.out.println("权限验证");
}
/**
* @param point 保存切入点信息
* @param returnValue 切点点方法的返回值
*/
publicvoid afterNotification(JoinPointpoint,Object returnValue){
System.out.print("登录成功跳转到首页");
}
/**
* @param point 环绕通知对应的特殊切入点,可以调用增强对象的方法
* @throws Throwable 执行切点方法时抛出的异常信息
*/
publicObject AroundrNotification(ProceedingJoinPoint point) throws Throwable{
System.out.println("权限验证");
Object object = point.proceed();
System.out.println("登录成功,跳转到首页");
returnobject;
}
/**
* @param point
* @param e 目标方法抛出异常
*/
publicvoidafterThrowingNotification(JoinPoint point,Throwable e){
System.out.println("登录失败");
}
/**
* @param point 切入点信息
*/
publicvoid finalNotification(JoinPointpoint){
System.out.println("清除登录痕迹");;
}
}
测试代码:
public class MainAspectJ {
publicstatic void main(String [] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
TeacherDao teacherDao = (TeacherDao)context.getBean("teacherDao");
teacherDao.login();
}
}
Spring配置文件:
<!--配置增强对象-->
<bean name="teacherDao" class="AspectJ.TeacherDaoImpl"/>
<!--配置切面 -->
<bean name="teacherAspect"class="AspectJ.TeacherAspect"/>
<!--AspecJ AOP配置核心-->
<aop:config>
<aop:aspect ref="teacherAspect">
<aop:pointcut id="teacherPoint"expression="execution(* AspectJ.*.*(..))"/>
<aop:before method="beforeNotification"pointcut-ref="teacherPoint"/>
<aop:after-returning method="beforeNotification"pointcut-ref="teacherPoint" returning="returnValue"/>
<aop:after-throwing method="afterThrowingNotification" throwing="e"pointcut-ref="teacherPoint"/>
<aop:after method="finalNotification"pointcut-ref="teacherPoint"/>
</aop:aspect>
</aop:config>
图1 前置通知以及后置通知
结合Spring的配置文件和图1的结果可以看到,Spring完成对目标对象的增强,可以看见after通知执行了,after-throwing通知并没有执行,after-throwing通知只有下发生异常的时候执行,after通知无论是否发生异常都执行。
对Teacher类做如下修改来验证上述结论(其他代码内容不变)
public class TeacherDaoImpl implements TeacherDao{
@Override
publicvoid login() {
// TODO Auto-generated method stub
int i=1/0;
System.out.println("teacher登录完成");
}
}
图2 发生异常时候执行情况
从图2的运行结果可以看出来,发生异常的时候,after-throwing通知会执行,但是after-returning通知不会执行,final通知会执行
三丶基于注解的AOP编程
通过上述的流程可以发现基于xml的AOP编程需要编写繁杂的xml文件,所以提供基于注解编写AOP的方法。下面通过一个和上述相似的例子来介绍基于注解的
ManDaoImpl类:被增强对象
@Repository("man")
public class ManDaoImpl implements ManDao{
@Override
publicvoid login() {
// TODO Auto-generated method stub
}
}
ManDaoAspectJ:切面对象
@Aspect
@Component
public class ManDaoAspect {
//定义一个切入点
@Pointcut("execution(* AspectJ.*.*(..))")
privatevoid mepointCut(){}
@Before(value="mepointCut()")
publicvoid befoeNotification(JoinPointpoint){
System.out.println("登录校验");
}
@AfterReturning(value="mepointCut()",returning="result")
publicvoid afterNotification(JoinPointpoint,Object result){
System.out.print("登录成功");
}
@Around(value="mepointCut()")
publicObject roundNotification(ProceedingJoinPoint point) throws Throwable{
System.out.println("权限校验");
Object object = point.proceed();
System.out.println("登录成功");
returnobject;
}
@AfterThrowing(value="mepointCut()",throwing="e")
publicvoid AfterThrowing(JoinPoint point,Throwablee){
System.out.println("登录失败");
}
@After(value="mepointCut()")
publicvoid finalNotification(JoinPointpoint){
System.out.println("清除登录痕迹");
}
}
测试代码对象:
publicclass MainAspectJ {
publicstatic void main(String [] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//TeacherDao teacherDao =(TeacherDao)context.getBean("teacherDao");
//teacherDao.login();
ManDao manDao= (ManDao)context.getBean("man");
manDao.login();
}
}
Spring配置文件写法:
<!-- 指定需要扫描的包,使注解生效 -->
<context:component-scan base-package="AspectJ"/>
<!--AOP注解生效-->
<aop:aspectj-autoproxy/>
测试结果:
图3基于注解的AOP测试结果
从上述这个基于注解配置AOP的例子中,我们可以观察到基于注解配置AOP和基于xml配置AOP在通知配置,切面配置,切入点配置方面是完全是一一对应的,但是基于注解的AOP编程显然比基于XML的AOP编程更为简洁。这里特别需要注意基于注解额AOP编程在配置文件中,必须介绍扫描注解以及配置AOP注解生效。