1. 案例-测量业务层接口万次执行效率
能描述出环绕通知里面的实现步骤
1.1 需求和分析
需求:任意业务层接口执行均可显示其执行效率(执行时长)
分析:
1:业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率
2:通知类型选择前后均可以增强的类型——环绕通知
1.2 代码实现
【前置工作】环境准备 (在jdbcTemplate CRUD中操作)
-
Spring整合JdbcTemplate对spring_db数据库中的Account进行CRUD操作
-
Spring整合Junit测试CRUD是否OK。
-
在pom.xml中添加aspectjweaver切入点表达式依赖
-
... ...
【第一步】编写通知类
@Component
@Aspect
public class ProjectAdvice {//匹配业务层的所有方法@Pointcut("execution(* com.by.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.by")
@PropertySource("classpath:jdbc.properties")
@Import(jdbcConfig.class)
@EnableAspectJAutoProxy //开启aop注解扫描
public class SpringConfig {
}
【第三步】运行测试类,查看结果
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class SpringTest {@Autowiredprivate AccountService accountService;@Testpublic void testFindById(){Account account = accountService.findById(2);}@Testpublic void testFindAll(){List<Account> list = accountService.findAll();}
}
运行结果:
2. AOP通知获取数据
获取切入点方法的参数
JoinPoint:适用于前置、后置、返回后、抛出异常后通知
ProceedJointPoint:适用于环绕通知
获取切入点方法返回值
返回后通知
环绕通知获
获取切入点方法运行异常信息
抛出异常后通知
环绕通知
AOP切入点数据获取
在环绕通知中可以获取到哪些数据?
数据准备
dao接口,dao实现类,Test测试类
public interface UserDao {public String findName(int id);
}@Component
public class UserDaoImpl implements UserDao {@Overridepublic String findName(int id) {System.out.println("id"+id);if(true)throw new NullPointerException();return "body";}
}@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AppTest {@Autowiredprivate UserDao userDao;@Testpublic void test(){String name = userDao.findName(1);System.out.println(name);}
编写通知类
@Component
@Aspect
public class MyAcpectde {@Pointcut("execution(* com.by.dao.impl.UserDaoImpl.*(..))")public void pt(){}}
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);
}
-
环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {// 手动调用连接点方法,返回值就是连接点方法的返回值Object ret = pjp.proceed();return 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;
}