json-lib反序列化精度丢失问题

最近在工作中,遇到一个问题,项目中某处使用了json-lib的2.4-jdk15版本.问题最终简化为 double amount = 6264583.33; String jsonString = "{\"pi\":" + amount + "}"; JSONObject jsonObject = JSONObject.fromObject(jsonString); System.out.println("转换前:" + jsonString); System.out.println("转换后:" + jsonObject); 这个值输出的将会是6264583.5 这个值.这个问题.先google一下,很快赵到了 http://sourceforge.net/p/json-lib/bugs/116/ 于是,大概问题知道了.是json-lib的一个bug,但是这个bug怎么来的呢.结合这个bug下面的评论和debug代码,先以pi这个例子,很快走到了. 可以看到json-lib走到了apache common-lang(2.5这个版本) 的NumberUtils.createNumber处,此时String的还是对的. 继续单步,来到这个方法里面 可以看到这里小数部分,整数部分也都还是对的.继续向下走.我擦.画风不太对.居然采用了先尝试float,发现没问题.然后就继续尝试double,我擦.直接数据就丢失了呀.. 好吧..那么现在问题就便成了更简单的一个问题. 使用NumberUtils.createNumber 的bug.在bug issue里,有人提到.这个bug,apache官方已知.好的. https://issues.apache.org/jira/browse/LANG-693 然后在这里有官方的一次修复,修复记录在这里.3.2版本已经修复. http://svn.apache.org/viewvc?view=revision&revision=1484263 可以看到是对小数部分的长度进行了判断.如果小于7位,就用float转换,如果大于7,小于16,就用double,如果还大,就用BigDecimal. n = org.apache.commons.lang3.math.NumberUtils.createNumber("3.14159265358"); System.out.println("lang3_createNumber_3.14159265358---->" + n + "->精度正常"); 于是我继续debug,看common-lang3的修复情况,好像确实是修复了.但是对于我出现的问题1.6264583.33 这个数字,还是出现了精度丢失,因为这里小数部分小于7位,所以尝试使用float转换,直接丢失精度 修复不完善.. 于是提个bug :https://issues.apache.org/jira/browse/LANG-1187 等回复. 继续.公司内部一般使用fastjson,那么如果我使用fastjson,有问题吗? 发现没有问题. Object o = com.alibaba.fastjson.JSONObject.parse("3.14159265358"); System.out.println("fastjson_createNumber_3.14159265358---->" + o + "->精度正常"); o = com.alibaba.fastjson.JSONObject.parse("6264583.33"); System.out.println("fastjson_createNumber_6264583.33---->" + o + "->精度正常"); 可以看到,这里做转换的时候传递了一个是否是bigdecimal的标识.而这个标识默认是开启的.而且即使不开启.. 最坏的情况也是个double.所以数据不会丢失. 再顺便说一下,double的6264583.33 为什么转换到float会精度丢失,先看一下浮点数在计算机中怎么表示的 找到一张图,这是double的标识和浮点数的计算. 而浮点数则是32位,1位符号位,8位幂,23位尾数,看测试代码 //double标识测试 double d = 6264583.33d; long l = Double.doubleToLongBits(d); System.out.println(Long.toBinaryString(l)); //float想要表示这个数字 float f = 6264583.33f; int value = Float.floatToIntBits(f); System.out.println(Integer.toBinaryString(value)); //double表示这个值 d = 6264583.5d; l = Double.doubleToLongBits(d); System.out.println(Long.toBinaryString(l)); 输出结果(做一下分割对齐) 1 00000101010 111111001011100000111010101000111101011100001010010 1 00101010 1111110010111000001111 1 00000101010 111111001011100000111100000000000000000000000000000 注意看,第一行是6264583.33的double表示.而同样想要用float表示这个数字,发现幂,符号位,都是对的.但是因为尾数只有23位,所以四舍五入,将完整double的后几位进位1,变成了这个二进制表示法,这时候已经不准确了, 而这个数字呢.看第三行,会发现实际上是6264583.5的精确值表示.尾数位0都是可以省略的,因为按照公式计算也没啥作用. 如有问题,欢迎评论讨论. 附录: 完整的测试代码 public class App { public static void main(String[] args) { //http://sourceforge.net/p/json-lib/bugs/116/ //2.4版本有问题 double pi = 3.14159265358; String jsonString = "{\"pi\":" + pi + "}"; JSONObject jsonObject = JSONObject.fromObject(jsonString); System.out.println("转换前:" + jsonString); System.out.println("转换后:" + jsonObject); double amount = 6264583.33; jsonString = "{\"pi\":" + amount + "}"; jsonObject = JSONObject.fromObject(jsonString); System.out.println("转换前:" + jsonString); System.out.println("转换后:" + jsonObject); //测试2.4版本引入的lang,这里面 Number n = org.apache.commons.lang.math.NumberUtils.createNumber("3.14159265358"); System.out.println("lang2_createNumber_3.14159265358---->" + n + "->精度丢失"); n = org.apache.commons.lang.math.NumberUtils.createNumber("6264583.33"); System.out.println("lang2_createNumber_6264583.33---->" + n + "->精度丢失"); //测试3.4版本,里面是根据小数部分的长度,选择是否使用float还是double,当小数部分大于7的时候,就会使用double n = org.apache.commons.lang3.math.NumberUtils.createNumber("3.14159265358"); System.out.println("lang3_createNumber_3.14159265358---->" + n + "->精度正常"); //这种情况就会有问题,虽然小数部分是33,两位,但是实际上是个浮点数.所以还会丢失精度 n = org.apache.commons.lang3.math.NumberUtils.createNumber("6264583.33"); System.out.println("lang3_createNumber_6264583.33---->" + n + "->精度丢失"); //测试fastjson Object o = com.alibaba.fastjson.JSONObject.parse("3.14159265358"); System.out.println("fastjson_createNumber_3.14159265358---->" + o + "->精度正常"); o = com.alibaba.fastjson.JSONObject.parse("6264583.33"); System.out.println("fastjson_createNumber_6264583.33---->" + o + "->精度正常"); //double标识测试 double d = 6264583.33d; long l = Double.doubleToLongBits(d); System.out.println(Long.toBinaryString(l)); //float想要表示这个数字 float f = 6264583.33f; int value = Float.floatToIntBits(f); System.out.println(Integer.toBinaryString(value)); //double表示这个值 d = 6264583.5d; l = Double.doubleToLongBits(d); System.out.println(Long.toBinaryString(l)); } }

2015-11-25 · 2 min · bystander

zookeeper伪集群部署

zookeeper是用来管理分布式环境的系统主要用来服务发现,配置管理,同步.大致原理是zookeeper 自身集群的每个节点都维护这一个目录树,内容相同,每个节点的数据一致性由zookeeper自身的算法来解决.下篇尝试.zookeeper本篇主要说明如果部署zookeeper的分布式环境. 下载 zookeeper由apache在管理,下载地址:http://www.apache.org/dyn/closer.cgi/zookeeper/.下载完成后,随便放个目录好了.. 配置 本次创建3个节点. 1 . 存储目录准备 首先给每个伪节点创建一个目录.用来存储每个节点保存的目录信息.真实的分布式环境将对应在不同的机器上. 这里我在D:\zookeeper,创建三个目录,分别是zk1,zk2,zk3. 然后为每个集群编写一个myid文件,标识集群id 2 . 启动配置文件 下载完成后,在conf目录会看到由一个zoo_sample.cfg实例配置文件,我们可以以这个为模板.来为分布式环境的每个zookeeper节点配置一个节点的数据目录,端口.其他节点的信息等. 我们在conf目录例创建三个配置文件,分别为zk1.cfg,zk2.cfg,zk3.cfg; 里面的值 zk1.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=D:/zookeeper/zk1 clientPort=2181 server.1=127.0.0.1:2888:3888 server.2=127.0.0.1:2889:3889 server.3=127.0.0.1:2890:3890 zk2.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=D:/zookeeper/zk2 clientPort=2182 server.1=127.0.0.1:2888:3888 server.2=127.0.0.1:2889:3889 server.3=127.0.0.1:2890:3890 zk1.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=D:/zookeeper/zk3 clientPort=2183 server.1=127.0.0.1:2888:3888 server.2=127.0.0.1:2889:3889 server.3=127.0.0.1:2890:3890 这里的server.1.2.3这就是每个机器对应的myid的值. server.1=127.0.0.1:2888:3888解释一下这条配置.前面的2888是各个节点用来互相交流.选取leader的端口.后面这个端口,3888是各个节点用来和leader沟通的节点.而clientPort 是开放出去,等待客户端连接的端口. 启动 分别启动三个实例,在zookeeper的安装目录下.进如bin目录,复制三个zkServer.cmd 文件,要是linux就不用这么麻烦了.. 分别加上一行 set ZOOCFG=../conf/zk1.cfg 最终这个文件像这样 setlocal call "%~dp0zkEnv.cmd" set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain set ZOOCFG=../conf/zk1.cfg echo on java "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* endlocal 然后直接双击启动zkServer1.cmd,zkServer2.cmd,zkServer3.cmd 刚启动第一个之后,你会看到有报错,是zookeeper进行选举的时候报错的.因为第一个zk节点.从自己的启动配置里,知道还有两个节点,于是尝试连接.但是连接不上,再启动另外两个.都启动后,报错消失 然后在D:\zookeeper中可以看到由数据写入. 测试 启动bin目录的zkCli.cmd,自动连接本机的2181端口.也可以自己指定 zkCli.cmd –server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 对客户端来说.连接上了一个列表之后,如果一台挂了,并不会影响.系统依旧可以运行. 然后执行一些简单的操作 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容。 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据。 创建文件,并设置初始内容: create /zktest “test” 创建一个新的 znode节点“ zk ”以及与它关联的字符串。 获取文件内容: get /zktest 确认 znode 是否包含我们所创建的字符串。 修改文件内容: set /zktest “zkbak” 对 zk 所关联的字符串进行设置。 删除文件: delete /zktest 将刚才创建的 znode 删除。 退出客户端: quit 帮助命令: help 可以关掉一个服务器,会发现客户端依然正常.可以执行get set等操作.

2015-11-22 · 1 min · bystander

Java可重入锁学习笔记

前几天被前辈问到这个可重入锁,结果忘掉了.于是抽空整个了解一下 目录 什么是可重入锁 为什么要可重入 如何实现可重入锁 有不可重入锁吗 demo代码展示 参考文章 1 . 什么是可重入锁 锁的概念就不用多解释了,当某个线程A已经持有了一个锁,当线程B尝试进入被这个锁保护的代码段的时候.就会被阻塞.而锁的操作粒度是"线程",而不是调用(至于为什么要这样,下面解释).同一个线程再次进入同步代码的时候.可以使用自己已经获取到的锁,这就是可重入锁 java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入的 2 . 为什么要可重入 如果线程A继续再次获得这个锁呢?比如一个方法是synchronized,递归调用自己,那么第一次已经获得了锁,第二次调用的时候还能进入吗? 直观上当然需要能进入.这就要求必须是可重入的.可重入锁又叫做递归锁,再举个例子. public class Widget { public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething();//若内置锁是不可重入的,则发生死锁 } } 这个例子是java并发编程实战中的例 子.synchronized 是父类Widget的内置锁,当执行子 类的方法的时候,先获取了一次Widget的锁,然后在执行super的时候,就要获取一次,如果不可重入,那么就跪了. 3 . 如何实现可重入锁 为每个锁关联一个获取计数器和一个所有者线程,当计数值为0的时候,这个所就没有被任何线程只有.当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数值置为1,如果同一个线程再次获取这个锁,技术值将递增,退出一次同步代码块,计算值递减,当计数值为0时,这个锁就被释放. ReentrantLock里面有实现 4 . 有不可重入锁吗 这个还真有.Linux下的pthread_mutex_t锁是默认是非递归的。可以通过设置PTHREAD_MUTEX_RECURSIVE属性,将pthread_mutex_t锁设置为递归锁。如果要自己实现不可重入锁,同可重入锁,这个计数器只能为1.或者0,再次进入的时候,发现已经是1了,就进行阻塞.jdk里面没有默认的实现类. 5 . demo代码展示 5.1 内置锁的可重入 public class ReentrantTest { public void method1() { synchronized (ReentrantTest.class) { System.out.println("方法1获得ReentrantTest的内置锁运行了"); method2(); } } public void method2() { synchronized (ReentrantTest.class) { System.out.println("方法1里面调用的方法2重入内置锁,也正常运行了"); } } public static void main(String[] args) { new ReentrantTest().method1(); } } 5.2 lock对象的可重入 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockTest { private Lock lock = new ReentrantLock(); public void method1() { lock.lock(); try { System.out.println("方法1获得ReentrantLock锁运行了"); method2(); } finally { lock.unlock(); } } public void method2() { lock.lock(); try { System.out.println("方法1里面调用的方法2重入ReentrantLock锁,也正常运行了"); } finally { lock.unlock(); } } public static void main(String[] args) { new ReentrantLockTest().method1(); } } 5.3 不同线程不可访问同一锁 ...

2015-11-20 · 2 min · bystander

jenkins相关资料

jenkins相关资料 jenkins的前身是hudson,是为了做持续集成测试而诞生的框架. 简单说就是把代码流配置上去,然后该框架就能根据设定的时间或其他方式不间断的执行测试用例.并给出报告.这样就可以随时掌控代码质量.支持执行shell命令.支持短信通知等等. jenkins,首先从http://jenkins-ci.org/官方下载war包,然后有两种方式可以本地部署,一种直接执行 java -jar hudson.war,然后在本地的8080端口访问就可以了.另一种是我们希望部署在容器上,那么可以部署到tomcat,jetty等等.直接拷贝war包到对应的目录即可. 部署成功后,可以新建job,然后配置svn流,配置build时间,配置一下build之前的动作,配置一下各种命令,执行完成后通知等等就可以了. 最近因为有个功能感觉通过开发jenkins插件的方式会更加方便,周末大量读了一些文档,能找到的都是helloworl.摸索了一下,已经写出一个小的demo了,后面改进一下,.就ok了.先简单把这个过程中收集的资料整理一下.基本都看过觉得还不错的,官方文档不给力.下周希望可以写完,然后分享出来. jenkins入门文档 The hudson book Writing-first-hudson-plugin Hudson插件开发简介 Hudson插件开发入门体验 插件界面设计设计的各种tag介绍 官方插件开发 UI开发jelly介绍 jenkins入门(itech)

2014-09-21 · 1 min · bystander

Spring揭秘-23章,Spring MVC初体验

 鸟瞰Spring MVC与其他请求驱动的Web框架思路类似。org.springframework.web.servlet.DispatcherServlet就是Spring mvC中的Front Controller。负责处理请求,但是不针对具体的处理逻辑。而是委派给下一级的控制器,也就是org.springframework.web.servlet.mvc.Controller去执行。 DispatcherServlet的处理流程如下:1.HandlerMappingDispathcherServlet是FrontController,所以他服务于一组Web请求,需要在web.xml中配置。DispathcherServlet需要自己处理请求和处理之前的对应关系,比如根据参数对应到不同的Controller上。为了更加方便的处理映射的匹配,引入HandlerMapping用来获取需要处理请求的对应的Controller类。 2.Controller对应于DispathcherServlet的次级控制器,本身实现了对应某个具体Web请求的处理逻辑,当HandlerMapping查到了Controller之后,DispathcherServlet获得了HandlerMapping的返回结果。然后调用Controller处理请求,处理完成后,返回一个ModelAndView实例,里面包含两部分内容视图的逻辑名称,DispathcherServlet根据名称决定显示哪个视图模型数据,渲染过程中需要将这些模型数据并入视图的显示中 ViewResolver和View我们已经来到了最后一步,要转成最终的JSP视图文件。由于模板引擎很多,Spring提供了一套基于ViewResolver和View的抽象层。 ![](/images/fc45dedeba68b31c39383776fc7a7a35eba30a5a.jpg) [来自为知笔记(Wiz)](http://www.wiz.cn/i/e0140d75 "来自为知笔记(Wiz)")

2014-07-13 · 1 min · bystander

Spring揭秘-22章,迈向Spring MVC的旅程

Spring揭秘-22章,迈向Spring MVC的旅程 Servlet导致数据访问逻辑和业务处理逻辑和对应的视图渲染逻辑相互混杂。之后,JSP出现,通过将输出渲染以模板的形式抽取到jsp后缀的模板文件中,jsp开始繁盛,同时要注意,Servlet处理web请求的时候,要在web.xml中,注册相应的请求url和具体的Serlet的映射关系。于是,jsp有开始混合着写代码 于是,我们引入了JavaBean,来封装相关业务逻辑,经过一次升级后, ![](/images/e6af173baae0c480b8dddc398ce8dd3078b260e4.jpg) 这个模型就比较清楚了。但是和MVC还是有点差别一个典型的mvc模式应该是这样的![](/images/4bcce41a42b7cc021f4b726da1c3533cc5371a21.jpg) 控制器处理请求,模型封装逻辑和状态,视图给用户, Structs以请求/响应框架为基础。 Spring MVC属于请求渠道的WEb框架。框架引入Front Controller做分发之后,就更加好管理了。 ![](/images/131d593fd9a36cfc8180a4b86495208a356ae563.jpg) [来自为知笔记(Wiz)](http://www.wiz.cn/i/e0140d75 "来自为知笔记(Wiz)")

2014-07-13 · 1 min · bystander

Spring揭秘-第13章 统一的数据访问异常层次体系

Spring揭秘-第13章 统一的数据访问异常层次体系 DAO可以分离数据哭的访问和存储,屏蔽各种数据访问方式的差异性,下面以访问顾客信息为例,使用DAO模式 首先定义一个数据访问对象接口,如下 public interrface ICustomerDao{ Customer findCustomerByPK(String customerId); void updateCustomerStatus(Customer customer);   之后,所有的数据访问都通过该接口进行,不论底层存储机制如何改变,DAO的实现因此会扩展,但客户端代码不需要调整 客户端要用的时候这样使用即可。 publicclassCustomerService { privateICustomerDao customerDao; publicvoid disableCustomerCampain(String customerId) { Customer customer=getCustomerDao().findCustomerByPK(customerId); customer.setCampainStatus(CampainStatus.DISABLE); getCustomerDao().updateCustomerStatus(customer); } publicICustomerDao getCustomerDao() { return customerDao; } publicvoid setCustomerDao(ICustomerDao customerDao) { this.customerDao=customerDao; }   我们只要针对不同的数据存储方式实现不同的Dao类即可。 我们开始实现具体的访问数据了。 publicCustomer findCustomerByPK(String customerId) { Connection con=null; try{ con=getDataSource().getConnection(); Customer cust=..; return cust; } catch(SQLException e){ //这里咋办,直接抛出还是直接处理? } finally{ releaseConnection(con); } } privatevoid releaseConnection(Connection con){ }   SQLException 是checked exception,如果在DAO的实现类里直接处理掉,那么客户端怎么知道发生了问题, 于是,只能处理,那么客户端的签名就要改了 <span class="typ">Customer</span><span class="pln"> findCustomerByPK</span><span class="pun">(</span><span class="typ">String</span><span class="pln"> customerId</span><span class="pun">)</span><span class="kwd">throws</span><span class="typ">SQLExcepiton</span><span class="pun">;</span> ICustomerDao也要改了,可是这样的话 如果数据在其他方式存储,那么其他类型的异常捕获就要继续添加到方法签名。由于数据访问机制的不同,导致这个接口签名不断在变化。 那怎么办呢?DAO如此美好的远景。 直接抛出,的话,因此,可以根据发生的异常信息封装成不同的unchecked exception,然后抛出,这样方法签名不用改。因为unchecked不需要编译器检查。 要开始写了?Spring的数据访问异常层次体系已经给我们做完了 该体系下面所有的异常类型均以DataAccessException为统领,然后划分成不同的子类型 ![](/images/3ab324530a8c3d0b0216d3f7549ebbfa6b7869f4.jpg) 这些类是啥搜索一下,或者根据名字猜一下,之后我们就可以

2014-07-13 · 1 min · bystander

Spring揭秘-第四章BeanFactory笔记

之前说了,IoC容器就是一个IoC Service Provider,但是容器是个啥意思?![](/images/d4d1615a6c1a06895088631c37e3fca7692f0b8a.png) 可以看到IoC容器提供了更多的内容, Spring的IoC容器又分成两种, 1.BeanFactory 。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延 迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对 该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需 要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的 IoC容器选择。 2.ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高 级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等,这些会在后面详述。ApplicationContext所管理 的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来 说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容 器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中, ApplicationContext类型的容器是比较合适的选择。 通过 图4-2,我们可以对BeanFactory和ApplicationContext之间的关系有一个更清晰的认识。 ![](/images/f531ea3e11bb391453eb5f763283398c938634b2.png) Bean工长,就是生成Bean的嘛,每个业务对象被看成Javabean,我们与工厂打交道就简单得多,我们只要告诉他我要什么对象,至于怎么组装,那是他的事情。 工厂提供了一些对外的接口,比如获取Bean,渠道Bean的状态等等。 以xml为例 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="djNewsProvider" class="..FXNewsProvider"> <constructor-arg index="0"> <ref bean="djNewsListener"/> </constructor-arg> <constructor-arg index="1"> <ref bean="djNewsPersister"/> </constructor-arg> </bean> <bean id="djNewsListener" class="..impl.DowJonesNewsListener"> </bean> <bean id="djNewsPersister" class="..impl.DowJonesNewsPersister"> </bean> </beans> 然后 public static void main(String[] args) { DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory(); BeanFactory container = (BeanFactory)bindViaXMLFile(beanRegistry); FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); newsProvider.getAndPersistNews(); } public static BeanFactory bindViaXMLFile(BeanDefinitionRegistry registry) { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry); reader.loadBeanDefinitions("classpath:../news-config.xml"); return (BeanFactory)registry; // 或者直接 //return new XmlBeanFactory(new ClassPathResource("../news-config.xml")); } 人生好像就美好

2014-07-13 · 1 min · bystander

Spring揭秘-第三章IoC Service Provider读书笔记

我们虽然已经通过IoC声明了相应的依赖,但是最终总要有个啥东西将这些依赖对象绑定在一起,这里而IoC Service Provider, 来啦,表示一种绑定的实现方式,可以使一段代码,也可以是一组相关的泪,甚至是框架或容器。 比如前一篇提到的 IFXNewsListener newsListener = new DowJonesNewsListener(); IFXNewsPersister newsPersister = new DowJonesNewsPersister(); FXNewsProvider newsProvider = new FXNewsProvider(newsListener,newsPersister); newsProvider.getAndPersistNews();   就是一个容器,只不过太简单了,不适用于更多的场景, IoC容器就是Spring提供依赖注入服务的Provider IoC Service Provider 是干嘛? 1.业务对象的构建管理,剥离客户端对象的构建的依赖逻辑,比如A引用B,那么A是B的客户端对象,容器需要理清这种关系。 2.业务对象之间的依赖绑定,通过1的实现, 识别各个对象的依赖关系,然后将这些对象依赖的对象注入绑定,用的时候就有了。 那你要问了,这怎么管理对象间的依赖关系呢 IoC Service Provider怎么就能完全领会代码的意图呢?他怎么记住这么多的依赖关系等等,他要这么做 1.元数据方式 2.通过描述性较强的xml来记录对应信息 3.通过编写代码的方式注册这些信息 3.直接编码方式, 明确了依赖关系 IoContainer container = ...; container.register(FXNewsProvider.class,new FXNewsProvider()); container.register(IFXNewsListener.class,new DowJonesNewsListener()); ... FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class); newProvider.getAndPersistNews();   2.配置文件方式 最常见的还是xml方式, <bean id="newsProvider" class="..FXNewsProvider"> <property name="newsListener"> <ref bean="djNewsListener"/> </property> <property name="newPersistener"> <ref bean="djNewsPersister"/> </property> </bean> <bean id="djNewsListener" class="..impl.DowJonesNewsListener"> </bean> <bean id="djNewsPersister" class="..impl.DowJonesNewsPersister"> </bean> 然后我们要用的时候 container.readConfigurationFiles(...); FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("newsProvider"); newsProvider.getAndPersistNews(); 容器读取配置文件,然后取到其中的两个类,注入进来。 1.元数据方式 代表实现Java Guide,基于java注解,在类中直接使用元数据信息来标注 各个对象之间的依赖关系。然后框架根据信息组装之后交给客户端对象使用 这种方式重写我们的例子就是 public class FXNewsProvider { private IFXNewsListener newsListener; private IFXNewsPersister newPersistener; @Inject public FXNewsProvider(IFXNewsListener listener,IFXNewsPersister persister) { this.newsListener = listener; this.newPersistener = persister; } ... } 通过 @Inject,我们指明需要IoC Service Provider通过构造方法注入方 余下的部分由Guide提供的Module完成,我们继承一下。 public class NewsBindingModule extends AbstractModule { @Override protected void configure() { bind(IFXNewsListener.class) .to(DowJonesNewsListener.class).in(Scopes.SINGLETON); bind(IFXNewsPersister.class).to(DowJonesNewsPersister.class).in(Scopes.SINGLETON); } } 最后,要用的时候,直接来拿吧。 Injector injector = Guice.createInjector(new NewsBindingModule()); FXNewsProvider newsProvider = injector.getInstance(FXNewsProvider.class); newsProvider.getAndPersistNews();  

2014-07-13 · 1 min · bystander

《Spring揭秘》读书笔记-第二章IoC的基本概念

理念就是让别人为你服务,中文名控制反转,也叫依赖注入DI public class FXNewsProvider { private IFXNewsListener newsListener; private IFXNewsPersister newPersistener; public void getAndPersistNews() { String[] newsIds = newsListener.getAvailableNewsIds(); if(ArrayUtils.isEmpty(newsIds)) { return; } for(String newsId : newsIds) { FXNewsBean newsBean = newsListener.getNewsByPK(newsId); newPersistener.persistNews(newsBean); newsListener.postProcessIfNecessary(newsId); } } } 假设这个类用来处理新闻, IFXNewsListener 用来获取新闻, IFXNewsPersister 用来把获取的新闻持久化 当我们需要获取不同的新闻源,比如道琼斯的新闻时,我们会写一个 DowJonesNewsListener 类和 DowJonesNewsPersister 类,然后实例化 public FXNewsProvider() { newsListener = new DowJonesNewsListener(); newPersistener = new DowJonesNewsPersister(); } 如果我们依赖于某个类或服务,最简单而有效的方式就是直接 在类的构造函数中新建相应的依赖类。 注意看, ,我们都是自己主动地去获 取依赖的对象! 可是回头想想,我们自己每次用到什么依赖对象都要主动地去获取,这是否真的必要?我们最终 所要做的,其实就是直接调用依赖对象所提供的某项服务而已 能不能我们用的时候自动送过来呢? 。现在是用什么,让别人直接送过来就成。所以,简单点儿说, IoC 的理念就是, 让别人为你服务!在图 2-1 中,也就是让 IoC Service Provider 来为你服务! ...

2014-07-01 · 2 min · bystander