文档章节

解惑JSP EL表达式 BigInteger类型除法运算导致的结果异常

无忌
 无忌
发布于 2017/06/02 17:24
字数 1002
阅读 320
收藏 0

1.发现问题

 数据库表有2个 int 类型字段,现在需要合并2个字段保留2位小数显示。
之前只展示一个字段的时候,我们的做法如下:

<fmt:formatNumber pattern="0.00" value="${A / 100}"/>

当 A = 50   页面显示结果 0.50

现在查询的时候 将2个int 字段 合并查询以  select (A+B) as C  方式 :

<fmt:formatNumber pattern="0.00" value="${C / 100}"/>

当 C = 50   页面显示结果 1.00

2.定位问题

将后台值直接在页面返回,显示没错:

通过在页面直接返回:
${C}
当 C = 50 没错

直接在JSP页面上手动赋值,正常显示:

%
        Integer A = 50;
        request.setAttribute("A", A);
%>

<fmt:formatNumber pattern="0.00" value="${A / 100}"/>

页面显示 0.50

手动赋值没问题,首先排除问题fmt 格式化问题,因为一直都是这么格式化。

项目其他都没改,只是在SQL中合并了2个字段返回,开始猜想应该是合并返回的导致的问题。

立马调试代码,项目技术使用: SpringBoot + JPA ,查询结果未做转化,直接返回前端

通过调试基本确定问题原因 :数据库中2个int 字段累加返回 在java中自动会变为 BigInteger. 

进一步确认问题,手动在页面进行测试:

<%
        BigInteger A = BigInteger.valueOf(50);
        request.setAttribute("A", A);
%>

<fmt:formatNumber pattern="0.00" value="${A / 100}"/>

页面显示 1.00

显示结果有误,问题完全定位。


3. 跟踪EL表达式源码,进一步了解问题原因


通过在tomcat/lib 下 el 依赖包抽取几个核心类:

下面列出几个进行除法运算的类:

AstDiv.java

//EL 表达式2个数相除
public final class AstDiv
{

  public AstDiv(){

  }
  
  public Object getValue(Object obj0, Object obj1)
  {
    return ELArithmetic.divide(obj0, obj1);
  }

}

ELArithmetic.java  计算核心类

/**
 * A helper class of Arithmetic defined by the EL Specification
 * @author Jacob Hookom [jacob@hookom.net]
 */
public abstract class ELArithmetic {

  public static final BigDecimalDelegate BIGDECIMAL = new BigDecimalDelegate();

  public static final BigIntegerDelegate BIGINTEGER = new BigIntegerDelegate();

  public static final DoubleDelegate DOUBLE = new DoubleDelegate();

  public static final LongDelegate LONG = new LongDelegate();

    //除法
    public static final Number divide(final Object obj0, final Object obj1) {
    if (obj0 == null && obj1 == null) {
      return ZERO;
    }

    final ELArithmetic delegate;
    if (BIGDECIMAL.matches(obj0, obj1)) //判断2个参数是否有一个为BigDecimal类型,如果存在就委托 BigDecimalDelegate 计算
      delegate = BIGDECIMAL;
    else if (BIGINTEGER.matches(obj0, obj1)) //判断2个参数是否有一个为BIGINTEGER类型,如果存在就委托 BigDecimalDelegate 计算
      delegate = BIGDECIMAL;
    else
      delegate = DOUBLE; //否则就委托 DoubleDelegate计算

    Number num0 = delegate.coerce(obj0);
    Number num1 = delegate.coerce(obj1);

    return delegate.divide(num0, num1);
  }

 public static final class BigIntegerDelegate extends ELArithmetic {

    @Override
    protected Number add(Number num0, Number num1) {
      return ((BigInteger) num0).add((BigInteger) num1);
    }

    @Override
    protected Number coerce(Number num) {
      if (num instanceof BigInteger)
        return num;
      return new BigInteger(num.toString());
    }

    @Override
    protected Number coerce(String str) {
      return new BigInteger(str);
    }

    //
    @Override
    protected Number divide(Number num0, Number num1) {
      return (new BigDecimal((BigInteger) num0)).divide(new BigDecimal((BigInteger) num1), BigDecimal.ROUND_HALF_UP);
    }

    @Override
    protected Number multiply(Number num0, Number num1) {
      return ((BigInteger) num0).multiply((BigInteger) num1);
    }

    @Override
    protected Number mod(Number num0, Number num1) {
      return ((BigInteger) num0).mod((BigInteger) num1);
    }

    @Override
    protected Number subtract(Number num0, Number num1) {
      return ((BigInteger) num0).subtract((BigInteger) num1);
    }

    @Override
    public boolean matches(Object obj0, Object obj1) {
      return (obj0 instanceof BigInteger || obj1 instanceof BigInteger);
    }
  }

}

当参数存在BigInteger 会委托 BigDecimalDelegate  进行计算,我们来看看关键计算除法的代码:

public static final class BigDecimalDelegate extends ELArithmetic {
  
    //加法
    @Override
    protected Number add(Number num0, Number num1) {}

    @Override
    protected Number coerce(Number num) {}

    @Override
    protected Number coerce(String str) {}

    //除法 关键:BigDecimal.ROUND_HALF_UP, 会对结果四舍五入 当结果小数位>=0.5,会入1 
    @Override
    protected Number divide(Number num0, Number num1) {
      return ((BigDecimal) num0).divide((BigDecimal) num1,
              BigDecimal.ROUND_HALF_UP);
    }
    //减法
    @Override
    protected Number subtract(Number num0, Number num1) {}

    @Override
    protected Number mod(Number num0, Number num1) {}

    @Override
    protected Number multiply(Number num0, Number num1) {}

    @Override
    public boolean matches(Object obj0, Object obj1) {
      return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
    }
  }

问题原因:关键:BigDecimal.ROUND_HALF_UP, 会对结果四舍五入 当结果小数位>=0.5,会入1 

代码测试:

/**
 * Created by rain.wen on 2017/6/2.
 */
public class Test {

    public static void main(String[] args) {
        Test.testLongDiv();
        Test.testOneBigIntegerDiv();
    }

    public static void testLongDiv(){
        AstDiv astDiv = new AstDiv();
        Object obj0 = 50L;
        Object obj1 = 100L;
        System.out.println(" 50L/100L = " + astDiv.getValue(obj0, obj1));
    }

    public static void testOneBigIntegerDiv(){
        AstDiv astDiv = new AstDiv();
        Object obj0 = BigInteger.valueOf(50L);
        Object obj1 = 100L;
        System.out.println(" BigInteger.valueOf(50L)/100L = " + astDiv.getValue(obj0, obj1));
    }

}

结果:

 

代码地址:https://git.oschina.net/rainwen/my-web.git

参考:http://jinnianshilongnian.iteye.com/blog/1869706

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
无忌
粉丝 31
博文 32
码字总数 35038
作品 0
深圳
私信 提问
javaweb开发之EL和JSTL

一、EL 1.概述 EL 全名为Expression Language。 EL表达式可用在所有的HTML和JSP标签中作用是代替JSP页面中复杂的JAVA代码。 EL主要作用如下: (1)获取数据: EL表达式主要用于替换JSP页面中的...

小米米儿小
2013/12/05
0
0
JSP——Java Server Pages

简介 1. Jsp技术:Jsp是一种 html代码+ Java代码 + Jsp页面代码的技术,而且其不用像Servlet哪样需要配置访问路径。 2. Jsp执行原理:第一次访问服务器web项目中的jsp页面时,会首先把jsp页面...

江左煤郎
09/13
0
0
(JavaEE-09)JSP中的MVC与三层架构

JSP开发模式 SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式 JSP+JavaBean模式适合开发业务逻辑不太复杂的web应用...

_-Leon-_
2014/07/11
0
2
EL表达式和JSTL标签

EL表达式: EL表达式用于获取数据,在JSP页面中可使用${标识符}的形式,通知JSP引擎调用pageContext.findAttribute()方法,以标识符为关键字从各个域对象中获取对象。如果域对象中不存在标识...

晨曦之光
2012/05/16
174
0
Java程序员从笨鸟到菜鸟之(十九)EL表达式和JSTL

一:EL表达式: 1.定义:为了计算和输出存储在标志位置的Java对象的值,JSP2.0引入了一种简洁的语言。 2.基本格式:${表达式} 所有的EL都是以“${”开始,以“}”结尾 表达式与开始符和终结符...

长平狐
2012/11/12
258
0

没有更多内容

加载失败,请刷新页面

加载更多

三星Galaxy S10可能会配备TOF 3D摄像头

12月3日消息,据Phone Arena报道,三星Galaxy S10可能会配备TOF 3D摄像头。 Phone Arena报道称三星Galaxy S10一共有五颗摄像头(前置双摄+后置三摄),而5G版本的Galaxy S10后置四颗摄像头,...

问题终结者
28分钟前
8
0
fabric增删改查Mac

备份1.3版本,重新下载1.1版本到fabric文件夹 /opt/gopath/src/github.com/hyperledger/fabric -> /opt/gopath/src/github.com/hyperledger/fabric1.3 新建/opt/gopath/src/github.com/hype......

八戒八戒八戒
56分钟前
9
0
盘点愚人节各大网站彩蛋,谁最爱恶搞?

如今的愚人节俨然已是各品牌宣传了一个重要节日,同时,也成为了各大互联网科技企业凑热闹,比拼创意和策划的节日。跟小编一起看看有哪些有趣的策划吧! Google地图变成吃豆人游戏 每年愚人节...

临江仙卜算子
今天
6
0
Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

本文分析的是源码,所以至少读者要熟悉它们的接口使用,同时,对于并发,读者至少要知道 CAS、ReentrantLock、UNSAFE 操作这几个基本的知识,文中不会对这些知识进行介绍。Java8 用到了红黑树...

java菜分享
今天
6
0
玩手机与做实验

看过这样一个故事:说的是在二十世纪二十年代初的一个深夜,担任英国剑桥大学卡文迪许实验室主任的卢瑟福来实验室检查,发现一位学生还在做实验。卢瑟福就问他:“你上午做什么了?”学生回答...

Bob2100
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部