springAop处理统一异常
一、AOP简介:
AOP:面向切面编程,是一种编程思想,是传统的OOP(面向对象编程)的补充,主要用来解决一些系统层面上的共性问题,比如记录日志、事务管理、权限等。
二、基本概念:
- Aspect(切面):通常是一个类,里面可以定义切入点和通知
- JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
- Advice(通知):AOP在特定的切入点上执行的增强功能处理,有before、after、afterReturning、afterThrowing、around
- Point(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
- AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
三、SpringAOP
Spring中的AOP代理离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,目前项目都是面向接口编程,所有JDK动态代理相对用的多一点。
四、基于注解的AOP配置方式
4.1、启用@Aspect支持
类上加@Aspect注解
@Component
@Aspect
public class ControllerLogAcpect {
}
4.2、通知类型介绍
- Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式就可以。
- AfterReturning:在目标方法正常完成后做增强处理,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值。
- AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象。
- Around:环绕通知,在目标方法前后座增强处理,
4.3、通知执行的优先等级
进入目标方法时,先织入Around,再织入Before,退出目标方法时,先织入Around再织入AfterReturning,最后再织入After。
PS:注意SPringAOP的环绕通知会影响到AfterThrowing通知的运行
4.4、切入点的定义和表达式
Spring AOP支持的切入点关键字
execution:用来匹配执行方法的连接点
在切入点方法上添加@Pointcut注解并指定切入点
/**
* 切入点
* execution:固定关键字
* 第一个*表示所有返回值类型,后面接着:com.liao.service包下的所有类*的所有方法*的所有参数(..)
*/
@Pointcut("execution(* com.liao.service.*.*(..))")
public void pointcut() {
}
五、举例:
客户端发送请求后,到达service层进行统一日志处理
@Component
@Aspect
public class ServiceLogAcpect {
private static final Logger logger = LoggerFactory.getLogger(ServiceLogAcpect.class);
/**
* 切入点
* execution:固定关键字
* 第一个*表示所有返回值类型,后面接着:com.liao.service包下的所有类*的所有方法*的所有参数(..)
*/
@Pointcut("execution(* com.powernode.controller.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
//JoinPoint 对象用来获取切入点的信息
// 用户[1.2.3.4],在[xxx],访问了[com.liao.service.xxx()].
//先获取request对象,通过request对象获取客户端ip
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return;
}
HttpServletRequest request = attributes.getRequest();
String ip = request.getRemoteHost();
String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
}
}