初学者心态

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  49 随笔 :: 0 文章 :: 28 评论 :: 50万 阅读

2018年8月4日

N年没有写过博客了……

开始:

2018.08.03  搬家项目,版本昨晚刚上线,今早测试与供应商的估价接口,发现问题。

背景:

    我司对接三家供应商A、B、C,各家的Url,appid不同,分别配置在配置文件中。

 

抽象类(截取部分片段):

 

 1 public abstract class AbstractSupplierOrderService implements ISupplierOrderService {
 2 
 3 
 4     protected static ISupplierConfig supplierConfig;
 5 
 6     @Autowired
 7     private MovTaskMapper movTaskMapper;
 8 
 9     //根据供应商code 获取对应供应商服务实例
10     public static ISupplierConfig getSupplierConfig(String supplierCode) {
11         if (SupplierEnum.LANXINIU.getCode().equals(supplierCode)) {
12             return SpringContextUtil.getBean(SupplierConfigLanxiniu.class);
13         }
14         if (SupplierEnum.ZIROOM.getCode().equals(supplierCode)) {
15             return SpringContextUtil.getBean(SupplierConfigZiroom.class);
16         }
17         if (SupplierEnum.SITONG.getCode().equals(supplierCode)) {
18             return SpringContextUtil.getBean(SupplierConfigSitong.class);
19         }
20         return null;
21     }
22 
23     @Override
24     public ResultInfoVo quotedPrice(EvaluateOrderPriceVo orderVo) {
25         supplierConfig = AbstractSupplierOrderService.getSupplierConfig(orderVo.getSupplierCode());
26         String quotedPriceUrl = supplierConfig.getQuotedPriceUrl();
27 
28         ResultInfoVo resultInfoVo = null;
29         EvaluateOrderPriceVo evaluateOrderPriceVo = null;
30 
31         try {
32             Map voMap = ObjectToMapUtils.objectToMapString(null, orderVo, "");
33             Map paramMap = getSupplierService(orderVo.getSupplierCode()).getOrderParams(voMap);
34 
35             log.info("=============调用供应商获取报价信息接口入参:{}=============", JSONObject.toJSONString(paramMap));
36             resultInfoVo = postSupplierServices(orderVo.getSupplierCode(), evaluateRestTemplate, quotedPriceUrl, paramMap);
37         } catch (Exception e) {
38             log.error("=============调用供应商获取报价信息异常,入参:{}=============", JSONObject.toJSONString(orderVo), e);
39             throw new OrderServiceException("调用供应商获取报价信息异常", e);
40         }
41 
42         log.info("=============调用供应商获取报价信息接口出参:{}=============", JSONObject.toJSONString(resultInfoVo));
43 
44         return resultInfoVo;
45     }
46 
47 }
View Code

 

调用入口:

 1 public ResultInfoVo evaluateOrderPrice(EvaluateOrderPriceVo evaluateOrderPriceVo) {
 2        ===========省略==========
 3         log.info("========估价流程开始,供应商code:{}========", evaluateOrderPriceVo.getSupplierCode());
 4         //动态获取服务商实例
 5         ISupplierOrderService supplierOrderService = AbstractSupplierOrderService.getSupplierService(evaluateOrderPriceVo.getSupplierCode());
 6         //调用供应商获取报价接口
 7         try {
 8             quotedPriceResult = supplierOrderService.quotedPrice(evaluateOrderPriceVo);
 9         } catch (Exception e) {
10         }
11         ===========省略==========
12     }

 

 

抽象类中有静态变量:supplierConfig。而 静态变量位于抽象类类对象的方法区,三个实现子类共用该静态变量。 如各子类对该静态变量赋值需求不同,在多线程情况下,会出现问题。

排查问题时的日志:

 

日志中可以看到,问题出在线程2,8。 

 

分析:

  线程2、1、8先后进入估价流程MovOrderService.evaluateOrderPrice,

1、线程8先根据对应的1003的supplierConfig实现,组装参数,生成sign。 实现类中取的 supplierConfig是在实现类中的一个类变量,所以不会共用抽象类中的类变量,取得apppid是对的

     

2、线程2根据抽象类中的静态类变量supplierConfig获取url。但此时supplierConfig被线程2更新成了线程2对应的实例,取的是线程2的对应配置。所以url取错了。

 

总结:多线程环境下,对于可变的变量,在抽象基类中,慎用静态变量。

因为各实现类都引用了基类的静态类变量(即便子类中自己定义了同名变量,也与基类中的变量不是一个存储空间)。该基类静态变量只有这么一个存储空间。 所以会很容易被修改,导致意想不到的结果。

posted @ 2018-08-04 18:38 初学者心态 阅读(2775) 评论(0) 推荐(0) 编辑

2014年11月5日

摘要: F5刷新缘何会引起表单重复提交 阅读全文
posted @ 2014-11-05 15:19 初学者心态 阅读(12801) 评论(1) 推荐(1) 编辑

2014年10月31日

摘要: 在jsp页面中添加base,可用相对路径:然后在标签内添加base"> 在本页面中“直接”引入外部文件时,可直接同理,本页面中的css类.top { position: absolute; left: 0; top: 95px; right: 0; height: 12... 阅读全文
posted @ 2014-10-31 14:34 初学者心态 阅读(22627) 评论(0) 推荐(0) 编辑

2012年12月13日

摘要: 已删除 阅读全文
posted @ 2012-12-13 11:25 初学者心态 阅读(812) 评论(0) 推荐(1) 编辑

2012年12月11日

摘要: 转自:http://dbear.iteye.com/blog/613745 备用学习,原文作者讲的很好,在此表示感谢。尤其是总结的八条规律及其下面的解释,其中第七条尤其好。类 Class类的概念应该是面向对象语言的一个特色,但是JavaScript并不像Java,C++等高级语言那样拥有正式的类,而是多数通过构造器以及原型方式来仿造实现。在讨论构造器和原型方法前,我可以看看一种叫做“工厂方式”的仿造方法。function start() { alert("Bang!!");}function createCar(color, title) { var car = {}; c 阅读全文
posted @ 2012-12-11 14:43 初学者心态 阅读(2327) 评论(1) 推荐(1) 编辑

2012年11月22日

摘要: 本人系转载,如有侵权,请速告知,本人会在第一时间删除1、到底在哪用cascade="..."?cascade属性并不是多对多关系一定要用的,有了它只是让我们在插入或删除对像时更方便一些,只要在cascade的源头上插入或是删除,所有cascade的关系就会被自动的插入或是删除。为了能正确的cascade,unsaved-value是个很重要的属性。Hibernate通过这个属性来判断一个对象应该save还是update,如果这个对象的id是unsaved-value的话,那说明这个对象不是persistence object,要save(insert);如果id是非unsa 阅读全文
posted @ 2012-11-22 17:34 初学者心态 阅读(5340) 评论(0) 推荐(0) 编辑

2012年11月19日

摘要: <ext:RowLayout ID="RowLayout1" runat="server" Split="false"> <Rows> <ext:LayoutRow RowHeight=".5"> <ext:Panel runat="server"> ... 阅读全文
posted @ 2012-11-19 09:55 初学者心态 阅读(1102) 评论(0) 推荐(0) 编辑

2012年11月14日

摘要: 要想了解"int i=1" "int i=new int() "和“String str = "a";” “String str = new String("a")”区别,先要了解c#值类型和引用类型区别,以及c#对值类型和引用类型的处理。值类型:整型、布尔型、字符型、实数型、结构型、枚举型。引用类型:类、对象、字符串、数组、接口、委托。注意字符是值类型,字符串是引用类型。http://www.sosuo8.com/article/show.asp?id=2184讲的非常好csdn这个帖子讨论的关于这个问题:htt 阅读全文
posted @ 2012-11-14 23:31 初学者心态 阅读(526) 评论(0) 推荐(0) 编辑

2012年11月1日

摘要: stor:<ext:Store ID="StorePutInImg" runat="server"> <Reader> <ext:JsonReader IDProperty="PicName"> <Fields> <ext:RecordField Name="PicName"> </ext:RecordField> <ext:RecordField Name="PicFileName"> ... 阅读全文
posted @ 2012-11-01 15:04 初学者心态 阅读(1853) 评论(0) 推荐(0) 编辑

2012年8月30日

摘要: 这个解决方法:需要手工把要引用的dll复制到C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies 下,解决 阅读全文
posted @ 2012-08-30 22:53 初学者心态 阅读(3042) 评论(0) 推荐(0) 编辑

点击右上角即可分享
微信分享提示