分布式cookie-session的实现(spring-session)

2015/03/17 18:39:10 No Comments

本文使用的spring-session版本为 1.0.0,地址为: https://github.com/spring-projects/spring-session

1     session存储策略

存储,即在后台使用session的setAttribute,getAttribute等方法时,这些内部存放的数据最终存储至什么位置。比如在默认的tomcat实现中,相应的数据即存储在内存中,并在停止之后会序列化至磁盘中。
可以使用内存存储和第三方存储,如redis。对于集群式的session存储,那么肯定会使用第三方存储的实现。

在spring-session中,对session存储单独作了一个定义,但定义上基本保证与http session一致,主要的目的在于它可以支持在非http的环境中模拟使用。因此不直接使用http session接口。
先看定义:

public interface Session {
     /** 获取惟一的id,可以理解为即将每个session当作一个实体对象 */
    String getId();
   <T> T getAttribute(String attributeName);
    Set<String> getAttributeNames();
    void setAttribute(String attributeName, Object attributeValue);
    void removeAttribute(String attributeName);
}

从这个定义来看,如果要使用httpSession,如果实现了这个session接口,那么实现上就可以全部实现httpSession对于存储的要求。对于非httpSession场景,使用这个也可以达到session存储的目的。
当然,为了保证在会话场景中会使用到失效,最后访问时间,最大不活跃时间的目的,spring-session也有一个继承于session接口的expiringSession接口。

1.0    id主键的生成

对于id,即理解为session实体对象惟一键,可以采用任意的一种惟一key生成策略。比如,使用uuid来生成惟一键。同时,也可以将这个id认为就是在http中sessionCookie的值

read more… »

使用spring typeConverter造成的数据错乱(多线程环境)

2015/02/10 17:00:23 No Comments

在线上环境碰到一个问题,经由数据库查询并进行数据处理转换之后的数据在界面显示时随机出现数据错位。由于该问题不可重现(重新清除缓存并操作一次问题即解决),并且由于缓存的存在(缓存了错误的数据),导致此问题严重并且很难查找。

业务场景描述如下:

数据库查询数据->一次处理->类型转换->二次处理->界面显示

由于每个步骤都涉及到很多代码,因此在处理时通过在不同的点设定潜在出错点,并通过判断数据变化进行log。尝试在本地环境重现此问题。经过一次偶然的场景,发现一个本该是8位数字的字符串在转换过程中报错,由此找出了真正的问题。

//报错的业务代码 假定数据为 20141111
str = s.substring(0,4) + "/" + s.substring(4,6) + "/" + s.substring(6,8)
//报错异常 indexOutofBound,即字符串不足8位...

经过断点,加层层回溯,终于发现数据在经过一次类型转换之后被修改了。转换代码如下所示:

private static TypeConverter typeConverter = new SimpleTypeConverter();
        public <T> T convertValue(Object value) {
            return (T)typeConverter.convertIfNecessary(value, Integer.class);
        }

初看起来,这个方法调用并没有什么问题,即将数据值转换为整数类型.重点的问题在于在这个方法(由spring提供)的内部实现并不是线程安全的。附API说明:

public interface TypeConverter
Interface that defines type conversion methods. Typically (but not necessarily) implemented in conjunction with the PropertyEditorRegistry interface.
Note: Since TypeConverter implementations are typically based on PropertyEditors which aren't thread-safe, TypeConverters themselves are not to be considered as thread-safe either.

typeConverter内部会使用propertyEditor来进行类型转换。这种转换在spring3.0之后已不再推荐使用。见 使用线程安全的spring类型转换器ConversionService VS TypeConverter

解决方法也简单,将其更换为conversionService即可(为什么不自己写?不想重复发明轮子)。spring3.0之后,为conversionService内置了常见类型之间的转换,相当于整个类型转换子框架被完全重写了,重写的转换是状态无关的,即线程安全。至于原来的typeConverter及propertyEditor,忘掉它吧,遗留的东西。

附:jdk自带的simpleDateFormat也存在相同的问题,用这个类需要小心处理线程问题。

使用线程安全的spring类型转换器ConversionService VS TypeConverter

2015/02/10 16:29:56 No Comments

此文翻译源:http://www.theserverside.com/tip/Spring-Converters-and-Formatters。部分无用的部分有删除

1 类型转换接口

在spring3.0之前,如果要自己实现一个从字符串到其它对象的转换,那么就需要实现PropertyEditor接口。PropertyEditor是遵循javaBean规范的属性处理器,其通过set方法设置属性值,通过get方法获取属性值,相应的转换逻辑就隐藏于其中。这个接口惟一的问题在于,他们不是线程安全的.并且只能实现从字符串到其它对象的转换。

在spring3.0,提供了一个更简单的类型转换器接口。在spring mvc中,可以使用内置的或者自己实现的接口来实现从请求参数到对象之间的转换。其相应的接口定义如下:

public interface Converter<S,T> {
    public T convert(S source);
}

即通过一个S类型的对象,将其转换为T类型的对象。其中S类型并不局限于字符串,可以是任何类型。这是与PropertyEditor的主要区别。

read more… »

thinkpad w540在win8下休眠或合上盖子屏幕不亮的解决方法

2014/12/28 18:09:56 No Comments

本文来自讨论:https://forums.lenovo.com/t5/W-Series-ThinkPad-Laptops/W540-Blank-screen-after-undocking-sleep/td-p/1768605

笔者笔记本w540装上win8(或windows 2012)之后,出现一个现象,只要盒上盖子,或者是自动休眠之后,重新打开笔记本,屏幕始终就不亮了。只有直接按电源重新启动,但只要再次休眠,同样的情况就会出现。不仅是这样,只要合上盖子,这个问题也会出现,还能不能愉快地工作了…

经google,搜到前端提到的帖子,此问题还是软件驱动的问题。驱动在休眠之后,不能自动唤醒屏幕,或者说不知道LCD屏幕的存在。解决此问题办法也很简单。按下面步骤即可:

  • 卸载原来笔记本自带的intel驱动,在我的电脑,右键属性,设备管理器,选择显卡,选择intel HD 4600,右键卸载,并且选择删除驱动程序。
  • 下载旧版本的intel驱动,地址:https://downloadcenter.intel.com/Detail_Desc.aspx?DwnldID=24329 。即版本:3958的版本,下载完安装。
  • 重启,问题解决:)

使用POI在Excel中添加外部图片

2014/12/28 12:00:54 No Comments

本篇的代码主要参考自poi讨论组:http://apache-poi.1045710.n5.nabble.com/Load-remote-image-inside-excel-sheet-td5709821.html

在通常情况下,我们在Excel中添加图片,只要是通过流的方式将图片写入excel特定的picture对象中。网上也有很多的例子,本文主要讲的是如何将一个外部的图片以链接的方式添加进excel中。这样,图片以外链的方式存储,在后台写excel文件时不需要处理图片存储的问题,在下载excel时也不用担心文件太大的问题了。

首先在excel添加外部图片,在添加图片时,只需要选择 以 链接的方式添加即可。这样,在excel时存储时并不需要存储实际图片的内容。这种方式也支持添加网络上的图片。分析这2种不同的添加方式,通过将xlsx文件进行解压(直接将文件修改为zip后缀,再解压即要)之后,在路径 \xl\drawings\_rels下面的文件drawing1.xml.rels可看到以下的不同内容:

<!-- 外部引用存储 -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="file:///C:\Users\Administrator\Desktop\图像%202.png" TargetMode="External"/></Relationships>
<!-- 内部引用存储 -->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image1.png"/></Relationships>

同上可以看出,两者的区别就在于一个是引用一个../media的内部地址,实际上就是把整个图片写在内部压缩文件的一个位置上。而外部存储,则实际上直接写的是一个原始的地址。以协议名://地址的方式进行引用。
当然,不相同的地址肯定不只这一种,以上图片只是标识。虽然poi不支持添加外部图片,但通过poi自带的相关API,我们仍是可以添加外部图片的,使用的API,就是poi内部使用的工具openxml4j。

read more… »

spring LTW agent方式在tomcat 8下无效

2014/12/07 19:13:55 No Comments

在常规的spring ltw中,我们对tomcat使用ltw,一般是以下两种方式。

//1 在启动项中添加javaagent选项
-javaagent:e:/spring-instrument-4.1.1.RELEASE.jar

//2 使用自定义的启动器,在META-INF/context.xml中增加以下内容
<?xml version="1.0" encoding="UTF-8" ?>
<Context>
 <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" /> 
</Context>

这两种方式在tomcat6以及tomcat7均可以正常工作。但对于tomcat8,第1种方式已经不能再工作,仅能使用第二种方式。原因就在于tomcat8的webClassLoader已经提供了InstrumentableClassLoader接口。此接口将导致spring直接将aspectj的AspectJClassBypassingClassFileTransformer 直接添加到tomcat的classLoader中。但由于不是很正确的实现方式,导致aspect在使用tomcat8提供的classLoader时,并不能有效地对自己的advice进行weaver,导致报以下的错误信息: 

java.lang.NoSuchMethodError: XXXAdvice.aspectOf()LXXXAdvice;
 atXXX.index(AbcController.java:30)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
 at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
 at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)

这个错误的产生在于aspectJ的初始化过程和classLoader之间的交互行为,以及tomcat8中不准确的缓存行为。

read more… »