这是一篇Spring boot aop的简单应用。
应用场景
我在我的应用中,如果尝试获取一个不存在的资源,例如GET /user/123,当不存在123这个用户时,我会返回404错误给前端。
以前的做法是在service层获取资源,资源为null时抛出NotFoundException异常。久而久之代码里就会充斥着大量的重复判断的代码,例如ArticleDO articleDO = articleMapper.getArticle(articleId);
if (articleDO == null) {
throw Exceptions.notFoundException;
}
这明显不美观,于是我通过aop的方式用一行注解@NotFound实现了上面的效果,只要SQL查询结果为null,即抛出异常。/**
* 获取文章详细
* @param id 文章id
* @return 文章详细
* @throws BaseNotFoundException not found
*/
@NotFound
@Select("SELECT * FROM db_article WHERE article_id=#{articleId}")
ArticleDO getArticle(Integer id) throws BaseNotFoundException;
引入包
org.springframework.boot
spring-boot-starter-aop
定义切点@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NotFound {
Class> notFoundExceptionClz() default BaseNotFoundException.class;
}@Aspect
@Component
public class NotFoundAop {
@Pointcut("@annotation(nf)")
public void notFound(NotFound nf) {
}
}
以上是通过注解@annotation的方式定义了一个切点,除了使用注解,还可以使用execution的方式指定包、类、方法等。
spring AspectJ的Execution表达式
AOP的5种通知before(前置通知): 在方法开始执行前执行
after(后置通知): 在方法执行后执行
afterReturning(返回后通知): 在方法返回后执行
afterThrowing(异常通知): 在抛出异常时执行
around(环绕通知): 在方法执行前和执行后都会执行
执行顺序around > before > around > after > afterReturning
代码实现
因为要对返回值进行判断,所以用了@AfterReturning通知,returning定义的是方法返回值。@Aspect
@Component
public class NotFoundAop {
@Pointcut("@annotation(nf)")
public void notFound(NotFound nf) {
}
@AfterReturning(returning = "ret", pointcut = "notFound(nf)", argNames = "joinPoint,ret,nf")
public void doNotFound(JoinPoint joinPoint, Object ret, NotFound nf) throws Exception {
if (ret != null) {
return;
}
if (BaseNotFoundException.class.isAssignableFrom(nf.notFoundExceptionClz())) {
throw (BaseNotFoundException) nf.notFoundExceptionClz().getDeclaredConstructor().newInstance();
} else {
throw Exceptions.notFoundException;
}
}
}
我在@NotFound注解中加了notFoundExceptionClz参数,可以选填抛出的异常,不填则默认抛出Exceptions.notFoundException。这只是一个简单的不能再简单的demo,AOP的功能很强大,能做到的事情也非常的多。如果项目中出现了大量重复不美观的代码,就要考虑是否可以靠一些方式简化了。