二、AOP案例
1. 案例-测量业务层接口万次执行效率
问题导入
能不能描述一下环绕通知里面的实现步骤?
1.1 需求和分析
需求:任意业务层接口执行均可显示其执行效率(执行时长)
分析:
①:业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率
②:通知类型选择前后均可以增强的类型——环绕通知
1.2 代码实现
【前置工作】环境准备
Spring整合mybatis对spring_db数据库中的Account进行CRUD操作
Spring整合Junit测试CRUD是否OK。
在pom.xml中添加aspectjweaver切入点表达式依赖
… …
【第一步】编写通知类
@Component @Aspect public class ProjectAdvice { //匹配业务层的所有方法 @Pointcut("execution(* com.itheima.service.*Service.*(..))") private void servicePt(){} //设置环绕通知,在原始操作的运行前后记录执行时间 @Around("ProjectAdvice.servicePt()") //本类类名可以省略不写 public void runSpeed(ProceedingJoinPoint pjp) throws Throwable { //获取执行的签名对象 Signature signature = pjp.getSignature(); //获取接口/类全限定名 String className = signature.getDeclaringTypeName(); //获取方法名 String methodName = signature.getName(); //记录开始时间 long start = System.currentTimeMillis(); //执行万次操作 for (int i = 0; i < 10000; i++) { pjp.proceed(); } //记录结束时间 long end = System.currentTimeMillis(); //打印执行结果 System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms"); } }
【第二步】在SpringConfig配置类上开启AOP注解功能
@Configuration @ComponentScan("com.itheima") @PropertySource("classpath:jdbc.properties") @Import({JdbcConfig.class,MybatisConfig.class}) @EnableAspectJAutoProxy //开启AOP注解功能 public class SpringConfig { }
【第三步】运行测试类,查看结果
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTestCase { @Autowired private AccountService accountService; @Test public void testFindById(){ Account account = accountService.findById(2); } @Test public void testFindAll(){ List<Account> list = accountService.findAll(); } }
2. AOP切入点数据获取
问题导入
在环绕通知中可以获取到哪些数据?
2.1 获取参数
说明:在前置通知和环绕通知中都可以获取到连接点方法的参数们
JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数 @Before("pt()") public void before(JoinPoint jp) { Object[] args = jp.getArgs(); //获取连接点方法的参数们 System.out.println(Arrays.toString(args)); }
ProccedJointPoint是JoinPoint的子类 @Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); //获取连接点方法的参数们 System.out.println(Arrays.toString(args)); Object ret = pjp.proceed(); return ret; }
2.2 获取返回值
说明:在返回后通知和环绕通知中都可以获取到连接点方法的返回值
抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterReturning(value = "pt()",returning = "ret") public void afterReturning(String ret) { //变量名要和returning="ret"的属性值一致 System.out.println("afterReturning advice ..."+ret); }
环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值
2.3 获取异常
说明:在抛出异常后通知和环绕通知中都可以获取到连接点方法中出现的异常
抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterThrowing(value = "pt()",throwing = "t") public void afterThrowing(Throwable t) {//变量名要和throwing = "t"的属性值一致 System.out.println("afterThrowing advice ..."+ t); }
抛出异常后通知可以获取切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象
@Around("pt()") public Object around(ProceedingJoinPoint pjp) { Object ret = null; //此处需要try...catch处理,catch中捕获到的异常就是连接点方法中抛出的异常 try { ret = pjp.proceed(); } catch (Throwable t) { t.printStackTrace(); } return ret; }