一、AOP的配置(注解)
步骤一、导入jar包:
处理那5个jar包之外,还需要导入:
- aopalliance
- aspectjweaver
- spring-aop
- spring-aspects
步骤二、在配置文件中加入aop、context的命名空间
步骤三分为基于注解方式配置AOP和xml方式配置aop;
基于注解方式(本篇)
①在配置文件中加入如下配置;
1 <!-- 使AspjectJ注释起作用,自动匹配的类生成代理对象 --> 2 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
②把横切关注点的代码都加入到切面的类中,
③切面首先是一个IOC中的bean,即加@Conponent注释
④切面需要加入@Aspect注释
⑤在类中声明各种通知:
- @Before:前置通知,在方法执行前执行;
- @After:后置通知,在方法执行后执行
-
@AfterRunning:返回通知,在方法返回结果后执行
-
@Afterthrowing:异常通知之后
-
@Around:环绕通知
二、AOP常用通知:
- @Before:前置通知,在方法执行前执行;
@Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 作用于接口中的所有方法 public void beforeMethod(JoinPoint joinPoint) { String method = joinPoint.getSignature().getName();// 方法的签名 List<Object> args = Arrays.asList(joinPoint.getArgs());// 方法的参数 System.out.println("the method " + method + " begins with" + args); }
- @After:后置通知,在方法执行后执行,在后置通知中,不能访问目标方法执行的结果,如果有异常也执行,通知在异常之前;
1 @After("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") 2 public void afterMethod(JoinPoint joinPoint) { 3 String method = joinPoint.getSignature().getName(); 4 List<Object> args = Arrays.asList(joinPoint.getArgs()); 5 System.out.println("the method " + method + " is end to " + args); 6 }
-
@AfterRunning:返回通知,在方法返回结果后执行,可以访问到返回值;
1 @AfterReturning(value = "execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )", returning = "result") 2 public void afterReturn(JoinPoint joinPoint, Object result) { 3 String method = joinPoint.getSignature().getName(); 4 System.out.println("the method " + method + " is end with " + result); 5 }
-
@Afterthrowing:异常通知之后,可以访问到异常,并且可以指定异常类型,只有符合该异常类型时才被执行
1 @AfterThrowing(value = "execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )", throwing = "ex") 2 public void afterThrowing(JoinPoint joinPoint, Object ex) { 3 String method = joinPoint.getSignature().getName(); 4 System.out.println("the method " + method + " occured exception: " + ex); 5 }
-
@Around:环绕通知;
环绕通知类似动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行日志方法 且必须有返回值,返回值是目标方法的返回值
1 @Around("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )") 2 public Object aroundMethod(ProceedingJoinPoint point) { 3 Object result = null; 4 String method = point.getSignature().getName(); 5 // 执行目标方法 6 try { 7 // 前置通知 8 System.out.println("the method " + method + " is begin with " + Arrays.asList(point.getArgs())); 9 result = point.proceed(); 10 // 返回通知 11 System.out.println("the method " + method + " is end to " + result); 12 } catch (Throwable e) { 13 // TODO Auto-generated catch block 14 System.out.println("the method " + method + " occured exception: " + e); 15 throw new RuntimeException(e); 16 } 17 System.out.println("the method " + method + " ends"); 18 19 return 100; 20 }
三、切点表达式
表达式
1 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下:
- modifiers-pattern:方法的可见性,如public,protected;
- ret-type-pattern:方法的返回值类型,如int,void等;
- declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
- name-pattern:方法名类型,如buisinessService();
- param-pattern:方法的参数类型,如java.lang.String;
- throws-pattern:方法抛出的异常类型,如java.lang.Exception;
举例说明:
// @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.add(int, int) )")// 作用于接口中的add方法 // @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 作用于接口中的所有方法 (所有方法是指public int类型的) // @Before("execution(* ixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 第一个* 表示:任意修饰符和任意返回值,第二个*代码任意参数为(int,int)方法 // @Before("execution( public * ixiuming.spring.aop.impl.ArithmeticCaculator.*(..) )") // 第二个*代表任意方法;..代表任意个数的参数,即所有公有方法 // @Before("execution( public double ixiuming.spring.aop.impl.ArithmeticCaculator.*(double.) )") // 返回所有double的第一个参数为double的public的方法
@Pointcut
使用@Pointcut 来声明切入点表达式,后面的其他通知直接使用方法名来引用当前的切入点表达式;如下代码,前置通知使用了方法名为declareJoinPointExpress来引用切点表达式;
这样做的好处是,可以统一管理切点表达式;
1 @Pointcut("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(..))") 2 public void declareJoinPointExpress() { 3 4 } 5 6 //声明该方法是一个前置通知:在目标方法之前执行 7 @Before("declareJoinPointExpress()") 8 public void beforeMethod(JoinPoint joinPoint) { 9 String method = joinPoint.getSignature().getName(); 10 List<Object> args = Arrays.asList(joinPoint.getArgs()); 11 System.out.println("the method " + method + " begins with" + args); 12 }
四、实例说明AOP配置:
以实现三(一)中的 为ArithmeticCaculator添加 各方法 执行前 和计算结果后的日志的AOP方案为实例;
步骤一、为ArithmeticCaculatorImpl类添加@Component 注解 来表示 该组件需要被sping容器管理
1 package lixiuming.spring.aop.impl; 2 3 import org.springframework.stereotype.Component; 4 5 @Component 6 public class ArithmeticCaculatorImpl2 implements ArithmeticCaculator { 7 8 @Override 9 public int add(int i, int j) { 10 int result = i+j; 11 return result; 12 } 13 14 @Override 15 public int sub(int i, int j) { 16 int result = i-j; 17 return result; 18 } 19 20 @Override 21 public int mul(int i, int j) { 22 int result = i*j; 23 return result; 24 } 25 26 @Override 27 public int div(int i, int j) { 28 int result = i/j; 29 return result; 30 } 31 32 }
步骤二、需要添加一个切面:
关于切面声明的说明:
- 切面需要放置在spring 容器中;所以首先需要一个@Component注解
- 声明一个切面用注解 @Aspect;
为实现上述实例,需要添加一个前置通知和后置通知;前置通知即,在目标方法执行之前执行;后置通知,即在目标方法执行后执行,无论是否发生异常。
1 package lixiuming.spring.aop.impl; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import org.aspectj.lang.JoinPoint; 7 import org.aspectj.lang.annotation.After; 8 import org.aspectj.lang.annotation.Aspect; 9 import org.aspectj.lang.annotation.Before; 10 import org.springframework.stereotype.Component; 11 12 //把这个类声明为一个切面,需要把该类放入到IOC容器中,再声明为一个切面 13 @Aspect 14 @Component 15 public class LoggingAspect { 16 // 声明该方法是一个前置通知:在目标方法之前执行 18 @Before("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") // 作用于接口中的所有方法 22 public void beforeMethod(JoinPoint joinPoint) { 23 String method = joinPoint.getSignature().getName();// 方法的签名 24 List<Object> args = Arrays.asList(joinPoint.getArgs());// 方法的参数 25 System.out.println("the method " + method + " begins with" + args); 26 } 27 28 // 声明后置通知:在目标方法执行后(无论是否发生异常)执行的通知 29 // 在后置通知中,不能访问目标方法执行的结果,需要在返回通知里面访问 30 @After("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(int, int) )") 31 public void afterMethod(JoinPoint joinPoint) { 32 String method = joinPoint.getSignature().getName(); 33 List<Object> args = Arrays.asList(joinPoint.getArgs()); 34 System.out.println("the method " + method + " is end to " + args); 35 } 36 37 }
步骤三、配置文件:引入了context和aop的命名空间
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 9 10 <context:component-scan base-package="lixiuming.spring.aop.impl"/> 11 <!-- 使AspjectJ注释起作用,自动匹配的类生成代理对象 --> 12 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 13 </beans>
使用main方法测试:
1 public static void main(String[] args) { 2 ApplicationContext cxt = new ClassPathXmlApplicationContext("ApplicationContext.xml"); 3 ArithmeticCaculator arithmeticCaculator = cxt.getBean(ArithmeticCaculator.class); 4 5 int result = arithmeticCaculator.add(1, 2); 6 System.out.println("-->" + result); 7 8 int result1 = arithmeticCaculator.div(4, 2); 9 System.out.println("-->" + result1); 10 11 int result2 = arithmeticCaculator.mul(4, 2); 12 System.out.println("-->" + result2); 13 14 int result3 = arithmeticCaculator.sub(4, 2); 15 System.out.println("-->" + result3); 16 17 }
测试运行结果:
the method add begins with[1, 2]
the method add is end to [1, 2]
-->3
the method div begins with[4, 2]
the method div is end to [4, 2]
-->2
the method mul begins with[4, 2]
the method mul is end to [4, 2]
-->8
the method sub begins with[4, 2]
the method sub is end to [4, 2]
-->2
步骤二中除了使用前置和后置通知,还可以使用环绕通知来实现上述功能;
代码如下:
1 package lixiuming.spring.aop.impl; 2 3 import java.util.Arrays; 4 5 import org.aspectj.lang.ProceedingJoinPoint; 6 import org.aspectj.lang.annotation.Around; 7 import org.aspectj.lang.annotation.Aspect; 8 import org.aspectj.lang.annotation.Pointcut; 9 import org.springframework.stereotype.Component; 10 11 //把这个类声明为一个切面,需要把该类放入到IOC容器中 12 @Aspect 13 @Component 14 public class LoggingAspect { 15 16 // 定义一个方法,用于声明切入点表达式,一般该方法中不需要其他的代码 17 // 使用@Pointcut 来声明切入点表达式, 18 // 后面的其他通知直接使用方法名来引用当前的切入点表达式 19 @Pointcut("execution(public int lixiuming.spring.aop.impl.ArithmeticCaculator.*(..))") 20 public void declareJoinPointExpress() { 21 22 } 23 24 /** 25 * 环绕通知需要携带ProceedingJoinPoint类型的参数 26 * 环绕通知类似动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行日志方法 且必须有返回值,返回值是目标方法的返回值 27 */ 28 @Around("declareJoinPointExpress()") 29 public Object aroundMethod(ProceedingJoinPoint point) { 30 Object result = null; 31 String method = point.getSignature().getName(); 32 // 执行目标方法 33 try { 34 // 前置通知 35 System.out.println("the method " + method + " is begin with " + Arrays.asList(point.getArgs())); 36 result = point.proceed(); 37 // 后置通知 38 System.out.println("the method " + method + " is end to " + Arrays.asList(point.getArgs())); 39 } catch (Throwable e) { 40 // TODO Auto-generated catch block 41 System.out.println("the method " + method + " occured exception: " + e); 42 throw new RuntimeException(e); 43 } 44 45 return result; 46 } 47 }
五、切面的优先级
使用@Order(index)指定执行顺序的优先级,index为数字,index越小,优先级越高;@Order位置为放置在@Aspect前面;代码如下:
@Order(1)//执行顺序的优先级 @Aspect @Component //验证通知 public class VlidationAspect { @Before("LoggingAspect.declareJoinPointExpress()") public void validationArgs(JoinPoint jointPoint){ System.out.println("-->validation:"+Arrays.asList(jointPoint.getArgs())); } }
六、xml方式配置AOP
ArithmeticCaculator 不变;ArithmeticCaculatorImpl移除@Component;
LoggingAspect:
1 package lixiuming.spring.aop.impl2; 2 3 import java.util.Arrays; 4 import java.util.List; 5 6 import org.aspectj.lang.JoinPoint; 7 import org.aspectj.lang.ProceedingJoinPoint; 8 import org.aspectj.lang.annotation.After; 9 import org.aspectj.lang.annotation.AfterReturning; 10 import org.aspectj.lang.annotation.AfterThrowing; 11 import org.aspectj.lang.annotation.Around; 12 import org.aspectj.lang.annotation.Aspect; 13 import org.aspectj.lang.annotation.Before; 14 import org.aspectj.lang.annotation.Pointcut; 15 import org.springframework.stereotype.Component; 16 17 public class LoggingAspect { 18 19 public void beforeMethod(JoinPoint joinPoint){ 20 String method = joinPoint.getSignature().getName(); 21 List<Object> args = Arrays.asList(joinPoint.getArgs()); 22 System.out.println("the method "+method+" begins with"+args); 23 } 24 25 public void afterMethod(JoinPoint joinPoint){ 26 String method = joinPoint.getSignature().getName(); 27 List<Object> args = Arrays.asList(joinPoint.getArgs()); 28 System.out.println("the method "+method+" is end to "+args); 29 } 30 31 /** 32 *在方法正常结束后执行的代码 33 *返回通知是可以访问到方法的返回值 34 */ 35 public void afterReturn(JoinPoint joinPoint,Object result){ 36 String method = joinPoint.getSignature().getName(); 37 System.out.println("the method "+method+" is end with " +result); 38 } 39 40 public void afterThrowing(JoinPoint joinPoint,Object ex){ 41 String method = joinPoint.getSignature().getName(); 42 System.out.println("the method "+method+" occured exception: " + ex); 43 } 44 45 public Object aroundMethod(ProceedingJoinPoint point){ 46 Object result = null; 47 String method = point.getSignature().getName(); 48 //执行目标方法 49 try { 50 //前置通知 51 System.out.println("the method "+method+" is begin with "+Arrays.asList(point.getArgs())); 52 result = point.proceed(); 53 //返回通知 54 System.out.println("the method "+method+" is end to "+ result); 55 } catch (Throwable e) { 56 // TODO Auto-generated catch block 57 System.out.println("the method "+method+" occured exception: " + e); 58 throw new RuntimeException(e); 59 } 60 System.out.println("the method "+method+" ends"); 61 62 return 100; 63 } 64 }
VlidationAspect:
1 package lixiuming.spring.aop.impl2; 2 3 import java.util.Arrays; 4 5 import org.aspectj.lang.JoinPoint; 6 import org.aspectj.lang.annotation.Aspect; 7 import org.aspectj.lang.annotation.Before; 8 import org.springframework.core.annotation.Order; 9 import org.springframework.stereotype.Component; 10 //验证通知 11 public class VlidationAspect { 12 public void validationArgs(JoinPoint jointPoint){ 13 System.out.println("-->validation:"+Arrays.asList(jointPoint.getArgs())); 14 } 15 16 17 }
配置文件:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> 7 8 <!-- 配置bean --> 9 <bean id="aop" class="lixiuming.spring.aop.impl2.ArithmeticCaculatorImpl2"></bean> 10 <!-- 配置切面的bean --> 11 <bean id="LoggingAspect" class="lixiuming.spring.aop.impl2.LoggingAspect"></bean> 12 <bean id="VlidationAspect" class="lixiuming.spring.aop.impl2.VlidationAspect"></bean> 13 14 <!-- 配置AOP --> 15 <aop:config> 16 <!-- 配置切面表达式 --> 17 <aop:pointcut expression="execution(* lixiuming.spring.aop.impl2.ArithmeticCaculator.*(int,int))" id="pointcut"/> 18 <!-- 配置切面及通知 --> 19 <aop:aspect ref="LoggingAspect" order="2"> 20 <aop:before method="beforeMethod" pointcut-ref="pointcut"/> 21 <aop:after method="afterMethod" pointcut-ref="pointcut"/> 22 <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/> 23 <aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="result"/> 24 <aop:around method="aroundMethod" pointcut-ref="pointcut"/> 25 </aop:aspect> 26 27 <aop:aspect ref="VlidationAspect" order="1"> 28 <aop:before method="validationArgs" pointcut-ref="pointcut"/> 29 </aop:aspect> 30 </aop:config> 31 </beans>