6.切入点表达式: 1.上述案例通过junit测试,会发现,我们调用目标类的四个方法只有add方法被加入了4个通知,如果想所有的方法都加上这些通知,可以 在切入点表达式处,将execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 换成: execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))这样只要是有两个参数,且 参数类型为int的方法在执行的时候都会执行其相应的通知方法! 2.①切入点表达式的语法格式 execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表])) 1.任意参数,任意类型 2.任意返回值 3.用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可! 需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值! 最详细的切入点表达式: execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 最模糊的切入点表达式: execution (* *.*(..)) 7.统一声明切入点表达式 @Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))") public void myPointCut(){} //调用上面的函数,就可以代替重复的很多代码 @AfterReturning (value="myPointCut()", returning= "result")
8.通知方法的细节 ①在通知中获取目标方法的方法名和参数列表 [1]在通知方法中声明一个JoinPoint类型的形参 [2]调用JoinPoint对象的getSignature()方法获取目标方法的签名 [3]调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表 ②在返回通知中获取方法的返回值 [1]在@AfterReturning注解中添加returning属性 @AfterReturning (value="myPointCut()", returning= "result")
[2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致 showReturnLog(JoinPoint joinPoint, Object result)
③在异常通知中获取异常对象 [1]在@ AfterThrowing注解中添加throwing属性 @AfterThrowing (value="myPointCut()",throwing= "throwable" ) [2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致 showExceptinLog(JoinPoint joinPoint, Throwable throwable) 9.根据接口类型获取target对象时,实际上真正放在IOC容器中的对象是代理对象,而并不是目标对象本身! 10.环绕通知:@Around 1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数 @Around(value="pointCut()") public void around(ProceedingJoinPoint joinPoint){ }
2.环绕通知会将其他4个通知能干的,自己都给干了! 注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理! @Around(value="pointCut()") public Object around(ProceedingJoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); String methodName = signature.getName(); List<Object> list = Arrays.asList(args); Object result = null; try { //目标方法之前要执行的操作 System.out.println("[环绕日志]"+methodName+"开始了,参数为:"+list); //调用目标方法 result = joinPoint.proceed(args); //目标方法正常执行之后的操作 System.out.println("[环绕日志]"+methodName+"返回了,返回值为:"+result); } catch (Throwable e) { //目标方法抛出异常信息之后的操作 System.out.println("[环绕日志]"+methodName+"出异常了,异常对象为:"+e); throw new RuntimeException(e.getMessage()); }finally{ //方法最终结束时执行的操作! System.out.println("[环绕日志]"+methodName+"结束了!"); } return result; }
11.切面的优先级 对于同一个代理对象,可以同时有多个切面共同对它进行代理。 可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高! @Aspect @Component @Order(value=40) public class TxAspect { @Around(value="execution(public * com.neuedu.aop.target.MathCalculatorImpl.*(..))") public Object around(ProceedingJoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); String methodName = signature.getName(); List<Object> list = Arrays.asList(args); Object result = null; try { //目标方法之前要执行的操作 System.out.println("[事务日志]"+methodName+"开始了,参数为:"+list); //调用目标方法 result = joinPoint.proceed(args); //目标方法正常执行之后的操作 System.out.println("[事务日志]"+methodName+"返回了,返回值为:"+result); } catch (Throwable e) { //目标方法抛出异常信息之后的操作 System.out.println("[事务日志]"+methodName+"出异常了,异常对象为:"+e); throw new RuntimeException(e.getMessage()); }finally{ //方法最终结束时执行的操作! System.out.println("[事务日志]"+methodName+"结束了!"); } return result; } } 12.注意:上面的AOP都是通过注解实现的,AOP实际上也可以通过xml配置的方式实现! <!-- 1.将需要加载到IOC容器中的bean配置好 --> <bean class="com.neuedu.aop.proxy.LogAspect"></bean> <bean class="com.neuedu.aop.target.TxAspect"></bean> <bean class="com.neuedu.aop.target.MathCalculatorImpl"></bean> <!-- 2.配置AOP,需要导入AOP名称空间 --> <aop:config> <!-- 声明切入点表达式 --> <aop:pointcut expression="execution(* com.neuedu.aop.target.MathCalculatorImpl.*(..))" /> <!-- 配置日志切面类,引用前面的类 ,通过order属性控制优先级--> <aop:aspect ref="logAspect" order="25"> <!-- 通过method属性指定切面类的切面方法,通过pointcut-ref指定切入点表达式 --> <aop:before method="showBeginLog" pointcut-ref="myPointCut"/> <aop:after method="showAfterLog" pointcut-ref="myPointCut"/> <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/> <aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/> <aop:around method="around" pointcut-ref="myPointCut"/> </aop:aspect> <!-- 配置事务切面类,引用前面的类 --> <aop:aspect ref="txAspect" order="20"> <aop:around method="around" pointcut-ref="myPointCut"/> </aop:aspect> </aop:config>
需要知道的是:事务的管理是和AOP是有很大关系的,即声明式事务的底层是用事务实现的!
