jfinal框架 事务配置 及 异常统一拦截处理

jfinal框架 事务配置 及 统一异常拦截处理

新接手的项目用了jfinal框架,发现系统有很多地方没有完善,例如事务配置方面,表单提交失败后,脏数据保存到数据库里;controller层返回json数据每个方法都加入try catch,有很多重复性代码,为了使得代码更加的简洁,着手优化一下系统代码,jfinal官方文档场景应用例子太少了。话不多说,动手就是干。。

一、声明式事务配置

在JFinalConfig类里增加配置

public void configInterceptor(Interceptors me) {
       me.add(new SessionInViewInterceptor());

       //自定义异常拦截器
       me.add(new ExceptionInterceptor());

       // 声明式事务 方法(可以通过正则 或者 直接方法名称)
       me.add(new TxByMethodRegex("(.*save.*|.*update.*|.*start.*|.*do.*|.*apply.*|.*del.*)"));
       //me.add(new TxByMethods("save","update"));
   }

网上有些人说在方法上面加上注解@Before(Tx.class),但我在项目中没有写上,可以实现出错时事务回滚。


它的原理过程:

  1. 建立数据库连接;
  2. 设置事务隔离级别;
  3. 设置事务手动提交;
  4. 反射机制调用切入点方法;
  5. 事务提交;
  6. 若事务失败,就回滚。
public void intercept(Invocation inv) {
    Config config = getConfigWithTxConfig(inv);
    if (config == null)
        config = DbKit.getConfig();
    
    Connection conn = config.getThreadLocalConnection();
    // 下面这段支持嵌套事务,可以忽略不看
    if (conn != null) {
        try {
            if (conn.getTransactionIsolation() < getTransactionLevel(config))
                conn.setTransactionIsolation(getTransactionLevel(config));
            inv.invoke();
            return ;
        } catch (SQLException e) {
            throw new ActiveRecordException(e);
        }
    }
    
    Boolean autoCommit = null;
    try {
        // 1. 建立数据库连接
        conn = config.getConnection();
        autoCommit = conn.getAutoCommit();
        config.setThreadLocalConnection(conn);
        // 2. 设置事务隔离级别
        conn.setTransactionIsolation(getTransactionLevel(config));    // conn.setTransactionIsolation(transactionLevel);
        // 3. 设置事务手动提交
        conn.setAutoCommit(false);
        // 4. 反射机制调用 savePost()
        inv.invoke();
        // 5. 事务提交
        conn.commit();
    } catch (NestedTransactionHelpException e) {
        if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
        LogKit.logNothing(e);
    } catch (Throwable t) {
        // 6. 若有异常就回滚
        if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
        throw t instanceof RuntimeException ? (RuntimeException)t : new ActiveRecordException(t);
    }
    finally {
        try {
            if (conn != null) {
                if (autoCommit != null)
                    conn.setAutoCommit(autoCommit);
                conn.close();
            }
        } catch (Throwable t) {
            LogKit.error(t.getMessage(), t);    // can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
        }
        finally {
            config.removeThreadLocalConnection();    // prevent memory leak
        }
    }
}

二、全局异常拦截器


import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.ucap.base.exception.NormalException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

/**
 * 全局异常处理拦截器
 *
 */
public class ExceptionInterceptor implements Interceptor {

    private static final Logger log = LoggerFactory.getLogger(ExceptionInterceptor.class);

    @Override
    public void intercept(Invocation inv) {
        Controller controller = inv.getController();
        HttpServletRequest request = controller.getRequest();
        try {
            inv.invoke();
        } catch (Exception e) {
            //输入到日记文件
            doLog(inv, e);
            //判断是否ajax请求
            String header = request.getHeader("X-Requested-With");
            boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header);
            String msg = formatException(e);
            if (isAjax) {
                msg = new StringBuilder().append("{\"status\":\"0\",\"message\":\"")
                        .append(msg).append("\"}").toString();
                controller.renderJson(msg);
            } else {
                String redirctUrl = request.getHeader("referer");
                if (StringUtils.isBlank(redirctUrl)) {
                    redirctUrl = request.getRequestURI();
                }
                controller.setAttr("message", msg);
                controller.setAttr("redirctUrl", redirctUrl);
                controller.render("/webpage/pc/failed.ftl");
            }
        } 

    }

    private void doLog(Invocation ai,Exception e) {
        //开发模式
        if(JFinal.me().getConstants().getDevMode()){
            e.printStackTrace();
        }
        //业务异常不记录
        //if( e instanceof NormalException) return;
        StringBuilder sb =new StringBuilder("\n---全局异常处理拦截---\n");
        sb.append("Controller:").append(ai.getController().getClass().getName()).append("\n");
        sb.append("Method:").append(ai.getMethodName()).append("\n");
        sb.append("Exception Type:").append(e.getClass().getName()).append("\n");
        sb.append("Exception Details:");
        log.error(sb.toString(), e);
    }

    private static String formatException(Exception e){
        String message = null;
        Throwable ourCause = e;
        while ((ourCause = e.getCause()) != null) {
            e = (Exception) ourCause;
        }
        String eClassName = e.getClass().getName();
        //一些常见异常提示
        if("java.lang.NumberFormatException".equals(eClassName)){
            message = "请输入正确的数字";
        }else if (e instanceof NormalException || e instanceof RuntimeException) {
            message = e.getMessage();
            if(StringUtils.isBlank(message))message = e.toString();
        }

        //获取默认异常提示
        if (StringUtils.isBlank(message)){
            message = "系统繁忙,请稍后再试";
        }
        //替换特殊字符
        message = message.replaceAll("\"", "'");
        return message;
    }

}

NormalException 为业务异常类,继承RuntimeException

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值