BBS论坛项目相关-7:统一异常处理和日志记录

BBS论坛项目相关-7:统一异常处理和日志记录

统一异常处理

@ControllerAdvice:用于修饰类,表示该类是controller配置类;在此类中,可以对controller进行三种全局配置:异常处理方案,绑定数据方案,绑定参数方案
@ExceptionHandler:用于修饰方法,该方法会在controller出现异常后被调用,用于处理捕获到的异常
@ModelAttribute:用于修饰方法,在controller方法执行前被调用,用于为Model对象绑定参数
@DataBinder:用于修饰方法,在Controller方法执行前被调用,用于绑定参数的转换器
将404.html页面放在templates的error文件夹下,发生404异常时会自动跳转到该404页面
因为所有异常抛出后都会到达表现层,所以只需要在表现层统一处理即可,整体扫描太费时间,可以只扫描带有controller注解的
遍历e.getStackTrace()对象,记录错误信息,记录完错误信息需要给浏览器一个响应,重定向到刚才的错误页面,需要额外处理,因为浏览器访问服务器可能是普通请求,也可能是异步请求,普通请求返回到500.html,对于异步请求,需要返回json字符串,所以需要区分开,可通过request判断,查看请求头。
浏览器访问服务器

@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {

    private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);

    @ExceptionHandler({Exception.class})
    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.error("服务器发生异常: " + e.getMessage());
        for (StackTraceElement element : e.getStackTrace()) {
            logger.error(element.toString());
        }

        String xRequestedWith = request.getHeader("x-requested-with");
        if ("XMLHttpRequest".equals(xRequestedWith)) {
            response.setContentType("application/plain;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
        } else {
            response.sendRedirect(request.getContextPath() + "/error");
        }
    }
}

统一日志记录

拦截器和控制通知都是针对控制层或异常进行处理,但这里统一日志不止是针对控制层或异常处理,也可能对业务组件进行记录。
AOP记录日志:
AOP:
1 编译时织入,需使用特殊的编译器
2 装载时织入,需使用特殊的类装载器
3 运行时织入,需为目标生成代理对象

AspectJ:扩展了java语言,定义了AOP语法,在编译期织入代码,有一个专门的编译器,用来生成java字节码规范的class文件
Spring AOP:使用纯java实现,不需要专门的编译过程,不需要特殊的类加载器,在运行时通过代理的方式织入代码,只支持方法类型的连接点,Spring支持对AspectJ的集成

代理:
JDK动态代理
Java提供动态代理技术,可以在运行时创建接口的代理实例
SpringAOP默认采用此种方式,在接口的代理实例中织入代码
CGLib动态代理:
采用底层的字节码技术,在运行时创建子类代理实例
当目标对象不存在接口时,Spring AOP会采用此种方式

AOP注解

Aspect(切面):通常是一个类,里面可以定义切入点和通知

JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

@Pointcut(“execution(* com.aijava.springcode.service….(…))”)
第一个表示匹配任意的方法返回值,…(两个点)表示零个或多个,上面的第一个…表示service包及其子包,第二个表示所有类,第三个*表示所有方法,第二个…表示方法的任意参数个数

5种注解通知

-@Before 前置通知,在方法执行之前执行
@Before(“execution(public int com.ArithmticCalculator.*(. .))”)
(JoinPoint joinPoint)

-@After 后置通知 (注意在后置通知中不能访问目标方法执行的结果) 无论是否异常
@After(“execution(public int com.ArithmticCalculator.*(. .))”)
(JoinPoint joinPoint)

-@AfterReturning 返回通知,在方法返回结果之后执行
@AfterReturning(value=“execution(public int com.ArithmticCalculator.add(int i,int j))”,
returning=”result”)
(JoinPoint joinPoint,Object result)

-@AfterThrowing 异常通知,在方法抛出异常之后,可以访问到异常对象,且可以指定在出现特定异常时再执行通知
@AfterThrowing(value=“execution(public int com.ArithmticCalculator.add(int i,int j))”,
throwing=”ex”)
(JoinPoint joinPoint,Exception ex)

-@Around 环绕通知,环绕着方法执行
需要携带ProceedingJoinPoint类型的参数
环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法

//@Component
//@Aspect
public class ServiceLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);

    @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        // 记录格式:用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        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));
    }
}

要获得用户IP可通过request对象获取,request对象可通过RequestContextHolder对象获取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值