文章目录
# 统一处理异常
SpringBoot 有统一处理异常的方法
把异常放到特定的路径下 只需要把错误页面放在templates/error下 错误页面的名称要和错误类型码一致
当出现对应类型错误的时候,springboot就会自动跳到相应的页面。
Spring处理异常 记录日志
记录日志后需要手动跳转到服务器发生错误页面
@RequestMapping(value = "/error",method = RequestMethod.GET)
public String getErrorPage()
{
return "/error/500";
}
Spring处理Controller请求
不需要对某个Controller进行处理,可以处理所有Controller请求
//扫描包含Controller注解的方法
@ControllerAdvice(annotations = Controller.class)
public class ExceptionAdvice {
//实例化日志
private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
//该方法会在Controller发生异常后被调用,用来捕获异常,记录日志
//传入request和response对象。用于获取请求方式和返回信息
@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());
}
//判断浏览器请求的是页面还是异步请求返回,JSON字符串
//固定技巧 通过reques获取
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的术语
- Target 处理需求的目标;
- Aspect(方面,切面) 封装业务需求的组件 ;
- 利用框架把Aspect织入到需要处理的需求目标上;
- 编译时织入,程序运行效率高,但可能因为条件不足导致一些错误;运行时织入效率低
- 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会采用此种方式,在子类实例中织入代码。
Spring AOP 应用和五类通知
–先设置切入点 表达式execution + 返回类型(*) +切入对象路径 + 对象方法 +(…)所有的参数
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut() {
}
//1、在连接点开始执行
@Before("pointcut()")
//2、在连接点返回之后执行
@AfterReturning("pointcut()")
//3、在连接点之后执行
@After("pointcut()")
//4、在连接点抛异常之后执行
@AfterThrowing("pointcut()")
//5、在连接点之前和之后执行
//ProceedingJoinPoint joinPoint 是连接点
//joinPoint.proceed(); 调用原始对象方法
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before");
Object obj = joinPoint.proceed();
System.out.println("around after");
return obj;
}
统一记录日志
@Component
@Aspect
public class ServiceLogAspect {
private static final Logger logger= LoggerFactory.getLogger(ServiceLogAspect.class);
//声明切点
@Pointcut("execution(* com.js.community.service.*.*(..))")
public void pointcut()
{
}
//在连接点方法执行器执行
@Before("pointcut()")
public void before(JoinPoint joinPoint)
{
// 用户[1.2.3.4],在[xxx],访问了[com.js.community.service.xxx()].
//获取用户ip 通过request工具类
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));
}
}
DEBUG
@Aspect 找不到该注解 导入依赖
<!--AOP依赖包-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>