数字证书

         前段时间接手了一个兄弟公司移交过来的项目,其中涉及资金交易安全的模块,采用了数字证书进行加密通讯,学习过程中走了不少弯路。目前工作终于告一段落,总算可以好好整理一下数字证书加密通讯相关的知识了。

         数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。最简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名(来源:百度百科)。换句话说,数字证书就是公钥、签名者及被签名者身份信息的载体,也就是说,签名者证明被签名者的真实有效的身份。比如支付宝首页打开就可看到url地址栏中的绿色标识,点击可看到该证书是第三方证书机构VeriSign公司证明了支付宝的真实身份(如果VeriSign系统被攻破,黑客用它来给危险的网站签名身份,那就危险了。。。当然这种可能性应该微乎其微,毕竟VeriSign可是第三方证书机构的佼佼者)。

        提到证书,就离不开加密,加密算法主要分两种:对称加密非对称加密。两者很好区分,加密解密使用的是同一个密钥,则属对称加密;而非对称加密解密使用的是两个不同的密钥——公钥和私钥,其中公钥加密,私钥解密。对称加密包括DES(Data Encryption Standard),3DES(DES的增强),RC2,RC4,RC5,Blowfish以及IDEA(International Data Encryption Algorithm,也是对DES的增强)等;非对称加密有RSA(使用最广泛)Elgamal、背包算法、Rabin、D-H(Diffie-Hellman)ECC(椭圆曲线加密算法)。算法真多,具体内容可百度谷歌。

        公司的项目中主要用的是RSA非对称加密算法,所以它是本文的重点。非对称加密,简单讲,就是数据发送方用公钥加密,数据接收方用私钥对密文解密(公钥和私钥成对出现,两者中任意一个都可作为公钥,而另一个作为私钥)。使用非对称加密通讯的基本原理是:

1.两个系统A和B之间要加密通讯,首先A和B都要分别生成自己的公钥和私钥。

2.A和B的公钥交换:A的私钥自己保密,把公钥告诉B;B的私钥自己保密,把公钥告诉A

3.A给B发送信息时,A用B的公钥加密信息,然后发送给B。

4.B收到A发过来的消息后,B用自己的私钥解密该消息。

5.同样的,B可以用A的公钥将响应信息加密后发送给A,然后A用自己的私钥解密。

在整个通讯过程中,即便有人截获了A与B之间的通信报文,但由于用来解密的私钥在A与B自己手里,所以截获者最终拿到的是无意义的密文,从而保证了通讯的安全。

Chrome扩展开发中的桌面通知管理

第一次在扩展中使用桌面通知功能,默认使用的webkit API接口,实现很简单:

var notification; 
function showNotification(content){    
    notification = webkitNotifications.createNotification('','提醒',content);  
    notification.show();
    setTimeout("notification.close()",5000);
}

本来相安无事,但如果在5秒之内连续发出多个通知呢?毕竟notification对象只有一个,每调用一次showNotification方法,该变量就会指向最新创建的通知对象,也就是说,只有最后一个窗口才会“如约”在5秒后自动关闭。为了解决这个问题,搜索网络终不得结果,最后想到用一个数组来存放多个通知对象,思路如下:

1.假如有N个通知对象,按先后顺便,存放在一个数组里。

2.第一个通知显示5秒后,从数组中取出该对象,调用关闭方法并从数组中移除,第二个、第三个、第N个依此类推。

var notifications = new Array(); 
function showNotification(content){    
    notification = webkitNotifications.createNotification('','提醒',content);  
    notification.show();

    notifications.push(notification);
    var index = notifications.length - 1;
    setTimeout("notifications["+index+"].close();notifications.splice("+index+",1)",5000);
}

这样看着似乎可行,但实际上有问题,每个通知对象从数组中被删除时,需要依靠下标来定位,然而当一个对象被删除后,数组中的其余对象的下标也就发生了变化,也就是导致对象定位不准的问题。于是决定弃用数组,最终采用了JSON对象:

var notifications = {};//通知容器
function showNotification(content){
	notification = webkitNotifications.createNotification("","提醒",content);
	notification.show();
	
	var k = "_" + new Date().getTime() + Math.random();//生成随机key
	notifications[k] = notification;
	setTimeout("notifications['" + k + "'].close();delete notifications['" + k + "']",5000);
}

感觉很不错,采用JSON格式的优势就在于,不用考虑下标的问题,只需用一个随机字符串作为key与对象一同保存到json对象容器中,理论上可以容纳任意多个通知对象,并且可以有条不紊地依次删除。

用最简单的事实说话:什么是Cookie,Cookie数据泄漏的危害

一、什么是Cookie

        简单来说,Cookie是网站在用户浏览器中保存下来的一份个人信息,有了Cookie,用户下次访问该网站时就可以免输入用户名甚至密码了(比如登陆淘宝网,用户名一栏就自动填充了,而为了安全,密码是必须的),因网站而异。另外,用户使用账号登陆网站后,要靠Cookie来维持会话Session,以保持在线状态,一旦清空当前网站对应的所有Cookie数据,用户将立即下线。真啰嗦,看图:

这是在OSChina登陆状态下的Cookie。接下来把它清除,然后刷新,结果如下:

可以看出,没有了Cookie,用户马上变成了游客身份,也就是用户维持登陆状态的信息已经不存在了。所以说,如果没有Cookie,许多网站都登陆不上去了。再比如,当用户访问京东等购物网站时,在不登陆的状态下,可以直接将选中的商品加入购物车,其原理也是基于Cookie来记录用户的购物车数据,只要不清除它,下次再打开该网站时,购物车依然能够显示添加的商品。

二、Cookie数据泄漏的危害

        又以上面OSChina为例,如果我打开第二个浏览器(猎豹),以游客身份访问oschina.net,然后把之前浏览器(Chrome)Cookie中的oscid值取出来,注入第二个浏览器中,结果会怎么样呢。

通过脚本注入这个oscid值,然后刷新,即可看到,游客身份变成了在线用户。也就是说,有的网站就是通过一个或多个cookie值来映射到一个会员ID,只要任何一台电脑的任何一个浏览器中包含这些Cookie值,就可以实现会员自动登陆。当然,这个cookie值会有一个过期时间(由网站系统响应头决定),以保证一定的安全性,另外补充一点,oschina对cookie的oscid值禁用了js读取或者httponly,以防网站未知的XSS跨站脚本漏洞导致用户账户被盗,所以为了演示的需要,我是从浏览器的设置功能中取出来的。

    cookie的作用不小,给用户和网站经营者带来很大的便利,但同时不可避免带来一些安全隐忧,只要我们保持安全意识,不在公共电脑上登陆敏感账户,我们就能趋利避害,享受cookie。

KindEditor+SyntaxHighlighter个性化修改

        我的个人博客用的富文本编辑器是KindEditor,这是国产开源软件中的一款精品,但作为程序员,它的默认语法高亮用的是google prettify,整体色调比较素雅,为了满足实际需求,也引入了SyntaxHighlighter作为第二个语法高亮工具,国内许多著名网站都用了这个工具,如CSDN和开源中国。关于二者的结合方法,网上很容易找到,本文也会简单介绍。

        在我的wordpress博客中使用的对应插件是Kindeditor For WordPress+SyntaxHighlighter Evolved。我发现,大多情况下,使用SyntaxHighlighter进行语法着色即可,但如果代码行数较少,使用Kindeditor默认的prettify则更好看。于是我决定对Kindeditor做些微的改动,使我在输入代码时可自由选择使用何种着色效果:SyntaxHighlighter or prettify先看效果:

public static void main(String[] args){
    System.out.println("hello,this is colored by SyntaxHighlighter!");
}
public static void main(String[] args){
    System.out.println("hello,this is colored by prettify!");
}

两者唯一的区别就是在输入代码框时,我加入了一个自定义的复选框,如果勾选,则表示使用prettify,否则默认SyntaxHighlighter :

这样我就实现了两种语法高亮工具同时使用,鱼和熊掌兼得。下面再看我对源码的一点修改。

1.修改Kindeditor For WordPress/plugins/code/code.js

KindEditor.plugin('code', function(K) {
	var self = this, name = 'code';
	self.clickToolbar(name, function() {
		var lang = self.lang(name + '.'),
			html = ['<div style="padding:10px 20px;">',
				'<div class="ke-dialog-row">',
				'<select class="ke-code-type">',
				'<option value="js">JavaScript</option>',
				'<option value="html">HTML</option>',
				'<option value="css">CSS</option>',
				'<option value="php">PHP</option>',
				'<option value="pl">Perl</option>',
				'<option value="py">Python</option>',
				'<option value="rb">Ruby</option>',
				'<option value="java">Java</option>',
				'<option value="vb">ASP/VB</option>',
				'<option value="cpp">C/C++</option>',
				'<option value="cs">C#</option>',
				'<option value="xml">XML</option>',
				'<option value="bsh">Shell</option>',
				'<option value="">Other</option>',
				'</select>',
				'<label>&nbsp;&nbsp;<input type="checkbox" id="isPretty">使用prettify</label>',//1.加入自定义的选择按钮
				'</div>',
				'<textarea class="ke-textarea" style="width:408px;height:260px;"></textarea>',
				'</div>'].join(''),
			dialog = self.createDialog({
				name : name,
				width : 450,
				title : self.lang(name),
				body : html,
				yesBtn : {
					name : self.lang('yes'),
					click : function(e) {
						var type = K('.ke-code-type', dialog.div).val(),
							code = textarea.val(),
                                                        //2.根据是否选择prettify生成相应的样式
							cls = type === '' ? '' : (isPretty.checked ? (' lang-' + type) : type),
							html = '<pre class="' + (isPretty.checked ? 'prettyprint':'brush:') + cls + '">\n' + K.escape(code) + '</pre> ';
						if (K.trim(code) === '') {
							alert(lang.pleaseInput);
							textarea[0].focus();
							return;
						}
						self.insertHtml(html).hideDialog().focus();
					}
				}
			}),
			textarea = K('textarea', dialog.div);
		textarea[0].focus();
	});
});

其实很简单,其他代码不用太关注,就只修改了两处(有注释)。

2.修改Kindeditor For WordPress/plugins/code/prettify.css

.ke-content pre {
     border: 1px solid #ddd;
     border-left: 5px solid #6CE26C;
     background: #f6f6f6;
     margin-left: 2em;
     padding: 0.5em;
     font-size: 110%;
     display: block;
     font-family: "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
     margin: 1em 0px;
     white-space: pre;
}

pre.prettyprint {
     border: 0;
     border-left: 3px solid rgb(204, 204, 204);
     margin-left: 2em;
     padding: 0.5em;
     font-size: 110%;
     display: block;
     font-family: "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
     margin: 1em 0px;
     white-space: pre;
} 

更简单,在原来的样式pre.prettyprint前面加入一个样式.ke-content pre(其实就是对pre.prettyprint复制过来再修改了一点颜色)。这个样式文件的修改并不是必须的,他只是在文本编辑期间,让正在编辑的代码可以被简单预览,而在文章发布出去以后,这个样式基本就不会被使用了。

这样,我的两个着色工具就可以自由切换使用了。

最简单的SSH代理客户端实现

    我的SSH帐号是通过香港的虚拟主机附赠的,目前主要是用浏览器插件(比如SwitchySharp)+ MyEnTunnel/Bitvise + 远程主机SSH代理服务的模式实现的。

    一直以来相安无事,但前几天主机商把SSH服务的监听端口由默认的22改为了2289,在家里将相应的端口也改过来即可恢复正常,但在公司就没那么幸运了。公司内部网络对外网的端口访问作了限制,只能访问标准的如80,8080,21,22等端口,很不幸,2289端口不在此范围内。几天下来出不了墙,很是焦虑,后来在公司的几台测试用linux服务器上通过telnet myhost.com 2289测试可用,太幸运了,至少找到了一个可供代理出口的机器,但是在linux机器上我用什么软件来替代MyEnTunnel/Bitvise呢,后来基于这个思路去寻找相关替代品,终于在走了这段弯路之后,发现了SSH命令本身就具备动态转发的功能。十分简单:

usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
           [-D [bind_address:]port] [-e escape_char] [-F configfile]
           [-I pkcs11] [-i identity_file]
           [-L [bind_address:]port:host:hostport]
           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
           [-R [bind_address:]port:host:hostport] [-S ctl_path]
           [-W host:port] [-w local_tun[:remote_tun]]
           [user@]hostname [command]

只需挑出几个必需的参数即可:

root@testserver [~]# ssh -D 0.0.0.0:7077 -p 2289 myname@myhost 

然后根据提示输入相应的用户密码即可,这样就完成了在公司内网代理服务器的实现(相对于我的办公电脑它是服务器,相对远程虚拟主机它是客户端)。当然也可以写自动化shell脚本,但因为是公用测试机器,就没这个必要了,而且可避免其他人看到我的账密信息。

参数说明:
-D 表示启用基于socks的动态端口转发,监听本地7077端口,0.0.0.0:7077表示将所有IP发送到本地7077端口的请求转发到远程myhost主机2289端口

-p 远程接收请求的SSH服务端口

sqldeveloper4.0无法启动的问题

    sqldeveloper4.0的配置在windows环境有所变化。当我下载并打开sqldeveloper4.0时,会提示本机JRE安装路径,设置好后,会接着弹出一个错误提示,如下:

网上目前的解决方案都是针对4.0以前版本,目前在windows版本中如下配置即可(以XP为例):

1.在系统安装盘搜索sqldeveloper用户数据目录
    找到C:\Documents and Settings\Administrator\Application Data\sqldeveloper

2.点击进入,最终找到文件C:\Documents and Settings\Administrator\Application Data\sqldeveloper\1.0.0.0.0\product.conf

3.修改配置项AddVMOption
将AddVMOption -Xmx800m修改为AddVMOption -Xmx600m

重新启动sqldeveloper.exe,运行成功!
(关于AddVMOption配置,可根据自己机器配置情况调高或高低)

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就能够真正被执行。

阅读清单2013

        许多聪明的人都喜欢读书,而且能够从中领悟许多东西并灵活运用到生活当中,给人以知识渊博和无穷智慧的深刻印象,对此我万分羡慕。无奈不太喜欢读书而且可能有点阅读困难症(dyslexia)的我在认识到这点后,也装模作样在2013年读了几本书,虽不多,但总比前几年要好吧。

1.《番茄工作法》

关于工作方法上的一些建议,强调按人的注意力与时间的关系来合理工作,最好是把工作分成一个个番茄钟,每个番茄钟25分钟,每个任务要纳入“任务清单”,每天的任务就是来自从这个任务清单中挑出最紧急的,放入“今日待办”,然后每天工作完成后把完成情况进行总结记录,过段时间就要对自己的工作规划进行调整,反思,改进。说实在的,这个东西是好的,但目前对于一个奔三但早已养成坏习惯的我来说,完好地执行起来确实有点难,而且现实的工作中情况也挺复杂的,内部中断和外部中断比较多,目前由于认识到了这一点,所以我会有意识地减少自己内部中断的次数,提高工作效率。

        这是在网上看别人博客时了解到的,内容不多,附有许多轻松贴切的插图,其体现的更多的是工作中的一种好方法及良好习惯的养成,确实是一本好书,但我不得不承认,这段时间我也确实没有把这份道理很好地坚持下来,甚感愧疚。

2.《读了明朝不明白》
就是关于明朝的那点事儿。明朝在中国历史上是一个距离我们今天较近的一个朝代,但是它又是一个十分特殊的朝代,自从朱元璋开创明代天地以来,为了加强皇权,采取了一系列措施,严禁宦官专权,严禁书生文人及老百姓思想觉醒。导致后面的皇帝成立了东厂、西厂、锦衣卫等机构,更搞笑的是,朱元璋严防宦官专权的措施号称是中国历史上最严格的,但明朝恰恰是中国历史上宦官专权最严重的。

        回顾历史,感悟时代的变迁,也许我们能够从中获得思想上的启迪甚至解放。

3.《戈壁天港》--航天系列丛书之一
关于中国航天科技发展及世界航天发展状况的科普读书。其中特别描述了中国酒泉载人航天城的相关设施及发射飞船的流程,有点专业,但也算是对我扫了点盲吧。这年头,科技引领世界进步,科普知识还是要适当补充一点的。

4.《创客:新工业革命》
这是在参加oschina源创会,搞开发板的一位老师在台上介绍硬件开发时,从他的演讲PPT里看到的,回家马上在网上买了这本书。这本书内容很新,最大的体会是:中国人的思维与西方人还是有很大的差距。3D打印技术在本书着墨较多,随着开源硬件的兴起,标志着我们个人创造硬件产品的时代已经到来。

5.《生死疲劳》

2013年看过几本没什么营养的言情小说,但都是在手机上看的,不过都没看完,因为后面的章节要收费了。但唯有这本,莫言那小子写的,这是他拿到诺贝尔文学奖后,我读的他的第一本小说,果断在亚马逊买下电子书,不贵,¥2.99,从此开创我阅读收费电子书的先河,是该为中国的正版伸出力所能及之手了。

        这本小说读来感慨良多,让我领教了一种从未体验过的小说写作风格。全篇以地主西门闹被杀后,阎王把他依次转世投胎为驴、牛、猪、狗、猴,最后终于转世为人的故事。以他的不同转世身份(猴及人的内容较少,除外)和蓝解放为第一人称交替讲述着这个从1950年1月1日开始,时间跨度近60年,涉及四代人的展现新中国时代变迁的漫长故事。

几个印象深刻的情节:(1)让我在读时竟然流下眼泪:蓝解放及庞春苗的坚韧爱情,以及他儿子蓝开放与庞凤凰的爱情结局,让莫言这小子写得十分悲剧赚眼泪。(2)让我最童心未泯最觉精彩的:人猪大战令人回味无穷,原来那个时代的生态环境如此美丽怡人。

        莫言的小说确实是靠着实力,对人物和环境细节的描写十分细致贴切,似乎还带着一种讽刺。这又让我模糊想起了大学时读过的被堪称莫言代表作的经典——《红高粱家族》,故事总是那么生动而让人印象深刻。

        此外我还想起了那时读过的另一本小说——路遥的《平凡的世界》,小说很长,也是以解放后的中国为时代背景,也是让我饱含热泪的情节:孙少平与田晓霞又是阴阳两隔,好不唏嘘,人的命运,唉。。。


什么是好书,它一定能让人引起共鸣,引起反思,细细咀嚼,回味无穷,悄悄地改变着我们的头脑,让我们变得更聪明。

当当网,我伤不起

    当当网,这个响当当的购物网站,虽然这些年用得不多,但偶尔也会买些书籍。这不,这几年在这家公司,每年会收到公司发给员工的生日礼品卡,这些礼品卡就是来自当当网,所以上当当网成为了一种必需。本来以为这种方式会相安无事地继续下去,但最近的一次当当网帐号被盗事件,让我最终选择离开。

    今年6月,我把礼品卡充入当当网帐户,当时帐户余额约100余元。到了11月份,打算买本书,未曾想我的当当网帐户进不去了,反复登陆我可能用到的用户名和密码,就是进不去,通过找回密码功能,居然提示我的帐户不存在。难道被盗了?上网搜索发现,去年(2012)当当网发生了一场巨大的帐号被盗事件,无数用户遭受不同程度的损失。尽管当当网声明将全额赔偿用户,并且也有不少网友撰文通过各种努力(包括在微博留言李国庆)获得了赔偿,但也有许多就这样无声无息了。没想到去年的风波竟然在归于平静一年多后又波及我等小辈,实在无聊。

    最后经与当当多次电话和邮件得知我的帐户已被注销,礼品卡余额在10月份已被盗用,已为我恢复了帐号。但最要命的是,当当最终回复我的是:已报警,等有结果了自然会通知您。这是玩哪般啊,我犯错了吗,为什么不赔偿我,我承认我的密码没有及时修改,但当当网应该负首要责任:

1.去年(2012)当当网出现过用户余额被盗用事件,声明会加强系统安全,也就是所谓系统安全加强一年多后我的帐户却被盗。

2.当当网的执行力有问题。有用户反映自己的余额被盗用后商品正在寄往陌生人的地址,请求当当网拦截物流,但许多都失败了。

3.最严重的,普通的当当网用户根本无法将自己的帐户注销。这意味着什么,有人用高于普通用户的权限或漏洞进入了当当网帐户系统篡改用户数据,修改用户的验证手机号,使得盗买商品时户主无法及时接收到短信提醒,并将该用户帐户注销。或者说,当当网内部有人监守自盗,直接篡改数据。即便CSDN帐户信息泄露,也绝非可能做到销户。

    最后,我想说的是,目前的当当网帐户系统肯定是百分之百不安全。我的猜想是,有人已经非法获取了当当网大量帐户信息,并在有需要时会光顾您。什么叫有需要时?如果你的帐户没有多少余额,可能你的帐户是“安全的”,因为偷窃者不会打草惊蛇,待你余额可观,或者值得“拿过来”时,那你就会遭遇跟我一样了。不信?你充几百块钱到当当网帐户并存放几个月试试(豆瓣网有网友反映帐户被盗并恢复帐户之后一个星期再次被盗)。总之,基于这种情况,即使你修改密码加手机验证也没用,因为偷窃者是通过当当网系统安全漏洞或内部员工管理漏洞以高级权限篡改数据的。怎么办?远离当当是唯一选择。

补充:今天网上有人传言当当网缺钱(被盗如此严重,要承诺如去年般的全额赔偿,能不缺钱?难怪不赔偿我),李国庆正考虑为当当网寻找买家。

http://www.techweb.com.cn/internet/2013-12-13/1369556.shtml

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中。