Aop编程就是面向编程的羝是切面,而切面是模块化横切关注点。
-切面:横切关注点,被模块化的特殊对象。
-通知:切面必须要完成的工作
-目标:被通知的对象
-代理:向目标对象应用通知之后创建的对象。
-连接点:程序执行的某个特定的位置。
-切点:相当于查询条件
其配置文件如下:
<?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"> <!--加入自动扫描的包--> <context:component-scan base-package="com.sevenhu.AOPTests"></context:component-scan> <!--使AspesctJ的注解起作用--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
声明切面的两种方式:
1.基于XML配置文件的方式:只需要在IOC容器中将切面声明为bean实例,当在Spring IOC容器中初始化AspectJ切面之后,Spring IOC容器会为那些与AspectJ切面相匹配的bean创建代理。
2.基于注解的方式:在AspectJ注解中,切面只是一个带有@Aspect注解的java类。
AspectJ支持5中类型的通知注解:
1.@Before:前置通知,在方法执行前执行
2.@After:后置通知,在方法执行后执行
3.@AfterReturning:返回通知,在方法返回结果之后执行
4.@AfterThrowing:异常通知,在方法抛出异常之后执行
5.@Around:环绕通知,围绕着方法执行。
上代码,首先建立一个bean实例:
package com.sevenhu.AOPTests; import org.springframework.stereotype.Component; /** * Created by hu on 2016/4/1. */ @Component public class Calculator { //加 public int add(int a,int b){ return a+b; } //减 public int sub(int a,int b){ return a-b; } //乘 public int multiply(int a,int b){ return a*b; } //除 public double divide(int a,int b) throws Exception { if(b==0){ throw new Exception(); }else{ return a/b; } } }
切面代码如下:
package com.sevenhu.AOPTests; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Arrays; /** * Created by hu on 2016/4/1. */ @Aspect @Component public class AspectDemo { //可以在通知方法中声明一个类型为JoinPoint的参数,然后就可以访问链接的细节,如方法名和参数值 //前置通知 @Before("execution(public int com.sevenhu.AOPTests.Calculator.*(int ,int ))") public void beforeMethod(JoinPoint joinPoint){ String methodName=joinPoint.getSignature().getName(); Object[] args=joinPoint.getArgs(); System.out.println("The method "+methodName+" begins with "+ Arrays.asList(args)); } //后置通知:在连接点完成之后执行,即连接点返回结果或抛出异常的时候。 @After("execution(public int com.sevenhu.AOPTests.Calculator.*(int ,int ))") public void afterMethod(JoinPoint joinPoint){ String methodName=joinPoint.getSignature().getName(); System.out.println("The method "+methodName+" ends"); } //返回通知:只在连接点返回结果的时候执行(即不抛出异常),并且还可以访问返回结果 @AfterReturning(pointcut = "execution(public int com.sevenhu.AOPTests.Calculator.*(int ,int ))",returning = "result") public void afterMethodReturning(JoinPoint joinPoint,Object result){//必须在方法签名中添加一个同名的参数“result”,Spring会通过这个参数传递返回值 String methodName=joinPoint.getSignature().getName(); System.out.println("The method "+methodName+" ends with a returning value "+result); } //异常通知:只有在连接点抛出异常的时候才会执行异常通知 @AfterThrowing(pointcut = "execution(public int com.sevenhu.AOPTests.Calculator.*(int ,int ))",throwing = "e") public void afterMethodThrowing(JoinPoint joinPoint,Exception e){//必须在方法签名中添加一个同名的参数“e”,Spring会通过这个参数传递异常对象 String methodName=joinPoint.getSignature().getName(); System.out.println("The method "+methodName+" has thrown a exception: "+e.getMessage()); } //环绕通知:环绕通知是所有通知中功能最为强大的,能够全面控制连接点,甚至可以控制是否执行连接点,对于环绕通知,连接点参数是必须的 /* * ProceedingJoinPoint是JoinPoint的子接口,允许控制何时执行,是否执行连接点 * 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法,如果忘记这样做就会导致通知被执行了,但是目标方法没有被执行。 * 注:环绕通知的方法需要返回目标方法执行之后的结果,即joinPoint.proceed();的返回值,否则 会出现空指针异常 * */ @Around("execution(public int com.sevenhu.AOPTests.Calculator.*(int ,int ))") public void aroundMethod(ProceedingJoinPoint joinPoint) { String methodName=joinPoint.getSignature().getName(); System.out.println("The method "+methodName+" begins");//相当于前置通知 try{ joinPoint.proceed();//执行连接点方法 }catch (Throwable e){ System.out.println("An Exception happened");//相当于异常通知 } System.out.println("The method "+methodName+" ends");//相当于后置通知 } }
指定切面的优先级
在同一个连接点上可能不止一个切面,除非明确指定,否则它们的优先级是不确定的,切面的优先级可以通过实现Ordered接口 或@Order注解指定。
实现Order接口,getOrder()方法的返回值越小,优先级越高,常用的也就是使用注解的方式,所以具体方法如下:
@Aspect @Order(0) class Order1{ ... } @Aspect @Order(1) class Order2{ ... }
重用切入点定义
在编写AspectJ切面时,可以直接在通知注解中书写切入点表达式,但是一个切入点表达式可能会在通知中重复出现。在AspectJ切面中,可以通过@PointCut注解将一个切入点声明成简单的方法,切入点的方法体通常是空的。其具体用法如下:
@Pointcut("execution(* *.*(..)") private void pointCut(){} @Before("pointCut()") public void beforeMethod(){ System.out.println("doning sth..."); }