文档章节

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

无忌
 无忌
发布于 2017/06/02 17:24
字数 1002
阅读 180
收藏 0
点赞 1
评论 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

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
无忌
粉丝 23
博文 27
码字总数 29345
作品 0
深圳
JSP 学习总结---学习笔记

什么是JSP 1)为什么说,Servlet是一个动态Web开发技术呢? Servlet是基于服务端的一种动态交互技术, HttpServletRequest表示客户端到服务端的对象 HttpServletResponse表示服务端到客户端的...

知止内明 ⋅ 04/18 ⋅ 0

JavaWeb12-HTML篇笔记(二)

Ø 为什么学习JSTL: JSTL和EL结合 替换页面中<%%> Ø JSTL版本: JSTL1.0 :不支持EL表达式. JSTL1.1 和 1.2 :支持EL表达式. Ø JSTL的标签库:包含了五类标签. core(核心标签),fmt(国际化标签)...

我是小谷粒 ⋅ 05/28 ⋅ 0

用idea创建maven项目时 遇到jsp页面EL表达式显示无效的解决办法。。。

maven创建项目时,web.xml头部声明默认是2.3,这个默认jsp关闭el表达式 建议使用第二种,省的每个页面都需要修改 1、页面头部添加 <%@page isELIgnored="false"%> 2、方法二更换头部声明为3...

喜欢敲 ⋅ 04/25 ⋅ 0

thymeleaf、freemaker、jsp、html的理解?

大家好最近突然有点困惑了,thymeleaf、freemaker、jsp、html,大学时学习的是jsp,我就以为jsp跟html一样是一种前端表现一种界面,后来接触了freemaker现在学习springboot有接触了thymeleaf...

樱木花道VS康 ⋅ 05/27 ⋅ 0

web项目中web.xml的作用

每个javaEE工程中都有web.xml文件,那么它的作用是什么呢?它是每个web.xml工程都必须的吗? 一个web中可以没有web.xml文件,也就是说,web.xml文件并不是web工程必须的。 web.xml文件是用来...

ChinaHYF ⋅ 04/27 ⋅ 0

挖洞经验 看我如何发现Paypal内部信息泄露漏洞

        本文我要分享的是,在Paypal网站manager.paypal.com上的某个页面存在“表达式注入“漏洞(Expression Language Injection),利用该漏洞我可以间接获取到Paypal系统的内部IP、...

FreeBuf ⋅ 今天 ⋅ 0

Servlet的一些细节--学习笔记

Servlet细节 1)浏览器访问的url-pattern只是一个符合格式的任意字符串,以/开头 2)一个Servlet的url-pattern可以是1个或多个,有二种形式; a).xx b)/xx/ 注意:/不能一起直接使用 3)/和....

知止内明 ⋅ 04/17 ⋅ 0

Servlet创建运行的过程--生命周期以及自动加载

前言 初学者像我这样的,刚开始学的时候一脸懵逼,为什么Servlet可以直接运行,明明没有入口啊 只是重写了Servlet中的方法。然后访问该地址怎么就直接运行了。 所以了解Serlvet的运行过程即生...

codingcoge ⋅ 06/12 ⋅ 0

JavaWeb开发比较重要的面试题

JavaWeb开发比较重要的面试题 1. 编码转换:怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串? 答:示例代码如下: String s1=”你好”; String s2=new String(s1.getBytes(“GB2312”...

xj_9264 ⋅ 05/06 ⋅ 0

Spring JMSTemplate 与 JMS 原生API比较

博客分类: JMS Spring 2.x JMSUtil与Spring JmsTemplate的对比 Author:信仰 Date:2012-4-20 未完待续,截止日期2012-4-20 从以下几方面比较JMSUtil和Spring JmsTemplate l 对JNDI的支持 ...

thinkyoung ⋅ 2014/12/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Nginx服务架构初探(四):nginx服务器的rewrite功能

nginx服务器的rewrite功能 1.nginx后端服务器组的配置 1>upstream name {…} name是给服务器组限的组名 2>server address [parameters]; address为服务器地址 parame......

余温灬未存 ⋅ 今天 ⋅ 0

layer.prompt使文本框为空的情况下也能点击确定

最近一直在使用layui,但是用到弹出层layer.prompt时,如果文本框是空的话点击确定没有反应,不能向下执行。 但是我又需要空值,看看我原来的代码。 123456789 layer.prompt...

孟飞阳 ⋅ 今天 ⋅ 0

Linux普通文件压缩工具gzip、Bzip2、xz

第六章 文件压缩和打包 6.1 压缩打包介绍 Linux环境常见压缩文件类型: .zip,.gz,.bz2,.xz, .tar.gz,.tar.bz2,.tar.xz 压缩打包的目的 方便文件传输 节省磁盘空间 减少传输花费的时间 ...

弓正 ⋅ 今天 ⋅ 0

移动弹窗基础知识浅析——IOS弹窗体系

摘要: 最为常见的【弹窗】反而是最“捉摸不定”的东西。各种类型的弹窗傻傻分不清楚,不知道在什么场景下应该用哪种弹窗。尤其是遇到“二次确认”等场景…… 因此,打算从头整理移动弹窗的基...

阿里云云栖社区 ⋅ 今天 ⋅ 0

zabbix短信报警统计以及报表展示

一、需求 由于我们的业务报警比较频繁,之前是针对每个报警进行具体处理,但是有时还会重复出现,或者后续处理有时忘记跟进等,因此进行报警短信的统计,可以针对一些问题与业务跟进,明确后...

o翡翠谷o ⋅ 今天 ⋅ 0

JNI 输出LOG

1、导入log头文件。在你使用的 .c/ .cpp 文件中,导入 log.h 头文件。 #include<android/log.h> 2、在android.mk 加上 LOCAL_LDLIBS := -llog 或 LOCAL_SHARED_LIBRARIES := liblog 3、定义L......

国仔饼 ⋅ 今天 ⋅ 0

主线程pthread_exit 作用

#include <iostream>#include <pthread.h>#include <unistd.h>using namespace std;#define NUM_THREADS 10void* say_hello(void* args){ int i = *((int*)args);/......

xxdd ⋅ 今天 ⋅ 0

崛起于Springboot2.X之Mybatis-xml方式操作mysql数据库(3)

序言:当第一篇讲道Mybatis的时候,只要使用过mybatis的java程序员100%都会知道这种方式,因为这是最广泛最全面的编写sql操作mysql数据库的方式,高级sql的编写往往通过xml方式,接下来进入正...

木九天 ⋅ 今天 ⋅ 2

移动弹窗基础知识浅析——IOS弹窗体系

摘要: 最为常见的【弹窗】反而是最“捉摸不定”的东西。各种类型的弹窗傻傻分不清楚,不知道在什么场景下应该用哪种弹窗。尤其是遇到“二次确认”等场景…… 因此,打算从头整理移动弹窗的基...

猫耳m ⋅ 今天 ⋅ 0

spring elasticsearch 2.4 date 日期

1.mappingPUT user_behavior { "mappings": { "user_behavior": { "properties": { "date": { "type": "createDate", ......

xiaomin0322 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部