目录
-
AOP基本配置
-
AspectJ
- Aspect(切面)用于描述切入点与通知间的关系,是AOP编程中的一个概念
- Aspectj是基于java语言对Aspect的实现
-
AOP配置
- 名称:aop:config
- 类型:标签
- 归属:beans标签
- 作用:设置AOP
- 格式:
- <beans>
- <aop:config>.....</aop:config>
- <aop:config>.....</aop:config>
- < beans>
- 说明:一个beans标签中可以配置多个aop:config标签
- 名称:aop:aspect
- 类型:标签
- 归属:aop:config标签
- 作用:设置具体的AOP通知对应的切入点
- 格式:
- <aop:config>
- <aop:aspect ref="beanId">.......</aop:aspect>
- <aop:aspect ref="beanId">..….</aop:aspect>
- </aop:config>
- 说明:
- 一个aop:config标签中可以配置多个aop:aspect标签
- 基本属性:
- ref:通知所在的bean的id
- 名称:aop:pointcut
- 类型:标签
- 归属:aop:config标签、aop:aspect标签
- 作用:设置切入点
- 格式:
- <aop:config>
- <aop:pointcut id="pointcutId" expression="…....."/>
- <aop:aspect>
- <aop:pointcut id="pointcutId" expression="…....."/>
- </aop: aspect>
- </aop: config>
- 说明:
- 一个aop:config标签中可以配置多个aop:pointcut标签,且该标签可以配置在aop:aspect标签内
- 基本属性:
- id:识别切入点的名称
- expression:切入点表达式
-
切入点表达式
-
切入点表达式的组成
- 切入点描述的是某个方法
- 切入点表达式是一个快速匹配方法描述的通配格式,类似于正则表达式
- 关健字(访问修饰符 返回值 包名.类名.方法名(参数)异常名)
- 关键字:描述表达式的匹配模式(参考关键字列表)
- 访问修饰符:方法的访问控制权限修饰符
- 类名:方法所在的类(此处可以配置接口名称)
- 异常:方法定义中指定抛出的异常
- 范例:
- execution (public User com.superdemo.service.UserService.findById (int) )
-
切入点表达式-关键字
- execution:匹配执行指定方法
- args:匹配带有指定参数类型的方法
- within :......
- this :......
- target : ......
- @within : ......
- @target : ......
- @args : ......
- @annotation : ......
- bean:......
- reference pointcut : .......
-
切入点表达式-通配符
- *:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
- execution (public * com.superdemo.*.UserService.find* (*) )
- 匹配com.superdemo包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
- ..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
- execution (public User com.UserService.findById (..) )
- 匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法
- +:专用于匹配子类类型
- execution (* *..*Service+.*(..))
-
切入点表达式-逻辑运算符
- &&:连接两个切入点表达式,表示两个切入点表达式同时成立的匹配
- ||:连接两个切入点表达式,表示两个切入点表达式成立任意一个的匹配
- !:连接单个切入点表达式,表示该切入点表达式不成立的匹配
-
切入点配置经验
- 企业开发命名规范严格遵循规范文档进行
- 先为方法配置局部切入点
- 再抽取类中公共切入点
- 最后抽取全局切入点
- 代码走查过程中检测切入点是否存在越界性包含
- 代码走查过程中检测切入点是否存在非包含性进驻
- 设定AOP执行检测程序,在单元测试中监控通知被执行次数与预计次数是否匹配
- 设定完毕的切入点如果发生调整务必进行回归测试
- (以上规则适用于XML配置格式)
-
五种通知类型配置
-
通知类型
- AOP的通知类型共5种
- 前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
- 应用:数据校验
- 后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
- 应用:现场清理
- 返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
- 应用:返回值相关数据处理
- 抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
- 应用:对原始方法中出现的异常信息进行处理
- 环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
- 应用:十分强大,可以做任何事情
- 环绕通知方法相关说明:
- 方法须设定Object类型的返回值,否则会拦截原始方法的返回
- 如果原始方法返回值类型为void, 通知方法也可以设定返回值类型为void,最终返回null
- 方法需在第一个参数位置设定ProceedingJoinPoint对象,通过该对象调用proceed()方法,实现对原始方法的调用
- 如省略该参数,原始方法将无法执行
- 使用proceed()方法调用原始方法时,因无法预知原始方法运行过程中是否会出现异常,强制抛出Throwable对象,封装原始方法中可能出现的异常信息
-
实例演示
- 前置通知
- 后置通知
- 返回后通知
- 抛出异常后通知
- 环绕通知
-
public class AOPAdvice { public void before(){ System.out.println("before"); } public void after(){ System.out.println("after"); } public void afterReturing(){ System.out.println("afterReturing"); } public void afterThrowing(){ System.out.println("afterThrowing"); } public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around before"); //对原始方法的调用 pjp.proceed(); System.out.println("around after"); } }
-
<!--6.配置切面(切入点与通知的关系)--> <aop:aspect ref="myAdvice"> <!--7.配置具体的切入点对应通知中的哪个操作方法--> <!--<aop:before method="before" pointcut-ref="pt"/>--> <!--<aop:after method="after" pointcut-ref="pt"/>--> <!--<aop:after-returning method="afterReturing" pointcut-ref="pt"/>--> <!--<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>--> <aop:around method="around" pointcut-ref="pt"/> </aop:aspect>
-
通知顺序
- 当同一个切入点配置了多个通知时
- 通知会存在运行的先后顺序,该顺序以通知配置的顺序为准
-
通知获取参数数据
- 设定通知方法第一个参数为JoinPoint
- 通过该对象调用getArgs()方法,获取原始方法运行的参数数组
- 所有的通知均可以获取参数
- 实例:
-
通知获取返回值数据
- 注意:
- before时,程序还没有运行,肯定得不了
- after-throwing绝对不会有,程序都异常了不会有正确结果
- after在抛异常时也运行,有可能拿不到,不能确定,所以它不会有
- 实例:
- after-returning
- around
-
通知获取异常数据
- 注意:
- before时,程序还没有运行,肯定拦截不了
- after-returning时,因为能正常返回,也拦截不到
- after还是那问题,可能能,可能不能,所以不能
- 实例:
- after-throwing
- around
-
public class UserServiceImpl implements UserService { public void save(int i){ //0.将共性功能抽取出来 //System.out.println("共性功能"); System.out.println("user service running..."+i); //int i=1/0; } @Override public int update() { System.out.println("user service update running..."); return 100; } @Override public void delete() { System.out.println("user service delete running..."); int i = 1/0; } }
-
<aop:aspect ref="myAdvice"> <!--7.配置具体的切入点对应通知中的哪个操作方法--> <!--<aop:before method="before" pointcut-ref="pt"/>--> <!--<aop:after method="after" pointcut-ref="pt"/>--> <!--<aop:after-returning method="afterReturing" pointcut-ref="pt" returning="ret"/>--> <aop:after-throwing method="afterThrowing" pointcut-ref="pt" throwing="t"/> <aop:around method="around" pointcut-ref="pt"/> </aop:aspect>
-
public class AOPAdvice { public void before(JoinPoint jp){ Object[] args = jp.getArgs(); System.out.println("before"+args[0]); } public void after(JoinPoint jp){ Object[] args = jp.getArgs(); System.out.println("after"+args[0]); } public void afterReturing(Object ret){ System.out.println("afterReturing..."+ret); } public void afterThrowing(Throwable t){ System.out.println("afterThrowing..."+t.getMessage()); } public Object around(ProceedingJoinPoint pjp){ System.out.println("around before"); //对原始方法的调用 Object ret = null; try { ret = pjp.proceed(); } catch (Throwable e) { System.out.println("around...exception..."+e.getMessage()); } System.out.println("around after..."+ret); return ret; } }
-
public class App { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) ctx.getBean("userService"); //userService.save(666); //int tj = userService.update(); //System.out.println("app......"+tj); userService.delete(); } }