一个奇葩问题

今天运行一个java工程时出现问题,总是提示某个配置文件的数据无法读取。百思不得其解,后来检查相关配置文件,发现一个有趣的问题:工程依赖的一个jar包里(这个jar包是我用gradle打包的),竟然在同级目录下有两个完全一样的文件(我的环境是ubuntu15.04),把该文件放到windows下显示效果一样,说明不是障眼法。我试着在jar包中删除其中一个,结果同名的两个xml文件同时被删除了,这又说明这两个文件其实是同一个文件。

 

检查发现,原来是用gradle打包这个jar文件时多复制了一次hivemodule.xml文件,不知这算不算操作系统中文件系统的bug。总之,改正过来后,一切正常了。
 


Gradle打包的war文件部署到tomcat后运行解压出错的问题

自从用了gradle来构建公司的所有新旧项目,总会涌出一堆各式各样的问题,看似配置比maven简单了,但问题却更多。究其原因,一方面团员成员都比较菜,我要一个一个为他们解决相关问题,但过不了几天,又冒出新问题,没养成自我解决问题的能力,又得找我,作为苦逼的项目经理这一职务,我还真羡慕他们,想当年,哪有谁帮我这样处理问题啊;另一方面是gradle作为开源软件,项目比较活跃,版本更新比较快,但问题也比较多,这次就遇到了编码问题。

    说到编码问题,我在这家公司也是深受其害而又无可奈何。首先公司的固有框架比较老,N年未改变,里面utf-8和GBK编码的源码文件都有,当拿过来作为新项目的开发基础框架时,却又要结合现代的gradle、eclipse、tomcat等开发工具,在这个过程中仅编码问题就令人头大,有时真是犯了强迫症,恨不得把老框架里的每个文件编码都改一遍,但老框架是依赖的一个外部封装的jar包,而这个jar包对应的源码SVN库我没有权限。。。

    回到正题,最初用gradle时的版本是2.2,当打包时提示编码问题,后来我在build.gradle文件中加入

[compileJava, javadoc, compileTestJava]*.options*.encoding = 'UTF-8'

强制以utf-8编码编译源文件,虽然有警告提示,但编译部署都能成功。最近的项目已经有人在用grade2.6了,随之而来的问题就是:有的人编译就直接报错;有的人有警告但能编译成功,在部署war包时报错。首先值得一提的是,本人一直用的ubuntu,系统默认编码为UTF-8,一直运行良好,而这些小伙伴都是用的中文windows,系统环境默认编码为GBK,在eclipse中开发时各种报错。经过尝试,最终在基于eclipse的gradle插件中配置了一项JVM参数,才使小伙伴们感受到阳光明媚(建议把上面build.gradle文件中的对应配置注释掉):

-Dfile.encoding=UTF-8



hibernate注解@Formula使用中要注意的一点

在工作中遇到一个@Formula使用中的小问题,记录一下。

以例子形式展现:

import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.Formula;

@Entity
@Table(name = "in_come")
public class Income {
	private int id;
	private double salary;// 薪水

	private double reward;// 奖金

	private double subsidy;// 补助

	@Formula("(select salary + reward + subsidy from in_come a where a.id=id)")
	private double total;// 总收入

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

        //省略其他getter和setter方法
} 

如上代码,我想通过@Formula来统计各类收入之和。但很不幸,每当新增或修改完一条记录后,自动跳转到记录列表页面时,total死活出不来,总是为0,需要将该列表页面重新查询一遍才行。

解决方法:当新增或修改完记录后,且在自动跳转到列表页面之前,将会对记录重新查询,在查询之前加入一句EntityManager.clear()即可。这句的目的是将刚刚操作过的记录彻底从缓存中清除,迫使程序查询时从数据库中获取最新的数据,这样@Formula中定义的SQL就能够真正被执行。

Spring获取WebApplicationContext方法介绍

    但凡用Spring框架,就免不了获取SpringBean实例。众所周知,spring主要是依靠xml配置及较新的注解方式供spring实例化并装载到容器中,web程序启动并初始化所有Bean之后,框架会以反射的方式提供对应的bean给有需要的其他实例,或者程序员直接调用类似getBean(“myBean”)的方法向spring容器请求需要的bean以完成业务操作。

    本文在此仅简单介绍后者即程序员手工获取bean的几种常用方法,而要获取bean,实现对getBean方法的操作,就需要获取WebApplicationContext实例:

一、史上最简单的获取WebApplicationContext方式

WebApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();
MyBean bean=ctx.getBean("myBean",MyBean.class);

没错,就这么简单,尤其值得一提的是,从spring3.0开始,ContextLoaderListener继承了ContextLoader,也就是说,如果在系统的web.xml中配置了该listener,我们也可以这样获取WebApplicationContext。

	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

WebApplicationContext wac = ContextLoaderListener.getCurrentWebApplicationContext();

二、通过指定的xml配置文件来获取WebApplicationContext

ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
ApplicationContext ac2 = new ClassPathXmlApplicationContext("applicationContext.xml");

此方法适合于独立应用程序,或者根据业务需求独立加载指定bean资源的情况。

三、在已知ServletContext情况下获取WebApplicationContext

这种方法应该是目前web应用程序中使用最普遍的,具体而言,它又有多种使用方式。

WebApplicationContext wac=(WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

spring在web程序加载时,默认会将WebApplicationContext对象存放在ServletContext的属性中,并以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为属性名。

当然,通过常用的WebApplicationContextUtils类也能获取WebApplicationContext,只不过多包裹了一层,最张还是从ServletContext中取出来的:

WebApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc);
WebApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);

另外,当我们在自己的业务类中需要调用WebApplicationContext时,除了以上的直接调用方式,还可以通过spring框架的自动依赖注入来获取该实例。此时需要实现相应的业务接口ApplicationContextAware或ServletContextAware

public class MyService implements ApplicationContextAware {

	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

        //my business methods...
}

OR

public class MyService implements ServletContextAware, ApplicationContextAware {

	private ServletContext servletContext;

	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}
        
        //my business methods... 
}

四、最后

在我们开发一个新的系统时,为了保证代码的统一和系统的规范,可以定义一个统一的全局接口供程序员调用以获取WebApplicationContext实例。

另外,如果我们需要在web应用启动时,对WebApplicationContext对象进行某些操作的话(比如马上获取一个bean处理某项业务),也可以自定义一个实现了ServletContextListener 接口的类,与ContextLoaderListener一同配置到web.xml中。



经纬度坐标及IP地址的正则表达式

以JS为例

经纬度坐标:

经度:<input type="text" id="lng" value="123.1254"/>
纬度:<input type="text" id="lat" value="-15.267"/>
//动态校验经纬坐标输入
var oldLng=$("#lng").val();
var oldLat=$("#lat").val();
var lngRe=/^[-]?(\d|([1-9]\d)|(1[0-7]\d)|(180))(\.\d*)?$/g;
var latRe=/^[-]?(\d|([1-8]\d)|(90))(\.\d*)?$/g;
$("#lng,#lat").on("input change propertychange",function(){
    if(this.value.match(this.id=="lng"?lngRe:latRe)==null){
        this.value=(this.id=="lng"?oldLng:oldLat);//输入非法,则恢复上次正确数据
    }else{//保留上次正确数据
        (this.id=="lng" ? oldLng = this.value : oldLat = this.value);
    }
});

正则表达式分析:

经度坐标范围为-180 ~ 180,而纬度坐标范围为-90 ~ 90,以经度为例,表达式可作如下拆分:

0-9,10-99,100-179,180,这样对应的表达式可分别写为\d,[1-9]\d,1[0-7]\d,(180),对他们进行“或”(|)运算,再考虑小数((\.\d*)?)以及负数(-),最后的正则表达式成为了这样:/^[-]?(\d|([1-9]\d)|(1[0-7]\d)|(180))(\.\d*)?$/g

同理,纬度的正则表达式就不难了(拆分步骤略,可参考经度):/^[-]?(\d|([1-8]\d)|(90))(\.\d*)?$/g


其实针对这类包含数字的业务情形写正则表达式时,主要是对数字的规律进行分组拆分,每一组代表一个独立的业务情形,然后将对应的各个独立的正则表达式求交集。

再看看IP地址的正则表达式,也是基于这个分组拆分法(本文只考虑IPv4)。

IP地址为四组0~255的整数,那么将问题缩小,则是考虑每组的0~255怎么拆分的问题:

0-9,10-99,100-199,200-249,250-255
====>
\d,[1-9]\d,1\d{2},2[0-4]\d,25[0-5]

因为IPv4由三个.分隔,或者说前面三组的末尾都带了个.,则表达式最终可写为:

(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])

优思小C2之体验

    魅族作为一款有个性的手机品牌,已经有好几年了,小米作为后起之秀,瞬间超越前者。总之,两者算是目前最火的国产手机品牌吧。

    国庆要回家,打算买个新手机犒劳自己,在魅族和小米之间一直无法定夺。魅族最新的MX3需要预订,担心在国庆之前拿不到手,而且魅族的手机散热一直不够好,电池容量不够大,且不能拆卸。小米手机最新的小米3一直未发布,其饥饿营销方式一直为人诟病,连预订都没有,要先预约然后去抢购,我勒个,小米2S出来这么久了,竟然还拿不到现货。算了,干脆在杂牌机里挑一款吧。于是在淘宝通过手机参数搜索过滤,最终知道并冒险拿下了这个叫做优思小C的手机。目前已用了半个月了,谈下我的使用感受吧。

T2dQw_Xb4XXXXXXXXX_!!1084116057

主屏尺寸: 5.0英寸

触摸屏: 电容式触摸屏
主屏分辨率: 1920×1080像素
运行内存: RAM 2G
机身内存: 32G

cpu: mtk6589t
四核cpu频率: 1.5G
电池容量: 2000mAh
摄像头(后置): 1300万像素

摄像头(前置): 500万像素

其实这样的配置没法跟小米3和MX3比,除了前置摄像头像素高点,机身内存高点,其他核心配置都不会高。甚至连红米都可以跟它一比,只是在运行内存和机身内存上确实较低,但其799元的价格是1999元的XC2所无法比拟的。配置不是今天的重点,这半个多月的使用感受才值得一提。

优点:
1.比较薄,手感还可以。
2.屏幕确实还不错,照相也很清晰。
3.玩游戏、看电影比较流畅,这点很不错。

缺点(有点多):
1.电池的问题就不多说了。
2.摄像头附近发热有点严重,玩游戏、看电影、拍照,甚至看电子书,都会散热很多。
3.音量键和电源键做得比较差,按下去没有手感,音量+键没有音量-键好按;电源键更糟糕,因为按下去后就起不来了,要求卖家换货之后,音量键的问题依然如故,而电源键好点了,但按了几次后还是可以看出比音量键要塌陷得比较多,两个按键不在同一平面上了。
4.关机状态下,开机后,手机屏幕为黑屏。特别是在充电状态下开机,黑屏的几率很高,不过手机屏幕依然可以感应,以摸黑的方式触摸关机并重启可以恢复正常(目前为止,遇到一次连续开机两次后才消除黑屏),这一点比较严重。
5.死机。有一次,在使用高德地图时,我按下了电源键待机,然后又按一下以唤醒屏幕,结果导致了死机,只能拨电池解决。
6.耳机。时而会出现耳机已拨出,但屏幕上方的状态栏依然显示耳机图标;耳机貌似只能线控内置的多米音乐播放器,而且只有第一次线控成功了,后来再也不行了(即便在多米设置中反复配置也无效);耳机插入接口时感觉也比较生硬。
7.手机下方的三个按键。USB接口做得比较粗糙,USB线插入后,还有一部分接头没插入,给人误觉。另外中间的按键灯亮时,在USB口可以直接看到白炽的亮光,另外两个按键也可以透过手机外壳看到内部的灯光。
8.有过一次在很焦急的情况下,死活滑不开接听键的经历(可能是手上出汗的缘故,看来老式手机的物理接听按键还是有其优势的)。
9.XC2突然发热黑屏。这是今天发现的问题,也是迄今最严重的问题。

       特别是第9点,要仔细再提一下。今天下午把XC2揣裤兜里,在外面逛了一圈(有晒太阳),回到家,突然感觉大腿发烫,伸手掏出XC2,奇热无比,让我感觉很害怕(因为几周前看到美国有人把三星galaxy放上衣口袋突然发烫,连衣带手机往商场地上一扔便爆炸了)。而且手机屏幕处于提示是否关机,取消关机,屏幕为黑屏,只得再次选择关机,重启后又正常了。
       这让我很恼火啊,要么就坏得彻底点,好直接返修或换货,要不然售后都估计不会管吧。这是我用了半个多月来发现的最严重的问题,因为已经不是好不好用的问题了,而是涉及人身安全,真让人提心吊胆。

总结:国产机真的让人伤不起吗?难道是我人品问题?

关于spring的roo shell不能正常使用的解决办法

    现在spring的使用非常流行,而且可能爱屋及乌地用上了STS(spring source toolsuite),而该套开发工具自带了maven及spring roo工具,所以很多小伙伴们会使用这些插件。但要命的是,许多小伙伴在实际应用这些插件的时候,会遇到各种莫名其妙的问题,比如roo shell不能使用了,真令人抓狂啊。

    基于我的使用经验及同事的一些解决办法,总结常见的问题及解决办法。

    问题1:roo shell报错,无法启动。

    问题2:roo shell启动后相关命令变少了,比如我要建个实体对象,连个entity命令都出不来了。

    解决办法:对于第一个问题,一般最好将spring roo工具包解压到非中文目录下即可(roo shell启动时貌似对中文路径解析有问题,有时会出现乱码);第二个问题最常见,也是最伤脑筋的,往往是roo shell可以正常使用,但某一天突然出现了,即便重装spring roo甚至STS也无济于事(如果你重装了操作系统那我没话说了)。

    当找到解决办法时,才发现问题原来如此简单:删除掉spring roo安装目录下使用后产生的缓存文件,如下图。

(删除以sts-cache或.sts-cache开头的文件)

如果这样依然无法解决问题,那就删除当前系统用户目录下的一个spring roo相关文件,比如我的是C:\Documents and Settings\Administrator\.spring_roo_pgp.bpg

这样就可以解决了,爽快地用你的roo shell吧。

小伙伴们,你们的问题解决了吗。

UltraEdit配置C、C++及Java的编译和运行环境

前提条件:

请自行安装好UltraEdit文本编辑器和C/C++、java编译环境。我使用的是UltraEdit 17.20.0.1013,C/C++环境使用的是MinGW,java版本为1.7.0_11,另外,因为editplus使用的版本不同及是否汉化,界面会有所差异,请对号入座。

一、C环境配置

打开UltraEdit–>高级–>工具栏配置,点击“插入”,然后依次输入如下:

菜单项目名称:C编译
命令行:gcc %n%e -o %n.exe
工作目录:%p

如下图:

说明:%n表示文件名称,%e表示文件扩展名,%p表示当前文件所在目录。

同样,再新增一个C运行工具,配置更简单,如下:

菜单项目名称: C运行
命令行:%n
工作目录%p

演示:
写一个简单的hello world程序,然后点击菜单中的工具–>用户工具组–>C环境,这样我自定义的C环境就出现在高级菜单栏的下方了,如图:

然后运行,结果如下:

二、C++环境配置
与C环境的配置基本相同,只是编译命令改为了g++,对此不再赘述,仅贴出C++编译及运行的菜单配置项:
菜单项目名称: C++编译
命令行:g++ %n%e -o %n.exe
工作目录:%p

菜单项目名称: C++运行
命令行:%n
工作目录:%p

三、Java环境配置

菜单项目名称: java编译
命令行:javac -d . %n%e
工作目录:%p

菜单项目名称: java运行
命令行:java -cp . %n
工作目录:%p

如果java文件是带包名的,则上面的java运行方式会报错,需要新建一个java运行菜单项:

菜单项目名称: java运行(带package)
命令行java -cp . %sel%.%n
工作目录:%p

当运行带package的java程序时,一定要选择对应的package名,如上图。

结束:
EditPlus配置C、C++及Java的编译和运行环境

简单模拟tomcat环境测试HttpSessionBindingListener实现效果

      HttpSessionBindingListener接口在很多情况下用于在线用户人数的统计与管理。不言而喻,session是必须的,但又不想专门启动tomcat运行一个web程序来测试它的效果。那么有没有办法直接调用tomcat自己的jar包来构造一个session来实现我的目的。比如:

1.调用tomcat的某个jar,这个包就是在tomcat运行时接收远程客户端的http请求时,用来构造HttpServletRequest、HttpServletResponse、HttpSession等我们熟知的servlet API中的实现类,现在我只需要HttpSession接口的实现。

2.获得了HttpSession实例之后,我就可以用session.setAttribute()方法将HttpSessionBindingListener的实例放入session,然后又调用session.removeAttribute()删除这个属性,看看HttpSessionBindingListener的实例中相应方法是否被触发,从而实现测试目的。

通过多番测试,证明上面的步骤是完全能实现的。

一、首先将tomcat安装目录下面的所有jar包引入工程(其实并非全部需要,只是为了操作简单),包括lib及bin目录下的。

二、自定义HttpSessionBindingListener的实现类

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class MyBindingListener implements HttpSessionBindingListener {

	// 绑定与解绑定时需要操作的业务数据
	private Object obj;

	public MyBindingListener(Object obj) {
		this.user = obj;
	}

	@Override
	public void valueBound(HttpSessionBindingEvent event) {
		System.out.println("hi...valueBound");
		// process obj
	}

	@Override
	public void valueUnbound(HttpSessionBindingEvent event) {
		System.out.println("hi...valueUnbound");
		// process obj
	}

}

三、寻找Tomcat中的HttpSession实现类
      其实也比较简单,首先查看一下tomcat源码对应的API文档http://tomcat.apache.org/tomcat-7.0-doc/api/index.html,找到后缀为session的包名:org.apache.catalina.session,经查看,HttpSession的实现类为StandardSession和StandardSessionFacade,事后测试,这两个都可以用来检查HttpSessionBindingListener的监听效果。不过为满足好奇,我还是启动了tomcat,在随便一个demo的页面中写上<%=request.getSession()%>,看看tomcat默认是使用的哪个实现,结果输出 org.apache.catalina.session.StandardSessionFacade@1f6df4c 那就用它吧。
    然后,就是写测试类,如下:

import org.apache.catalina.core.StandardContext;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.session.StandardSessionFacade;
import org.junit.Test;

public class HttpSessionBindingListenerTest {

	@Test
	public void test() {
		StandardManager sm=new StandardManager();
		sm.setContainer(new StandardContext());//添加容器
		StandardSession ss=new StandardSession(sm);//构造一个session

		System.out.println(ss.isValid());
		ss.setValid(true);
		System.out.println(ss.isValid());//session变为了有效,我们才能用它

         //利用上面的session构造出我们需要的那个session,
         //其实也可直接用上面的session测试
         StandardSessionFacade session=new StandardSessionFacade(ss);
//		StandardSession session=new StandardSession(sm);
//		session.setValid(true);
		System.out.println(session);

		MyBindingListener my=new MyBindingListener(new Object());

		session.setAttribute("my", my);
		try {
			Thread.currentThread().sleep(3000);//暂停3秒,看控制台的变化
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		session.removeAttribute("my");
	}
}

    运行结果:

false
true
org.apache.catalina.session.StandardSessionFacade@1f6df4c
hi...valueBound
hi...valueUnbound

      说明,为求简便,MyBindingListener类中的属性只是简单使用了Object类型,具体可依个人实现业务需要进行相应修改,然后在对应的触发方法中对其进行相应处理。

EditPlus配置C、C++及Java的编译和运行环境

前提条件:
请自行安装好editplus文本编辑器和C/C++、java编译环境。我使用的是editplus v3.30,C/C++环境使用的是MinGW,java版本为1.7.0_11,另外,因为editplus使用的版本不同及是否汉化,界面会有所差异,请对号入座。

一、C环境配置
打开editplus–>工具–>配置用户工具–>工具–>用户工具–>工具组(任意一个工具组都可以),点击右边的组名,取名为“C环境”,确认后回到父窗口,点击“添加工具”,选择“应用程序”,然后依次输入如下:

菜单文字:C编译
命令:gcc
参数:$(FilePath) -o $(FileDir)\$(FileNameNoExt).exe
初始目录:$(FileDir)
动作:选择“捕获输出”

如下图:
C环境编译
注意:
1.其中的命令gcc这种写法的前提条件必须是已经将C编译环境的目录加入了系统PATH环境变量,否则必须写出此gcc命令的全路径,如:D:\Program Files\MinGW\bin\gcc.exe
2.编译参数也可简单配置为:$(FilePath) -o $(FileNameNoExt)

同样,再加入一个C运行程序,再次点击“添加工具”,选择“应用程序”,然后依次输入如下:

菜单文字:C运行
命令:$(FileNameNoExt)
参数(为空):
初始目录:$(FileDir)
动作:选择“无”

如下图:
C环境运行
注意:动作之所以选择为“无”,是因为如果程序需要与命令行交互,比如输入一些指令时,editplus无法提供输入功能,从而导致程序阻塞,所以必须释放,让windows的命令行自动弹出来执行此程序。

演示:
写一个简单的hello world程序,然后点击菜单中的工具–>用户工具组–>C环境,这样我自定义的C环境就出现在工具菜单栏的最下面了,如图:
C环境编译及运行菜单

 依次点击相应的“C编译”及“C运行”,效果如下:

C运行demo

二、C++环境配置
与C环境的配置基本相同,只是编译命令改为了g++,对此不再赘述,仅贴出C++编译及运行的工具配置:

菜单文字:C++编译
命令:g++
参数:$(FilePath) -o $(FileNameNoExt)
初始目录:$(FileDir)
动作:选择“捕获输出”

菜单文字:C++运行
命令:$(FileNameNoExt)
参数(为空):
初始目录:$(FileDir)
动作:选择“无”

三、Java环境配置
菜单文字:java编译
命令:javac -d .
参数:$(FileName)
初始目录:$(FileDir)
动作:选择“捕获输出”

菜单文字:java运行
命令:java
参数:$(FileNameNoExt)
初始目录:$(FileDir)
动作:选择“无”

如果java文件是带包名的,则上面的java运行方式会报错,需要改为如下配置方式:
1.新建一个组

菜单文字:java运行(带package)
命令:java
参数:$(CurSel).$(FileNameNoExt)
初始目录:$(FileDir)
动作:选择“无”

2.当运行带包名的.class文件时,需要选择包名(这点非常重要),然后点击菜单项“java运行(带package)”,即可,如下图:


结束:
另外一个稍强点的编辑器UltraEdit,也能对相关编程语言进行类似本文的配置,见本博客UltraEdit配置C、C++及Java的编译和运行环境