项目-12-统一处理异常和日志

统一处理异常

@ControllerAdvice

用于修饰类,表示该类是Controller的全局配置类。
在此类中,可以对Controller进行如下三种全局配置:
异常处理方案、绑定数据方案、绑定参数方案。

@ExceptionHandler—异常处理方案

用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。

@ModelAttribute—绑定数据方案(想象下Page类被自动封装进Model里)

用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。

@DataBinder—绑定参数方案(想象下Page类的使用)

用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。

在这里插å¥å›¾ç‰‡æè¿°

其中,数据层和业务层产生异常时,均会向上抛出异常,最终都会在表现层被统一处理。

SpringBoot自动处理方式

1.把报错的错误码作为页面名放到如下目录下[templates],当报出来相关错误会自动显示报错的页面。
在这里插入图片描述

@ControllerAdvice和@ExceptionHandler处理异常

1.把报错的错误码作为页面名放到如下目录下[templates]

2.写一个跳转到处理页面的controller,这里在HomeController里写

    @RequestMapping(path = "/error",method = RequestMethod.GET)
    public String getErrorPage(){
        return "/error/500";
    }

3.在controller包下新建advice包并创建处理异常类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@ControllerAdvice(annotations = Controller.class) //表示只处理带有Controller注解的类
public class ExceptionAdvice {
    private static final Logger logger = LoggerFactory.getLogger(HandleException.class);
    /**
     * 处理所有的异常
     * @param e 异常
     * @param request 用于判断请求是请求页面还是json字符串
     * @param response 用于向浏览器返回页面或json字符串
     */
    @ExceptionHandler({Exception.class})
    public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
        //1.首先确定服务器发送异常
        logger.error("服务器发送异常"+e.getMessage());
        //2.获取异常,将每条异常进行遍历
        StackTraceElement[] elements = e.getStackTrace();
        for (StackTraceElement element : elements) {
            logger.error(element.toString());
        }

        //3.判断请求是普通请求,还是异步请求
        String header = request.getHeader("x-requested-with");  // 获取请求头的请求方式
        if (header.equals("XMLHttpRequest")){
            //3.1请求是异步请求
            response.setContentType("application/plain;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(CommunityUtil.getJSONString(1,"服务器异常"));
        }else {
            //3.1请求是普通请求
            response.sendRedirect(request.getContextPath() + "/error");
        }
    }
}

统一记录日志

需求:对所有的service记录日志

AOP

AOP的概念

  • Aspect Oriented Programing,即面向方面(切面)编程。
  • AOP是一种编程思想,是对OOP的补充,可以进一步提高编程的效率。

在这里插å¥å›¾ç‰‡æè¿°

在这里插å¥å›¾ç‰‡æè¿°

Target:已处理完业务逻辑的代码为目标对象
Joinpoint:目标对象上有很多地方能被织入代码叫连接点
Pointcut:切点声明到底织入到哪些位置
Advice:通知声明到底要处理什么样的逻辑

AOP的实现

AspectJ

AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法。

AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。

功能强大。但是需要学习新的语言,使用较复杂。

Spring AOP

Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。

Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点。

Spring支持对AspectJ的集成。

Spring AOP

JDK动态代理

  • Java提供的动态代理技术,可以在运行时创建接口的代理实例。
  • Spring AOP默认采用此种方式,在接口的代理实例中织入代码。

CGLib动态代理

  • 采用底层的字节码技术,在运行时创建子类代理实例。
  • 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。
示例:

1.新建aspect包写一个AlphaAspect类

@Component
@Aspect
public class AlphaAspect {
	// 切点:切点声明到底织入到哪些位置 
   // com.guo.community.service. 表示在service包下 .* 所有组件 .*所有方法 (..) 所有参数 所有返回值
    @Pointcut("execution(* com.guo.community.service.*.*(..))")
    //方法名字随便取
    public void pointcut(){

    }
/*    @Before("pointcut()")   // 在连接点开始前织入
    public void before(){
        System.out.println("before"); 
    }
    @After("pointcut()")  // 在连接点后前织入
    public void after(){
        System.out.println("after");
    }
    @AfterReturning("pointcut()")   // 在返回值后开始前织入
    public void afterReturning(){
        System.out.println("afterReturning");
    }
    @AfterThrowing("pointcut()")  // 在异常时织入
    public void afterThrowing(){
        System.out.println("afterThrowing");
    }*/ 
    @Around("pointcut()")  // 在连接点前后织入
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("aroundBefore");
        Object obj = joinPoint.proceed();
        System.out.println("aroundAfter");
        return obj;
    }
}

@Aspect:切面。表示一个横切进业务的一个对象。它里面包含切入点(Pointcut)和Advice(通知)。
@Pointcut:切入点。表示需要切入的位置,比如某些类或者某些方法,也就是先定一个范围。
@Before:Advice(通知)的一种,切入点的方法体执行之前执行。
@Around:Advice(通知)的一种,环绕切入点执行也就是把切入点包裹起来执行。
@After:Advice(通知)的一种,在切入点正常运行结束后执行。
@AfterReturning:Advice(通知)的一种,在切入点正常运行结束后执行,异常则不执行
@AfterThrowing:Advice(通知)的一种,在切入点运行异常时执行

统一日志:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;

//统一日志管理
@Component
@Aspect
public class ServiceLogAspect {

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

    //切入点:service下的所有方法和属性
    @Pointcut("execution(* com.guo.community.service.*.*(..))")
    public void pointcut(){}

    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        //日志格式:
        //用户[ip]在[**时间]访问了[**方法]
        //1.获取用户的ip:通过request获取
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = request.getRemoteHost();
        //2.获取时间
        String nowTime = new SimpleDateFormat("yyyy-MM-dd, HH-mm-ss").format(new Date());
        //3.方法名称
        String name = joinPoint.getSignature().getName();
        //4.访问路径
        String path = joinPoint.getSignature().getDeclaringTypeName();
        //5.拼接
        String target = name + "." + path;
        //6.返回日志
        logger.info(String.format("用户[%s]在[%s]访问了[%s]",ip,nowTime,target));
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值