统一异常拦截、ioc apo实现操作记录、非法sql拦截

统一异常拦截

平时我们都会用try catch 或throw的方式去处理异常,统一异常拦截可以进行统一处理将后端的异常,将没有try,catch住的给拦截住,不用每次编码都进行try,catch,降低了开发的复杂度。

 如果不统一异常拦截,我们需要写很多try catch

实现

了解两个注解

@RestControllerAdvice

可以扫描到所有的controller,所有controller中都有@RestController这个注解

Spring在扫描容器的时候,将

类归到了Controller中,扫描Controller即可

 

@ExceptionHandler

扫描到Controller后报错了就会走这个注解下面的代码逻辑

创建config包

定义一个ExceptionAdvice类

package com.example.demo.config;

import com.example.emos.wx.exception.EmosException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 捕获全局异常,并做处理
 * @author wanglong
 * @ControllerAdvice 和 @RestControllerAdvice都是对Controller进行增强的,可以全局捕获spring mvc抛的异常。
 * RestControllerAdvice = ControllerAdvice + ResponseBody
 */
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {

    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public String exceptionHandler(Exception e){
        log.error("执行异常",e);
        if(e instanceof MethodArgumentNotValidException){
            MethodArgumentNotValidException exception= (MethodArgumentNotValidException) e;
            //返回精简消息(具体某个字段没有通过校验的原因)
            return exception.getBindingResult().getFieldError().getDefaultMessage();
        }
        else if(e instanceof MyException){
            MyException exception= (MyException) e;
            return exception.getMsg();
        }
        else if(e instanceof UnauthorizedException){
            return "你不具备相关权限";
        }
        else{
            return "后端执行异常";
        }
    }
}

异常分为系统异常(不可预知,例如:程序空指针 )和业务异常(可预知,例如:传参失败)

自己定义一个异常

extends RuntimeException

RuntimeException,运行时异常,在项目运行之后出错则直接中止运行,异常由JVM虚拟机处理。

package com.example.yiruo.user.util.exception;

/**
 * 业务异常
 * 
 * @author zs
 */
public final class ServiceException extends RuntimeException
{
    private static final long serialVersionUID = 1L;

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 错误提示
     */
    private String message;

    /**
     * 错误明细,内部调试错误
     *
     * 和 {@link CommonResult#getDetailMessage()} 一致的设计
     */
    private String detailMessage;

    /**
     * 空构造方法,避免反序列化问题
     */
    public ServiceException()
    {
    }

    public ServiceException(String message)
    {
        this.message = message;
    }

    public ServiceException(String message, Integer code)
    {
        this.message = message;
        this.code = code;
    }

    public String getDetailMessage()
    {
        return detailMessage;
    }

    @Override
    public String getMessage()
    {
        return message;
    }

    public Integer getCode()
    {
        return code;
    }

    public ServiceException setMessage(String message)
    {
        this.message = message;
        return this;
    }

    public ServiceException setDetailMessage(String detailMessage)
    {
        this.detailMessage = detailMessage;
        return this;
    }
}

运行后测试

 如果这里不加上log.error不输出e,控制台不会输出异常

 

aop切面实现操作记录(操作留痕)

用户在进行更新操作的时候形成的操作记录(修改密码等等)

实现原理:ioc+aop

切面本身是一个动作 ,将通知切入到切入点的过程

1.连接点:类当中有哪些方法可以被增强那么这些方法就是连接点

2.切入点:实际被增强的方法就是切入点

3.增强(通知):实质上被增强的逻辑部分被称为增强

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

定义注解

import java.lang.annotation.*;

/**
 * 自定义操作日志记录注解
 * 
 * @author wyh
 *
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{

    /**
     * 处理类型
     *
     * @return {@link String}
     */
    String handleType() default "";

    /**
     * 操作功能
     *
     * @return {@link String}
     */
    String czgn() default "";
}

 定义切点

@Aspect
@Component
@DependsOn("springFactoryUtils")
public class CzjlMark {



    // 定义切点
    @Pointcut("@annotation(com.shineyue.zzjg.config.annotation.SyMark)")
    public void annotationPointcut() {}
    // 切点执行前需要的操作
    @Before("annotationPointcut() && @annotation(syMark)")
    public void beforePointcut(JoinPoint joinPoint,SyMark syMark) {
        Object[] args = joinPoint.getArgs();
        CzjlMarkHandle czjlMarkHandle = CzjlMarkHandleMap.get(syMark.handle());
        czjlMarkHandle.Before(args,syMark);
    }
    // 相关接口特定逻辑,可以不用管
    @Around("annotationPointcut()")
    public Object Interceptor (ProceedingJoinPoint joinPoint)  {
        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;

    }

    /**
     * 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理),加入异常捕获目的是,若操作记录报错,不影响之前接口的返回值
     * @param joinPoint
     */
    @AfterReturning(returning="rvt",pointcut="@annotation(com.shineyue.zzjg.config.annotation.SyMark)")
    public void doAfterReturning(JoinPoint joinPoint,Object rvt) {
        JSONObject outJson = (JSONObject) rvt;
        try{
            if("200".equals(outJson.get("state"))){
                // 获取注解
                MethodSignature signature = (MethodSignature)joinPoint.getSignature();
                // 获取当前方法的注解对象
                SyMark syMark = signature.getMethod().getAnnotation(SyMark.class);
                CzjlMarkHandle czjlMarkHandle = CzjlMarkHandleMap.get(syMark.handle());



                Object[] args = joinPoint.getArgs();
                czjlMarkHandle.after(rvt,syMark,args);
            }
        }catch (Exception e){
            return;
        }

        }

定义抽象类

@Component
public abstract class CzjlMarkHandle {

    @Autowired
    ZzjgczjlService zzjgczjlService;

    /**
     * 在操作之前执行
     * @param object
     * @return
     */
    public abstract void Before(Object[] object, SyMark syMark);

    /**
     * 在操作之后执行
     * @param object
     * @return
     */
    public abstract void after(Object object,SyMark syMark,Object[] param);



}

加上@Log注解

 怎么让Spring知道@Log注解下两个参数的意义?

先定义一个service



import com.example.yiruo.user.annotation.Log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * 操作记录实现类
 *
 * @author 
 * @date 2022/12/03
 */
@Component
@Slf4j
public abstract class CzjlMarkService {

    /**
     * 之前
     *
     * @param args arg游戏
     */
    public  void before(Object[] args, Log loga){
        log.info("handle:{},入参:{}",loga.handleType(), args);
    };


    /**
     * 后
     *
     * @param args      arg游戏
     * @param signature 签名
     */
    public abstract void after(Object[] args, MethodSignature signature);

}

新建一个类,让新建的类去继承从而实现方法

@Component("bmcx")
@Slf4j
public class BmcxHandle extends CzjlMarkService {


    @Override
    public void after(Object[] args, MethodSignature signature) {
        DeptVo dept = (DeptVo) args[0];
        log.info("操作功能:{},操作值:{}",signature.getMethod().getAnnotation(Log.class).czgn(),String.valueOf(dept));
    }

在CzjlMark中加上

    // 通过注解传入的handle来确定走哪个具体的handle代码。
    private static Map<String, CzjlMarkHandle> CzjlMarkHandleMap = SpringFactoryUtils.getBeanMap(CzjlMarkHandle.class);

将所有的子类形成一个Map,用哪个子类就get哪个子类

非法sql拦截

当我们的sql语句没有where条件时,全表都会被更新,这样是有风险的

非法sql拦截主要拦截的是不带where的update和delete语句

 new一个MybatisPlusConfig 

@Configuration
public class MybatisPlusConfig {
  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
    return interceptor;
  }
}

加上配置后

 在进行sql语句执行的时候会被拦截住并提示不允许进行全表的更新

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值