EntityFramework之领域驱动设计实践【后续篇】:基于EF 4.3.1 Code First的领域驱动设计实践案例

两年前我在博客中发布了《EntityFramework之领域驱动设计实践》系列文章,也得到了广大读者朋友的关注,在完成了系列文章的总结之后,也一直没有这部分内容的更新了。现在,Entity Framework的稳定版(就是那个Stable的版本,不是Entity Framework 5的beta版本)4.3.1已经逐步应用到各种.NET项目中,为了演示Entity Framework 4.3.1 Code First编程模式以及其它的一些.NET技术在领域驱动设计实践上的应用,我重新采用经典的分层架构(也就是类似Microsoft NLayerApp的区别于CQRS的架构)实现了一个案例程序:Byteart Retail。在这个案例中,仓储的实现不再采用NHibernate,而是使用的Entity Framework 4.3.1 Code First。虽然这个案例较为完整地从各方面展示了.NET技术在企业级程序中的应用,但Entity Framework确实是其亮点之一,因此,我将这篇介绍这个案例的文章也编排在原来的EF系列文章中。

案例概述

Byteart Retail以笔记本电脑在线销售业务为背景,展示了Microsoft.NET技术以及领域驱动设计理念在软件设计、架构与实践中image的应用。在Byteart Retail之前,开源社区也有一些领域驱动设计的实践案例,比如Microsoft Domain-Oriented NLayered Application Architecture、面向CQRS体系结构模式的Tiny Library CQRS等。与这些案例相比,Byteart Retail在领域驱动的实践指导方面有一定的相似性和可比性,但它更注重Microsoft.NET技术与领域驱动设计相结合。比如,Byteart Retail案例对基于Entity Framework 4.3.1版本中Code First模式的仓储实现进行了全方位的演示,这样的Entity Framework仓储设计,使得领域模型对象能够完全设计为POCO对象而不需要依赖任何其它技术框架,因此,在对仓储的选择和使用上,就能够做到“无缝替换”。另一方面,领域模型的设计也更为考究,实体、值对象的设计、聚合的划分等,都与系统业务紧密结合,相对于其它的演示案例更为成熟。从实现的业务逻辑上看,Byteart Retail大致实现了以下功能:

 

  1. 笔记本电脑商品的浏览
  2. 客户账户注册和基本信息查询与修改
  3. 笔记本电脑详细信息查询
  4. 客户添加笔记本电脑商品到购物篮
  5. 购物篮商品项目管理
  6. 从购物篮创建销售订单
  7. 销售订单的确认与查询
  8. 销售订单明细查询

案例对以下.NET技术和开发技巧进行了演示:

  1. Microsoft Entity Framework 4.3.1 Code First
  2. ASP.NET MVC 3
  3. WCF
  4. Microsoft Patterns & Practices Unity Application Block
  5. 使用AutoMapper实现DTO与领域对象映射
  6. T4自动化代码生成

案例下载

【单击此处】下载本案例的所有源代码和Visual Studio 2010的解决方案文件。

系统需求

开发环境:Visual Studio 2010 Professional/Ultimate with SP1,ASP.NET MVC3。其它的程序集引用都在压缩包的packages目录下,因此读者无需上网下载安装其它组件。

安装部署

数据库

Byteart Retail采用Microsoft SQL Server 2008作为后台数据库。首先,修改ByteartRetail.Services项目下的web.config文件,对数据库链接字符串进行配置:

image

然后,按照下面“程序启动”部分的描述,启动ByteartRetail.Web项目,此时Entity Framework会根据上面的连接字符串创建一个名为ByteartRetail的数据库。

最后,打开SQL Server Management Studio,执行压缩包中SQL目录下的ByteartRetailData.sql文件即可将所需的测试数据导入ByteartRetail数据库中。

说明:这种数据库的部署和初始化方式虽然能够规避“Model compatibility cannot be checked because the database does not contain model metadata.”的错误,但仍然不是一个很理想的部署方式。在这里我们暂时采用这种方式让案例先运行起来,以后我会找出一个更合理的办法并对这部分内容进行更新。

程序启动

使用Visual Studio打开解决方案并完成编译,然后在ByteartRetail.Services项目中任选一个.svc文件点击右键,并选择View in Browser选项以启动WCF Service;之后,直接运行ByteartRetail.Web项目,即可出现主界面。

设计概要

在此我先将部分设计的类图贴出,以方便读者朋友在查看源代码的过程中参阅。

领域模型

 

基于Entity Framework的仓储设计(省略属性与方法)

 

规约设计

 

企业应用架构模式参考

本案例大致涉及到了以下企业级应用架构模式,也一并列举于此,供读者朋友们参考,也可以作为学习《Patterns of Enterprise Application Architecture》、《Core J2EE Patterns》等书籍的参考。

总结

热烈欢迎爱好Microsoft.NET技术以及领域驱动设计的读者朋友对本案例进行深入讨论。有疑问或建议请直接留言回复。

转载至:http://www.cnblogs.com/daxnet/archive/2012/04/16/2452660.html

发表在 C# | 留下评论

Byteart Retail V3 – 全新的面向.NET与领域驱动设计的企业应用实践案例

经过近一个多月的努力,我使用自己的业余时间在V2的基础上对Byteart Retail案例重新打造,使得V3以一种全新的面貌出现在关注.NET企业级架构和领域驱动设计的读者朋友面前。与前两个版本相比,V3无论在界面上,还是在业务方面,甚至是技术方面都有了很大的进步。虽然系统完善了不少,但毕竟我没办法将全部时间精力SNAGHTML439a24d都投入在这一开源项目上,所以V3肯定还存在不少缺陷,因此希望朋友们能够谅解,如果遇到问题,可以自己试着研究并动手解决,我想这样会对自己有很大的帮助。如果实在无法解决,或者有好的想法和建议,也欢迎在此留言与我联系。对Byteart Retail不太了解的朋友,可以点击下面的链接以了解前两个版本的详细信息,一些相关的设计思路和架构视图,我就不在此重复了。

EntityFramework之领域驱动设计实践【后续篇】:基于EF 4.3.1 Code First的领域驱动设计实践案例

Byteart Retail V2 – 基于Entity Framework Code First的领域驱动设计实践案例

案例源代码下载

请【单击此处】下载本案例的所有源代码和Visual Studio 2012解决方案文件(zip压缩包)。很多朋友建议我将本案例转移到类似codeplex或者GitHub的repository中,由于时间关系,暂时还没能将其转移到这些网站中,因此V3仍然以zip包下载的方式提供给大家,还望海涵。我争取在后续的开发过程中,将其加入codeplex或者GitHub中。

运行案例程序

先决条件

从V3开始,本案例使用Visual Studio 2012开发,因此,要编译本案例的源代码程序,则需要首先安装Visual Studio 2012。由于数据库采用了SQL Server Express LocalDB,因此,这部分组件也需要正确安装(如果是选择完整安装Visual Studio 2012,则可以忽略LocalDB的安装)。此外,无需安装其它组件。

编译运行

将下载的ByteartRetail_V3.zip文件解压到一个本地的磁盘目录下,然后在Microsoft Visual Studio 2012中打开ByteartRetail.sln文件,再将ByteartRetail.Web项目设置为启动项目后,直接按F5(或者Debug –> Start Debugging菜单项)运行本案例即可。注意:

  1. 如果不打算以Debug的方式启动本案例,那就需要首先展开ByteartRetail.Services项目,任选其中一个.svc的服务文件(比如UserService.svc)然后点击右键选择View In Browser菜单项,以便启动服务端的ASP.NET Development Server;最后再直接启动ByteartRetail.Web项目
  2. 由于Byteart Retail V3的数据库采用的是SQL Server 2012 Express LocalDB(默认实例),在程序连接LocalDB数据库时,LocalDB需要创建/初始化数据库实例,因此在首次启动时有可能会出现数据库连接超时的异常,如果碰到这类问题,则请稍等片刻然后再重试。MSDN上有关于这个问题的描述:
    The first time a user on a computer tries to connect to LocalDB, the automatic instance must be both created and started. The extra time for the instance to be created can cause the connection attempt to fail with a timeout message. When this happens, wait a few seconds to let the creation process complete, and then connect again.

  3. 如果以上述第一点的方式运行ByteartRetail.Web项目并出现与WCF绑定相关的错误时,这表示WCF服务并没有完全启动,请重新启动ByteartRetail.Services项目,然后再启动ByteartRetail.Web项目

登录账户

启动成功后,就可以单击页面右上角的“登录”链接进行账户登录。默认的登录账户有(用户名/密码):

  • admin/admin:以管理员角色登录,可以对站点进行管理
  • sales/sales:以销售人员角色登录,可以查看系统中订单信息并进行发货等操作
  • buyer/buyer:以采购人员角色登录,可以管理商品分类和商品信息
  • daxnet/daxnet:普通用户角色,不能对系统进行任何管理操作

解决方案结构

ByteartRetail.sln包含以下项目:

  • ByteartRetail.Design:包含一些设计相关的图画文件,仅供参考,没有实际意义
  • ByteartRetail.Application:应用层
  • ByteartRetail.DataObjects:数据传输对象及其类型扩展
  • ByteartRetail.Domain:领域层
  • ByteartRetail.Domain.Repositories:仓储的具体实现(目前是基于Entity Framework 5.0的实现)
  • ByteartRetail.Infrastructure:基础结构层
  • ByteartRetail.Infrastructure.Caching:位于基础结构层的缓存实现
  • ByteartRetail.ServiceContracts:基于WCF的服务契约
  • ByteartRetail.Services:WCF服务
  • ByteartRetail.Web:基于ASP.NET MVC的站点程序(表示层)

以下是各项目之间的依赖关系:

image

改进的内容

与之前的版本相比,V3更多的是在业务方面有了新的功能,同时在技术方面也有一些改进。

业务方面

  1. 添加了商品分类功能,丰富了原有的商品种类划分
  2. 添加了用户角色功能
  3. 添加了商品信息的维护功能
  4. 添加了“特色商品”的功能
  5. 完善了订单状态
  6. 优化了界面效果,提高了用户体验(菜单、分页等)

技术方面

  1. 使用ChannelFactory实现WCF的调用,替代了原有的直接使用Service Reference的方式,为后续的WCF认证和授权做准备
  2. 在配置文件中加入了与Byteart Retail相关的配置节点,这包括:分页设置以及角色的映射信息等
  3. 通过AOP拦截的方式引入缓存机制,使用Patterns & Practices Enterprise Library Caching Application Block为WCF服务提供数据缓存机制。在今后的版本更新中将引入Appfabric Cache作为缓存供应者
  4. 更新了仓储(repository)的接口定义,实现了基于Entity Framework的分页功能
  5. 演示了Entity Framework 5.0对枚举(enum)类型的支持

总结

时间关系,暂时先介绍这么些内容,有关技术方面的细节,我打算以系列文章的方式详细介绍,争取能够涉及到Byteart Retail项目的各个角落。Byteart Retail案例将会继续发展下去,无论在业务方面还是在技术层面,都将会有新的内容加入。我也会根据大家的建议,更早地将其发布到codeplex上,以便大家能够随时跟踪本案例项目的状态。

其实平时做这些工作的时候,也会要顶着不小的压力,一方面有时候工作上的事情会很多,当然需要把手头工作放在首位;另一方面来自生活的压力,也有周围的人会觉得我做这些事情也是毫无收益,又是何苦。在此也谈谈自己的感受吧。虽然国内技术牛人很多,比我技术做的好的也不少,但我们的软件技术水平跟国际上的一些国家之间还是有一定的差距,当然,“环境因素”暂且放一边,“国内水平”、“国际水平”的分析也先放一放。我们就说说自己能做的事情,我也不指望自己能够给目前的这些“现状”带来多大的改变,因为自知实力有限,但我觉得我能够一些事情,或者说能够给他人带来一些影响。就拿现在在社区博客中做的事情来说,或许我做的这些能够影响到他人,比如让一些朋友少走弯路,或者帮另一些朋友理清了解决问题的思路,我想这就是很好的。你存在于世界上,你对其产生了影响,也算是一种成就。事实上一生中能做的事情是有限的,能做好的更是少之又少,关键是贵在坚持吧。最后也感谢一下那些支持我的朋友们。

转载至:http://www.cnblogs.com/daxnet/archive/2012/11/08/2760473.html

发表在 C# | 留下评论

深度剖析Byteart Retail案例:前言

背景

Byteart Retail是一个面向领域驱动的.NET企业级应用架构设计案例,到目前为止已经发布了三个版本。有关这三个版本的详细介绍,请参考以下几个链接:

为了向读者朋友详细介绍Byteart Retail项目,我打算写一个系列专题文章,对案例的各个方面进行详细介绍。随着Byteart Retail的版本更新,发表在本博客的文章不一定会保证100%同步更新,但在系列文章结束的时候,我会给出一个PDF格式的电子版,这个电子版文档是会与最新版的案例保持同步的,有兴趣的读者敬请关注。

前言

从2007年至今,我一直关注着与领域驱动设计相关的软件开发过程与技术,在这几年中,我坚持不懈地学习、实践,在总结自己实践经验的基础上,设计并开发了一套基于.NET的面向领域驱动的企业应用程序开发框架,沿用我以前开发的一个松耦合架构实验原型,为之取名为Apworks。为了向社区展示Apworks在企业级应用开发上给开发人员带来的便捷,我也针对Apworks框架开发了一套面向CQRS架构的案例程序:Tiny Library CQRS。随着Apworks的不断发展,Tiny Library CQRS也先后更新了两个版本,毋庸置疑,第二版更为成熟,更贴近于实际项目应用。

然而,社区对Tiny Library CQRS的反馈却不是那么积极,分析原因,我觉得有三个方面:首先,基于事件溯源(Event Sourcing)机制的CQRS架构本身就非常复杂,套用世界级软件架构大师Udi Dahan对CQRS架构的总结,就是:“简单,但不容易(Simple, but not easy)”,要让一个对企业级软件架构设计不太熟悉的开发人员掌握好CQRS相关的知识,是一件困难的事情,即使有现成的案例,也会让人感觉无从下手;其次,目前大多数应用程序还远没达到需要使用CQRS架构的规模,在项目中应用CQRS,只能把简单问题复杂化;再次,由于个人时间能力有限,Tiny Library CQRS案例本身也没有提供太多的文档与说明,加上该案例直接使用了Apworks框架,所以很多后台运行机制就变得不那么明朗,这对希望研究CQRS架构的开发人员造成了一定的困难。因此,Tiny Library CQRS感觉就与真实项目的实践脱节,自然关注的人就不多了。所以我打算暂时搁置Tiny Library CQRS的更新,让其也成为一个CQRS架构设计的参考原型,供有兴趣的朋友参观学习。

于是,从今年4月开始,我就着手开发并发展了另一个面向领域驱动的.NET企业级应用架构设计案例:Byteart Retail。与Tiny Library CQRS不同的是,Byteart Retail采用了面向领域驱动的经典分层架构,并且为了展示微软.NET技术在企业级应用开发中的应用,它所使用的第三方组件也几乎都是微软提供的:Entity Framework、ASP.NET MVC、Unity IoC、Unity AOP、Enterprise Library Caching等(用于记录日志的log4net除外,但log4net本身也是众所周知的框架),所以,开发人员只需要打开Byteart Retail的源程序,就能够很清楚地看到系统的各个组件是如何组织在一起并协同工作的。经典分层架构的采用,也为实际项目带来了参考和指导的价值。

image

图一 Byteart Retail V1&V2 界面,略显简单

Byteart Retail以一个在线零售系统为业务背景,目前经过了三个版本的演进,无论在业务方面还是在技术方面,都在逐渐完善。在第一版(V1)中,案例着重于框架的搭建,并偏重于后台技术的演示,因此用户界面仍然还是一个经典的ASP.NET MVC3界面,而且实现的业务功能也显得过于简单;第二版(V2)在第一版的基础上,完善了技术架构,引入了WCF Per-Request生命周期管理器(Lifetime Manager)、基于Unity的AOP拦截、异常处理和日志管理等企业级应用程序架构元素,相信它能够成为软件开发人员很好的学习材料。然而第二版在业务功能和用户体验方面,与第一版完全相同,作为企业级架构的演示案例,又显得不够完善。之后,我利用业余时间,历经3个多月打造了Byteart Retail的第三个版本(V3),这个版本进一步完善了业务功能,同时使用ASP.NET MVC 4的新技术,让用户界面焕然一新:基于jQuery的图片上传、级联菜单、数据分页等,让人感觉耳目一新,使Byteart Retail更接近于一个实际项目。虽然从业务上看,V3与实际应用之间还是有段距离,在今后我仍然会安排一定的时间来改进它的业务模型,但它毕竟是一个演示案例,所以核心业务部分不打算做得过于复杂,以方便开发人员对领域建模和技术架构的研讨。

image

图二 Byteart Retail V3 界面,比前两版丰富不少

为了能让感兴趣的读者朋友更好地了解到Byteart Retail的架构设计思路与方法,我在发布案例源代码的同时,编写了本文档。虽然从V2开始,我在源代码中,加入了不少中文注释,但毕竟注释的描述能力有限,对于一些设计上的动态特性,或许使用图表的表示方式会更加直观,因此,我觉得除了向大家展示出源代码之外,能让大家拥有这样一份文档是很有必要的。

image

图三 Byteart Retail案例整体架构概览,与微软NLayerApp的架构类似

本文会跟随“领域驱动设计”的经典分层方式,从基础结构层、领域层、应用层和展示层四大部分对案例进行介绍。展示层的介绍会相对简单一些,因为展示层更偏向于已有框架的技术应用,而且前台技术也不是我的技术强项,因此这方面的细节内容就只能靠读者们自己研究发挥了。在基础结构层部分,我将详细介绍Byteart Retail所使用的与技术相关的内容,比如Service Locator与AOP拦截、配置文件和配置信息读取、异常处理、缓存服务,以及基于Entity Framework的仓储实现;在领域层部分,我将针对Byteart Retail的业务功能对领域建模进行讨论,比如领域对象设计和聚合划分等;在应用层部分,我会讨论对象模型的失衡问题,同时还会向读者介绍一些应用层相关的框架和技术。在学习和讨论的过程中,我们会涉及到很多企业级应用架构模式,比如Service Locator模式(Core J2EE Patterns)、Unit Of Work模式(PoEAA)、Separated Interface模式(PoEAA)等;也会涉及到一些.NET/C#惯用法,比如Dispose模式不变对象(Immutable Object)、基于C#的Singleton等,在文章中我也会以注解的方式向读者阐述模式与惯用法在实际项目中的应用。希望能对学习企业应用架构模式的读者有一定的帮助。

最后,说说本案例名称的由来吧。Retail很简单,“零售”的英文单词,表示本案例的业务背景;而Byteart(字节艺术),则是我刚开始学习编程的时候,跟我一个同学组成的软件工作室名称。年轻人,对未来总会有很多梦想和期望,虽然除了一个简单的DirectX RPG游戏引擎和小霸王学习机上用BASIC语言开发的扫雷游戏(支持游戏手柄的喔)以外,我们并没有做成过什么,但当年那种为梦想而奋斗的激情却总是无法忘记。将Byteart用在本案例的名称中,以纪念那段美好的时光。

转载至:http://www.cnblogs.com/daxnet/archive/2012/11/13/2768175.html

发表在 C# | 留下评论

使用NuGet发布自己的类库包(Library Package)

NuGet是一个为大家所熟知的Visual Studio扩展,通过这个扩展,开发人员可以非常方便地在Visual Studio中安装或更新项目中所需要的第三方组件,同时也可以通过NuGet来安装一些Visual Studio的插件等。作为一名开发人员,您可能也会开发一些公共组件以供他人使用,本文将一步步介绍如何以最简单的方式将自己所开发的类库包发布到nuget上,以供更多的人使用。

背景

如果你还是不知道什么是NuGet,那么就看这样一个案例:我现在需要在我的项目中引用Castle.Core程序集,按照以往的做法,就是从Castle Projects官方网站,下载一个最新版本的dll,然后把它复制到项目的lib目录下(或者随便什么地方都行),这样做不仅繁琐,而且你需要时刻关心官网上这个程序集的最新版本信息(当然或许你也不会去关注),更烦的是,如果你是一个开源项目的Contributor,你还需要花一定的时间去管理所有的这些libs,不仅如此,如果你是使用的源代码管理系统来管理项目源码,比如使用git等,那你还不得不把这些libs上传到源代码管理系统中,否则团队中的其他组员即使获得了源代码,也无法正确编译。但这样做又大大增加了源代码的存储空间,使得代码克隆和下载都变得非常耗时。

现在,就可以直接使用NuGet来解决所有问题,我们先创建一个Class Library,命名为DaxnetNugetTest,然后在这个项目上点右键,选择Manage NuGet Packages:

image

在弹出的对话框中,搜索Castle关键字,然后在搜索结果列表中选择Castle.Core,单击Install按钮:

image

安装完成后,Castle.Core的程序集就被引用到项目中了,同时在项目中多出了一个packages.config文件,以向NuGet表明,当前项目使用了哪些Package,版本是什么,以及是基于哪个版本的.NET Framework。

今后,如果Castle.Core程序集有版本更新,则同样可以使用Manage NuGet Packages菜单打开上面的对话框,然后在左边的Updates列表中,就会列出发生了版本更新的Package,如果有,则单击Update按钮即可更新。

更有趣的是,如果你在解决方案上点右键,选择Enable NuGet Package Restore菜单,那么在你编译项目的时候,NuGet会自动分析出你项目所依赖的第三方组件,然后在编译开始之前会自动上网下载所需的版本,因此,你也就不要去维护这些libs了,更没必要把这些libs也上传到源代码管理系统中。

image

不过这些也都不是本文的重点,本文的重点是,介绍如何将自己的Class Library发布到NuGet上。

发布自己的类库包(Library Package)

STEP 1:在NuGet上注册并获取API Key

首先,你需要到NuGet上注册一个新的账号,然后在My Account页面,获取一个API Key,这个过程很简单,我就不作说明了。

STEP 2:下载NuGet.exe

NuGet有个命令行工具:NuGet.exe,非常好用,不过使用之前需要下载,下载地址:http://nuget.codeplex.com/downloads/get/669083。为了方便使用,请设置机器的PATH环境变量,将NuGet.exe的路径添加到PATH中。

STEP 3:设置API Key

使用以下命令设置NuGet API Key:

nuget setApiKey <my_api_key>

记得将上面的my_api_key替换为STEP 1中获得的API Key。

STEP 4:开发自己的类库(Class Library)

上面我们新建了一个类库:DaxnetNugetTest,并通过NuGet添加了对Castle.Core的引用,现在我们添加一些代码,来使用Castle.Core所提供的一些功能。我们将Class1.cs改名为CastleHelper.cs,此时也会将Class1类改名为CastleHelper。在CastleHelper.cs中写入以下代码:

public class CastleHelper
{
    public static Castle.Core.Pair<int, int> GetIntPair()
    {
        return new Castle.Core.Pair<int, int>(20, 30);
    }
}

然后,打开AssemblyInfo.cs文件,将assembly的属性设置好,记得再设置一下AssemblyVersion特性,以指定我们类库的版本。目前我们使用1.0.0.0版本:

[assembly: AssemblyTitle("DaxnetNugetTest")]
[assembly: AssemblyDescription("Daxnet's test of the NuGet.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("daxnet")]
[assembly: AssemblyProduct("DaxnetNugetTest")]
[assembly: AssemblyCopyright("Copyright ? daxnet 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

[assembly: ComVisible(false)]

[assembly: Guid("20662b9f-91de-4515-9c8c-ced3d61589e1")]

[assembly: AssemblyVersion("1.0.0.0")]

全部设置好以后,编译整个项目待用。

STEP 5:产生并修改nuspec

nuspec是NuGet将项目打包成nupkg的输入文件,可以通过nuget spec命令产生。在命令提示符下,进入DaxnetNugetTest.csproj文件所在目录,然后执行:

nuget spec

此时会提示创建成功:

image

用notepad打开DaxnetNugetTest.nuspec文件,把需要替换的信息替换掉,不需要的tag全部删掉,注意里面的$xxx$宏,这些就是引用了AssemblyInfo.cs中的设置值,在编译产生package的时候,会使用AssemblyInfo.cs中的相应值进行替换。完成编辑后,我们的nuspec文件如下:

<?xml version="1.0"?>
<package >
  <metadata>
    <id>$id$</id>
    <version>$version$</version>
    <title>$title$</title>
    <authors>$author$</authors>
    <owners>$author$</owners>
    <licenseUrl>http://www.apache.org/licenses/LICENSE-2.0.html</licenseUrl>
    <projectUrl>http://apworks.org</projectUrl>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$description$</description>
    <releaseNotes>First release</releaseNotes>
    <copyright>Copyright 2013</copyright>
  </metadata>
</package>

注意两点:1、$description$使用AssemblyDescriptionAttribute的值进行替换,在产生package之前,一定要记得先编译项目,否则会提示$description$找不到的错误;2、releaseNotes如果没有,就直接删掉这个节点,如果有,则填入自己的内容,不要使用默认内容,否则会在下一步产生警告信息。

STEP 6:产生类库包(Library Package)

同样在DaxnetNugetTest.csproj路径下,使用下面的命令产生NuGet类库包:

nuget pack DaxnetNugetTest.csproj

成功后,提示:

image

注意:由于我们的项目通过NuGet引用了Castle.Core,因此,它将会作为一个依赖组件(dependency)打包到产生的nupkg文件中。

另外,NuGet会使用默认的项目配置所产生的程序集进行打包。如果项目默认是Debug,而你需要用Release打包,则使用下面的命令:

nuget pack DaxnetNugetTest.csproj -Prop Configuration=Release

STEP 7:发布类库包

现在,通过以下命令发布类库包:

nuget push DaxnetNugetTest.1.0.0.0.nupkg

完成以后,出现以下提示:

image

STEP 8:测试已发布的类库包

新建一个控制台应用程序,在项目上点右键,选择Manage NuGet Packages,在搜索框中输入DaxnetNugetTest,此时我们发布的Package已经可以显示了:

SNAGHTMLecfd75

单击Install按钮,NuGet会自动分析组件依赖关系,然后把所需要的所有程序集都下载下来并添加到项目引用中:

image

写一点代码来测试:

class Program
{
    static void Main(string[] args)
    {
        var pair = DaxnetNugetTest.CastleHelper.GetIntPair();
        Console.WriteLine(pair.First);
        Console.WriteLine(pair.Second);
    }
}

输出如下:

image

STEP 9:更新类库包

随着类库开发进度不断向前,必然会有版本更新。更新类库包很简单,只需要在AssemblyInfo.cs中更新一下版本号,然后重新执行上面的STEP 6、7即可。注意在执行STEP 7的时候,nupkg的文件名应该使用新版本的文件名。

现在,我们重新打开DaxnetNugetTest项目,将CastleHelper类中的20,30改为40,50,然后打开AssemblyInfo.cs,版本号升级为2.0.0.0,重新编译项目,并重新产生、发布nupkg:

image

再打开用来测试的控制台程序,同样打开Manage NuGet Packages对话框,我们可以在Updates中看到,DaxnetNugetTest有了更新:

image

点击Update按钮,将类库更新到最新版本。重新运行这个控制台程序,我们发现,输出已经是最新版本的值了:

image

STEP 10:删除已发布的包

原则上,NuGet不允许用户删除已发布的包,而只能将其设置为不显示在Manage NuGet Packages的列表中。打开www.nuget.org,用已注册的账户登录后,可以在My Account页面选择Manage My Packages链接进入管理页面:

image

进入后,可以看到我们已发布的Packages:

image

点击DaxnetNugetTest左边的小垃圾桶图标,即可进入Listing页面,页面中我们也能看到“Permanently deleting packages is not supported”的提示。要将Package从Package List中移除,只需要去掉List DaxnetNugetTest 2.0.0.0 in search results选项前面的钩钩,然后单击Save按钮保存即可:

image

总结

本文简要介绍了NuGet的使用,并介绍了一种将自己开发的类库以NuGet Package的方式发布到NuGet服务器的简单方法。NuGet功能非常强大,有兴趣的朋友可以上www.nuget.org进行学习研究。

转载至:http://www.cnblogs.com/daxnet/archive/2013/05/07/3064577.html

发表在 C# | 留下评论

使用Microsoft Roslyn提取C#和VB.NET源代码中的字符串常量

Microsoft Roslyn是微软.NET“编译器即服务(Compiler as a Service)”的主要产品,它提供了开放的编译器API,并为源代码产生、分析和重构提供了新一代的语言对象模型。Anders Hejlsberg在BUILD 2013大会上提到,C# 6.0的编译器将使用Roslyn实现,这一实现会包含在Visual Studio 2013之后的产品中。根据Anders的描述,C# 6.0的编译器将采用C#开发,从而告别现有的本机代码(native code)的实现方式,“虽然是采用C#来实现C#编译器,但我想性能至少不会比原来的实现方式差。”

有关Roslyn的内容,可以参考以下链接:

让我们先睹为快,了解一下Roslyn的一个具体应用:提取C#和VB.NET代码中的字符串常量。

字符串常量的提取

先看下面的一段代码:

using System;
using System.Collections;
using System.Linq;
using System.Text;
 
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            // Output a "greeting" string
            Console.WriteLine("Hello, World!");
            // Output another "say hello" string
            Console.WriteLine("Hi, nice to meet you!");
        }
    }
}

很明显这段代码中有四个字符串:greeting、Hello, World!、say hello和Hi, nice to meet you!,或许我们可以通过正则表达式来提取这些字符串,但请注意:这些字符串中有两个是在注释语句中出现的,而不是我们所需要的字符串常量。我们只需要得到其中真正用于可执行代码的Hello, World!和Hi, nice to meet you!,这如果通过正则表达式来区分还是有一定难度的,而且对于字符串中的转义字符等特殊字符的判断和提取,正则表达式也略显麻烦。

现在,让我们用Roslyn来完成这一工作。首先,打开Visual Studio 2012/2013,新建一个控制台程序(Console Application),.NET Framework的版本选用4.5或者4.5.1。然后,在新建的控制台程序项目上单击右键,选择Manage NuGet Packages菜单项(注意:.NET Framework的版本必须是4.5以上):

image

在打开的对话框中,搜索Roslyn,并将Roslyn安装到项目中:

SNAGHTMLdbda5b

首先创建一个语法树的访问者,它继承于Roslyn.Compilers.CSharp.SyntaxWalker,用于遍历访问C#的语法树,它的实现如下:

class ExtractStringLiteralVisitor : SyntaxWalker
{
    readonly List<string> literals = new List<string>();
    public override void VisitLiteralExpression(LiteralExpressionSyntax node)
    {
        if (node.Kind == SyntaxKind.StringLiteralExpression)
            literals.Add(node.ToString());
        base.VisitLiteralExpression(node);
    }

    public IEnumerable<string> Literals { get { return literals; } }
}

然后,将上面第一段代码文本保存到一个名为source的字符串变量中(当然实际应用中也可以从文件读入源代码),并使用SyntaxTree产生语法树对象,之后使用上面的ExtractStringLiteralVisitor从根部对语法树进行遍历。由于重写的VisitLiteralExpression方法中保存了被访问的文本节点,因此,当Visitor完成遍历之后,即可通过Literals属性获得所有的字符串常量。

var syntaxTree = SyntaxTree.ParseText(source);
var root = syntaxTree.GetRoot();
var visitor = new ExtractStringLiteralVisitor();
visitor.Visit(root);
foreach (var literal in visitor.Literals)
    Console.WriteLine(literal);

程序输出如下:

image

当然还可以使用root.DescendantNodes方法来简化上面的过程。我在例子中使用Visitor的目的就是为了体现Roslyn的语法解析功能。

对VB.NET语言的应用

上面的输入代码是一段C#的程序,如果是VB.NET的源代码,其实处理过程是一样的,无非就是将引用的命名空间从Roslyn.Compilers.CSharp改为Roslyn.Compilers.VisualBasic。注意:Roslyn.Compilers.CSharp和Roslyn.Compilers.VisualBasic下都有SyntaxTree等类型的定义,但这些类型都是独立的,并非从某个基类继承或实现了某些接口,在实际应用中还得注意这点。

应用场景的思考

Roslyn的应用场景应该还是很多的,比如大家熟悉的FxCop,能够根据一些规则来检测托管程序集是否满足这些规则,以保证质量。但FxCop很局限,它需要使用反射,并根据程序集的调试信息PDB文件进行规则判断,而对于源代码本身的规范校验就不太适用了。仔细思考,Roslyn却能够在保证源代码编写规范方面,起到一定的作用。比如:

  • 对定义的变量名、函数名等进行拼写检查
  • 检查注释语句中的拼写错误
  • 检查变量、函数等的命名规范
  • XML文档的自动化翻译(可以借助Bing Translate、Google Translate的API实现自动化翻译),等等

大家也可以在实际中总结一些能够使用Roslyn的场景,我想只要合理利用,一定能在实际工作中帮助我们提高效率,做到事半功倍。

转载至:http://www.cnblogs.com/daxnet/p/3378193.html

发表在 C# | 留下评论

Entity Framework Model First下改变数据库脚本的生成方式

在Entity Framework Model First下, 一个非常常见的需求是改变数据库脚本的生成方式。这个应用场景是指,当用户在Designer上单击鼠标右键,然后选择Generate Database from Model选项,此时Entity Framework Model First会根据模型产生数据库SQL脚本,并将SQL脚本文件添加到解决方案资源管理器中。

事实上,这个自动化产生的数据库SQL脚本还是会有一些局限性。比如:Model上支持DateTime这一CLR类型,在自动化SQL生成的过程中,Entity Framework会自动使用SQL中的数据类型datetime来产生相应的字段定义,如果我们希望对于某些DateTime类型的属性产生date类型,而不是datetime类型的字段,那么我们就需要对数据库脚本的生成方式做一些修改。

例子

首先看一个例子,我们建立一个非常简单的模型:Employee,在这个Employee实体中会有一个DayOfBirth的属性,用以保存雇员的生日日期。该模型定义如下:

SNAGHTML7bcee4

在模型设计器上单击鼠标右键,选择“Generate Database from Model”菜单后,产生的SQL语句如下,可以看到,对于DayOfBirth属性,产生的字段是datetime类型:

image

现在,让我们来尝试改变Entity Framework Model First下数据库SQL脚本的生成方式,以使得所产生的DayOfBirth字段为date类型。

实现

通过使用Entity Framework的Structural Annotation的特性,我们可以很方便地定制SQL脚本的生成方式。

首先,在Solution Explorer中,找到模型文件(扩展名为edmx的文件),单击鼠标右键,选择Open With选项。在Open With对话框中,选择Automatic Editor Selector:

SNAGHTML522dd4

此时会关闭模型设计器,并以XML编辑器的方式打开edmx文件。在打开的编辑器中,我们可以看到edmx文件的详细内容。如果模型比较大的话,这个文件的内容也会比较多(有的甚至几千几万行)。总体来看,主要有三个部分:

image

  • SSDL content:对存储模型的定义
  • CSDL content:对概念模型的定义 – 也就是保存设计器上所设计的模型
  • C-S mapping content:定义了概念模型与存储模型之间的映射

一看就知道,Entity Framework就是一个ORM框架(废话)。

接下来,我们要对edmx的概念模型部分做一些修改。修改的目的就是为了给SQL脚本的自动化生成提供一些客户化的信息,以便自动化生成工具能够根据这些客户化信息产生不同的结果。

我们需要在ConceptualModels节点下的Schema上定义自己的命名空间。例如:

image

然后,我们自己自定义一个XML标签,并把这个标签应用到概念模型中的DayOfBirth属性上,如下:

image

注意此处的“edmx:CopyToSSDL”属性,意思是这部分属性需要在产生模型的时候复制到SSDL存储模型中。因为在生成SQL脚本时,转换引擎会读取SSDL中的内容并根据这些内容产生SQL。现在,我们双击edmx文件,并重新在设计器中打开模型。同样在设计器中点击鼠标右键,选择“Generate Database from Model”选项,待SQL脚本重新生成之后,再用XML编辑器打开edmx文件,此时我们会看到,在SSDL部分,先前添加的“custom:SqlType”节点也被复制到了这里,只不过稍许有些变化:

image

现在,我们需要定制SQL脚本的产生过程。打开模型设计器,在模型设计器的属性编辑窗口中,我们可以看到一个名为“DDL Generation Template”的属性:

SNAGHTML1b224f

它就是主导SQL脚本生成的T4模板文件,现在需要对这个T4文件进行定制。该文件位于%PROGRAMFILES(x86)%\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen目录下。为了不更改原有的SSDLToSQL10.tt文件,我们将其复制到Solution Explorer中,注意将该文件的BuildAction设置为None,并去掉Custom Tool的设置:

image

仍然打开模型设计器,在属性窗口中,设置“DDL Generation Template”属性为“.\SSDLToSQL10.tt”,注意路径符“.\”,它表示需要使用Solution Explorer下的SSDLToSQL10.tt文件,而不是标准的那个文件。

最关键的一步,就是修改SSDLToSQL10.tt文件。打开这个文件,找到“Creating all tables”部分,并用以下粉红色高亮部分替换其中的内容:

image

注意:我们还需要在这个tt文件的顶部引入System.XML和System.XML.Linq的命名空间:

<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>

至此,实现部分已经完成。

测试

现在来测试一下效果。双击打开edmx模型,在模型设计器上单击鼠标右键,选择“Generate Database from Model”,然后查看生成的SQL语句。我们发现,DayOfBirth已经变成了date类型了:

image

如果在产生数据库脚本的时候提示以下错误,请稍许更改一下模型(比如拖动一下模型中的实体等)保存之后再试。

image

转载至:http://www.cnblogs.com/daxnet/p/3541802.html

发表在 C# | 留下评论

一种通用查询语言的定义与实践

最近发现在项目中或许会遇到让用户自己构建查询表达式的情况。比如需要通过一种可配置的界面,来让用户输入一组具有逻辑关系的查询表达式,然后根据这个查询表达式来过滤并返回所需要的数据。这种用户案例其实非常常见。由此受到启发,或许我们可以自己定义一种通用的面向查询的领域特定语言(DSL),来实现查询的序列化和动态构建。

概述

由此我发布了一个称为Unified Queries(以下简称UQ)的开源项目,UQ定义了一种DSL,用以描述一种查询的特定结构。它同时还提供了将查询规约(Query Specification)转换为SQL WHERE子句以及Lambda表达式的功能。UQ提供了非常灵活的框架设计,能够非常方便地通过实现IQuerySpecificationCompiler接口,或者继承QuerySpecificationCompiler<T>抽象类来自定义查询规约的转换功能。

DSL结构定义

下面的XSD架构(XSD Schema)定义了UQ的DSL语义,需要注意的是,它包含了一组递归的层次结构:

例子

假定在QuerySpecificationSample.xml文件中定义了如下的查询规约,在执行该查询规约时,系统将返回所有名字以“Peter”开头,并且姓氏中不含有“r”字符,以及年收入在30000以上的客户。

<?xml version="1.0" encoding="utf-8"?>
<QuerySpecification>
  <LogicalOperation Operator="And">
    <Expression Name="FirstName" Type="String" Operator="StartsWith" Value="Peter"/>
    <UnaryLogicalOperation Operator="Not">
      <LogicalOperation Operator="Or">
        <Expression Name="LastName" Type="String" Operator="Contains" Value="r"/>
        <Expression Name="YearlyIncome" Type="Decimal" Operator="LessThanOrEqualTo" Value="30000"/>
      </LogicalOperation>
    </UnaryLogicalOperation>
  </LogicalOperation>
</QuerySpecification>

以下C#代码将根据该xml文件产生SQL的WHERE子句:

static void Main(string[] args)
{
    var querySpecification = QuerySpecification.LoadFromFile("QuerySpecificationSample.xml");
    var compiler = new SqlWhereClauseCompiler();
    Console.WriteLine(compiler.Compile(querySpecification));
}

所产生的SQL WHERE子句如下:

((FirstName LIKE 'Peter%') AND (NOT ((LastName LIKE '%r%') OR (YearlyIncome <= 30000))))

然而在很多情况下,ADO.NET的开发人员更喜欢通过使用DbParameter来指定查询中所包含的参数值,而不是简单地将参数拼接在SQL语句中。UQ通样能够产生带有参数列表的SQL WHERE子句。要达到这样的效果,仅需在初始化SqlWhereClauseCompiler时,将构造函数参数设置为true即可:

var compiler = new SqlWhereClauseCompiler(true);

于是产生的SQL WHERE子句就是:

((FirstName LIKE @fvP8gN) AND (NOT ((LastName LIKE @ESzoyd) OR (YearlyIncome <= @fG5Z7e))))

参数值则可以通过SqlWhereClauseCompiler的ParameterValues属性获得。

事实上SqlWhereClauseCompiler所产生的SQL WHERE子句是满足Microsoft SQL Server需要的,如果您希望能够产生符合Oracle或MySQL语法的WHERE子句,可以自己扩展SqlWhereClauseCompiler类来实现。

接下来,下面的C#代码可以将上面的xml文件中所定义的查询规约编译成Lambda表达式:

static void Main(string[] args)
{
    var querySpecification = QuerySpecification.LoadFromFile("QuerySpecificationSample.xml");
    var compiler = new LambdaExpressionCompiler<Customer>();
    Console.WriteLine(compiler.Compile(querySpecification));
}

产生的Lambda表达式如下:

p => (p.FirstName.StartsWith("Peter") AndAlso Not((p.LastName.Contains("r") OrElse (p.YearlyIncome <= 30000))))

下面的C#例子详细描述了如何在一组客户对象上应用查询规约,并将满足条件的客户数据返回:

private static Customer[] GetAllCustomers()
{
    return new[]
               {
                   new Customer { FirstName = "Sunny", LastName = "Chen", YearlyIncome = 10000 },
                   new Customer { FirstName = "PeterJam", LastName = "Yo", YearlyIncome = 10000 },
                   new Customer { FirstName = "PeterR", LastName = "Ko", YearlyIncome = 50000 },
                   new Customer { FirstName = "FPeter", LastName = "Law", YearlyIncome = 70000 },
                   new Customer { FirstName = "Jim", LastName = "Peter", YearlyIncome = 30000 }
               };
}

static void Main(string[] args)
{
    var querySpecification = QuerySpecification.LoadFromFile("QuerySpecificationSample.xml");
    var compiler = new LambdaExpressionCompiler<Customer>();
    var customers = GetAllCustomers();
    foreach (var customer in customers.Where(compiler.Compile(querySpecification).Compile()))
    {
        Console.WriteLine(
            "FirstName: {0}, LastName: {1}, YearlyIncome: {2}",
            customer.FirstName,
            customer.LastName,
            customer.YearlyIncome);
    }
}

总结

现在我们已经有了一种查询结构的DSL定义,这就使得一个查询规约可以保存在内存的对象中,也可以被持久化到外部的存储系统,比如xml文件中,或者数据库中。接下来我们可以设计一种通用的界面,通过这个界面来设计一个查询规约,于是,就可以通过Compiler将所设计的查询规约转换为另一种可被已有系统接受的形式。更进一步,我们还可以设计一系列的Builder,将SQL WHERE子句或者Lambda表达式转换为UQ中的查询规约。

希望这个小项目能够给大家带来启发和帮助。

转载至:http://www.cnblogs.com/daxnet/p/3925426.html

发表在 C# | 留下评论

Java的Integer类型使用==与equals

或许有不少Java程序猿在写Integer比较的时候出现过一种情况,均为Integer类型的相同值比较,却不是我们想要的结果!刚好在今天在开发中赶进度,也犯了这么一个错,所以来整理分享一下!

 

刚好今天有这么一个比较:

device.getIdevicetypeid != dbDevice.getIdevicetypeid()

在测试的时候就一直有问题,逼得我来个debug,一只跟踪到这里发现原本类型一致、值也一致的两个属性值比较时,却永远是true,很是诧异,仔细看看才发现"!="这个比较!

 

在这个时候,作为一个有了几年开发经验的来说,马上会想到几种解决方案!先例举两种:

1、不改比较方式,还是使用"!=",但是将

device.getIdevicetypeid().intValue != dbDevice.getIdevicetypeid().intValue()

这样就会得到你想要的答案

2、改变比较方式,使用"equals()",代码如下:

!device.getIdevicetypeid().equals(dbDevice.getIdevicetypeid())

这种比较方式一样能比较出来!以上两种比较方式要注意java.lang.NullPointerException异常,至于怎么处理这个异常,我就不多说了!

 

说说为什么吧,Integer不属于基本数据类型,使用==只能比较值相等,而Integer为对象所以使用==比较时就会出现问题,只要使用equals比较就不会有这种问题出现。再说说为什么用上面的两种方式就能比较出来呢,有使用jdk API习惯的程序猿,会发现Integer对象的intValue()会把Integer对象的值转换为int的基本数据类型值,这样使用==比较就是正常的!而用equals比较在底层的逻辑也就是将它用intValue()转换了!

 

内容很简单,知识很基础!

转载至:http://blog.csdn.net/luo201227/article/details/22392943

发表在 Java | 留下评论

Java enum 枚举还可以这么用

在大部分编程语言中,枚举类型都会是一种常用而又必不可少的数据类型,Java中当然也不会例外。然而,Java中的Enum枚举类型却有着许多你意想不到的用法,下面让我们一起来看看。

1、可以在enum中添加变量和方法

先来看一段代码示例:

public enum State {
        Normal("正常态", 1), Update("已更新", 2), Deleted("已删除", 3), Fired("已屏蔽", 4);
        // 成员变量
        private String name;
        private int index;

        // 构造方法,注意:构造方法不能为public,因为enum并不可以被实例化
        private State(String name, int index) {
            this.name = name;
            this.index = index;
        }

        // 普通方法
        public static String getName(int index) {
            for (State c : State .values()) {
                if (c.getIndex() == index) {
                    return c.name;
                }
            }
            return null;
        }

        // get set 方法
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }
    }

从上面的代码中我们可以看到,定义完枚举值,然后在其后面加上分号,接着就可以定义其他的变量、方法了。另外需要特别说明的是,enum中的构造方法不可以用public标识,这样做是为了防止用户实例化enum。

2、可以用来定义常量

先来回顾一下Java中如何定义常量吧,看下面一段代码:

public static final int normalState = 1;
private static final int updateState = 2;

下面我们还可以用enum枚举来代替上面的常量定义,代码如下:

public enum State {  
  Normal, Update, Deleted, Fired
}

在Java中用enum来定义常量在语法上没有什么优势,但是enum枚举类型可以提供更多的操作功能。

3、在enum中实现接口

先来看下面一段代码:

public interface ICanReadState {
        void read();

        String getState();
}

    public enum State implements ICanReadState {
        Normal("正常态", 1), Update("已更新", 2), Deleted("已删除", 3), Fired("已屏蔽", 4);

        private String name;
        private int index;

        private State(String name, int index) {
            this.name = name;
            this.index = index;
        }

        // 接口方法1

        @Override
        public String getState() {
            return this.name;
        }

        // 接口方法2
        @Override
        public void read() {
            System.out.println(this.index + ":" + this.name);
        }
    }

和一般的类中使用接口一样,enum枚举中同样可以继承接口,并实现接口中的所有方法,这样做的好处在于可以更方便地对枚举中的值进行排序、比较等操作,封装性更好。

总结

说白了,enum枚举类型是一个不可以被继承的final类,就以上面的State枚举为例,如果你查看enum类型的字节码,其实是State类型的类静态常量。

转载至:http://blog.csdn.net/luo201227/article/details/39177295

发表在 Java | 留下评论

Java JNA ―― JNI的加强版

在开发过程中,Java程序猿有时候很郁闷,要取计算机的相关信息相当无措!其实了解JNI的猿人应该知道,通过JNI调用C/C++的动态链接库就可以轻松的实现!但是有人又会觉得JNI还是有点复杂,好吧,今天就分享一下JNI的加强版――JNA,它让我们轻松了跨语言的调用,调用其他语言的方法/函数就如调用Java自己定义方法一般!好啦,不多说,直接贴代码,简单的实例,获取计算机的名称:

/**
 * @Description: 
 *
 * @Title: JNATestServlet.java
 * @Package com.joyce.jna
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-2-27 下午04:26:21
 * @version V2.0
 */
package com.joyce.jna;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.jna.Library;
import com.sun.jna.Native;
/**
 * @Description: 
 *
 * @ClassName: JNATestServlet
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-2-27 下午04:26:21
 * @version V2.0
 */
public class JNATestServlet extends HttpServlet {
    
    /**
      * @Fields serialVersionUID : TODO
      */
    private static final long serialVersionUID = -8617706236668864317L;
    public interface GetComputerName extends Library {
        GetComputerName INSTANCE = (GetComputerName) Native.loadLibrary("dll/GetCompName",GetComputerName.class);
        public String GetCopmuterName();
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        String computerName = GetComputerName.INSTANCE.GetCopmuterName();
        System.out.println(computerName);
        PrintWriter out = response.getWriter();
        out.print(computerName);
        out.flush();
        out.close();
    }
}

这是后台是的代码,至于怎么展示在前台上就不用我多说了吧!

 

动态链接库dll、jar包下载地址:http://download.csdn.net/download/luo201227/7133593

转载至:http://blog.csdn.net/luo201227/article/details/22779341

发表在 Java | 留下评论

Java生成二维码

为了庆祝明天和这不成器的好产品,写上这Java生成的二维码!这里生成的二维码所使用的jar是为qrcode!具体资源的下载路径:http://download.csdn.net/download/luo201227/7130951(包含了代码和jar包),好啦。直接上代码:

一、[编码]

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import com.swetake.util.Qrcode;
/**
 * @Description: 编码器
 *
 * @ClassName: QRCodeEncoderHandler
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-2-11 下午01:57:47
 * @version V2.0
 */
public class QRCodeEncoderHandler {
    
    /**
     * @Description: 生成二维码(QRCode)图片 
     *
     * @param content 二维码内容
     * @param imgPath 二维码存放路径
     *
     * @Title: QRCodeEncoderHandler.java
     * @Copyright: Copyright (c) 2014
     *
     * @author Comsys-LZP
     * @date 2014-2-11 下午03:54:18
     * @version V1.0
     * @throws Exception 
     */
    public void encoderQRCode(String content, String imgPath) throws Exception {
        // 处理异常
        try { 
            // 实例化对象
            Qrcode qrcodeHandler = new Qrcode(); 
            // 设置
            qrcodeHandler.setQrcodeErrorCorrect('M'); 
            qrcodeHandler.setQrcodeEncodeMode('B'); 
            qrcodeHandler.setQrcodeVersion(7); 
            
            // 二维码内容转换
            byte[] contentBytes = content.getBytes("gb2312"); 
            
            // 实例化对象
            BufferedImage bufImg = new BufferedImage(140, 140, BufferedImage.TYPE_INT_RGB); 
            
            // 创建
            Graphics2D gs = bufImg.createGraphics(); 
            // 设置
            gs.setBackground(Color.WHITE);
            
            gs.clearRect(0, 0, 140, 140);
 
            // 设定图像颜色> BLACK 
            gs.setColor(Color.BLACK); 
 
            // 设置偏移量 不设置可能导致解析出错 
            int pixoff = 2; 
            // 输出内容> 二维码 
            if (contentBytes.length > 0 && contentBytes.length < 120) { 
                boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes); 
                for (int i = 0; i < codeOut.length; i++) { 
                    for (int j = 0; j < codeOut.length; j++) { 
                        if (codeOut[j][i]) { 
                            gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3); 
                        } 
                    } 
                } 
            } else {
                throw new Exception("QRCode content bytes length = " + contentBytes.length + " not in [ 0,120 ]. ");
            } 
            gs.dispose(); 
            bufImg.flush(); 
            
            // 二维码文件对象
            File imgFile = new File(imgPath); 
            
            // 判断是否存在
            if(!imgFile.exists()){
                // 不存在,先进行创建
                imgFile.mkdirs();
            }
            // 生成二维码QRCode图片 
            ImageIO.write(bufImg, "png", imgFile); 
        } catch (Exception e) {
            throw new Exception("Error:" + e.getMessage());
        } 
    }
}

二、[解码]

import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import jp.sourceforge.qrcode.QRCodeDecoder;
import jp.sourceforge.qrcode.data.QRCodeImage;
/**
 * @Description: 解码器
 *
 * @ClassName: QRCodeDecoderHandler
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-2-11 下午02:51:55
 * @version V1.0
 */
public class QRCodeDecoderHandler {
    /**
     * @Description: 解码二维码 
     *
     * @param imgPath 二维码图片文件路径
     * @return 解码内容
     *
     * @Title: QRCodeDecoderHandler.java
     * @Copyright: Copyright (c) 2014
     *
     * @author Comsys-LZP
     * @date 2014-2-11 下午03:44:04
     * @version V1.0
     * @throws Exception 
     */
    public String decoderQRCode(String imgPath) throws Exception { 
        // QRCode 二维码图片的文件 
        File imageFile = new File(imgPath); 
        // 声明对象
        BufferedImage bufImg = null; 
        // 声明变量
        String decodedData = null;
        // 处理异常
        try { 
            // 获取对象
            bufImg = ImageIO.read(imageFile); 
            // 实例化对象
            QRCodeDecoder decoder = new QRCodeDecoder();
            // 解码
            decodedData = new String(decoder.decode(new J2SEImage(bufImg))); 
        } catch (Exception e) {
            // 抛出异常
            throw new Exception("Error: " + e.getMessage());
        }
        // 返回
        return decodedData; 
    } 
    
    class J2SEImage implements QRCodeImage { 
        BufferedImage bufImg; 
 
        public J2SEImage(BufferedImage bufImg) { 
            this.bufImg = bufImg; 
        } 
 
        public int getWidth() { 
            return bufImg.getWidth(); 
        } 
 
        public int getHeight() { 
            return bufImg.getHeight(); 
        } 
 
        public int getPixel(int x, int y) { 
            return bufImg.getRGB(x, y); 
        } 
    } 
}

关键代码已贴上!至于怎么调用就不用我说了吧!

想看看效果吗?不好意思,不贴图,如果觉得神奇就拿起扫描的扫起来吧!

转载至:http://blog.csdn.net/luo201227/article/details/22939833

发表在 Java | 留下评论

使用Gson对象将json格式的字符串与集合和对象互转

程序猿在一些多语言或者多平台的时候,数据传递绝大部分时候都是采用XML和JSON,以便双方都能识别,目前Java在XML方面有些常用的:dom4j这就不用说了,然后还有就是使用的比较少的,但是很强大很方便的XStream和XBlink!这里就不多介绍这些了;而对于JSON呢,有Java本身解析的包和方法,不过自从使用Google的Gson后,似乎就不再用以前的那种方式了!好啦,来简单的说说Gson对Json的使用吧,其实也真的很简单,所以现在使用的人也越来越多了!

Gson gs = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
List<T> list= gs.fromJson(jsonStr,new TypeToken<List<T>>() {}.getType());//转换为集合
Object obj = gs.fromJson(jsonStr, Object.class);//转换为对象
gs.toJson(list);//集合转换为json字符串
gs.toJson(obj);//对象转换为json字符串

 

关键代码就在这里,好好领悟去吧!多动动脑筋哟

Gson的jar包下载地址:http://download.csdn.net/download/luo201227/7176985

Google官网下载地址:https://code.google.com/p/google-gson/downloads/list

转载至:http://blog.csdn.net/luo201227/article/details/23436951

发表在 Java | 留下评论

DozerBeanMapper对象之间相同属性名赋值

在业务逻辑很复杂的时候,一般都是牵涉到很广,这最直白的表现就是牵连诸多表,然后数据却不是一个对象中的属性或字段,这个时候我们为了方便数据传输一般都会将多个对象中的属性封装到一个VO中,使用Hibernate的人应该知道,查询出来一般都是一个或多个对象,如果依次通过setter方法来赋值的话,效率太低!这时候我们的DozerBeanMapper就体现出来了价值,所以我便将其封装了起来!好啦,直接上代码:

/**
 * @Title: EntityObjectConverter.java
 * @Package com.joyce.util
 * @Copyright: Copyright (c) 2013
 *
 * @author Comsys-LZP
 * @date 2013-11-4 上午09:55:14
 * @version V2.0
 */
package com.joyce.util;

import java.util.ArrayList;
import java.util.List;

import net.sf.dozer.util.mapping.DozerBeanMapper;

/**
 * @Description: 两个对象间,相同属性名之间进行转换
 * 
 * @ClassName: EntityObjectConverter
 * @Copyright: Copyright (c) 2013
 *
 * @author Comsys-LZP
 * @date 2013-11-4 上午09:55:14
 * @version V2.0
 */
public class EntityObjectConverter {
	/*
	 * 实例化对象
	 */
	private static DozerBeanMapper map = new DozerBeanMapper();
	
	/**
	 * @Description: 将目标对象转换为指定对象,相同属性名进行属性值复制
	 * 
	 * @Title: EntityObjectConverter.java
	 * @Copyright: Copyright (c) 2013
	 *
	 * @author Comsys-LZP
	 * @date 2013-11-4 下午02:32:34
	 * @version V2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getObject(Object source,Class<T> cls){
		if (source==null) {
			return null;
		}
		return (T) map.map(source, cls);
	}
	
	/**
	 * @Description: 两个对象之间相同属性名的属性值复制
	 * 
	 * @Title: EntityObjectConverter.java
	 * @Copyright: Copyright (c) 2013
	 *
	 * @author Comsys-LZP
	 * @date 2013-11-4 下午02:33:56
	 * @version V2.0
	 */
	public static void setObject(Object source,Object target){
		map.map(source, target);
	}
	
	/**
	 * @Description: 对象集合中对象相同属性名的属性值复制
	 * 
	 * @Title: EntityObjectConverter.java
	 * 
	 * @Copyright: Copyright (c) 2013
	 * @author Comsys-LZP
	 * @date 2013-11-4 下午02:34:26
	 * @version V2.0
	 */
	@SuppressWarnings("unchecked")
	public static List getList(List source,Class cls){
		List listTarget = new ArrayList();
		if(source != null){
			for (Object object : source) {
				Object objTarget = EntityObjectConverter.getObject(object, cls);
				listTarget.add(objTarget);
			}
		}
		return listTarget;
	}
}

附上jar资源下载地址:http://download.csdn.net/download/luo201227/7213937

欢迎大家关注我的博客!

转载至:http://blog.csdn.net/luo201227/article/details/24021791

发表在 Java | 留下评论

Java的命名规范

所谓无规矩不成方圆,在编程世界中,也自有其一套编写规范。接下来,我们一起了解下Java的命名规范

 
    变量名
 
    1.普通变量命名应该采用首字母小写,其他字母首字母大写的方式。
    2.final static变量的名字应该都大写,并且指出完整含义。如果一个常量名称由多个单词组成,则应该用下划线来分割这些单词如:NUM_DAYS_IN_WEEK  MAX_VALU
    3. 如果需要对变量名进行缩写时,一定要注意整个代码中缩写规则的一致性。如:context=ctx  message=msg
    4. 通过在结尾处放置一个量词,就可创建更加统一的变量
    First(一组变量中的第一个)  Last(一组变量中的最后一个)  Next(一组变量中的下一个变量)  Prev(一组变量中的上一个)  Cur(一组变量中的当前变量)
    5. 无论什么时候,均提倡应用常量取代数字、固定字符串。也就是说,程序中除0,1以外,尽量不应该出现其他数字。
    6. 索引变量:i、j、k等只作为小型循环的循环索引变量。
    7. 逻辑变量:避免用flag来命名状态变量,用is来命名逻辑变量。
    if(isClosed){  dosomeworks;  return;  }
 
    包的命名
 
    包名按照域名的范围从大到小逐步列出,恰好和Internet上的域名命名规则相反。由一组以“。”连接的标识符构成,通常第一个标识符为符合网络域名的两个或者三个英文小写字母。
 
    类,接口命名
 
    类的名字必须由大写字母开头而单词中的其他字母均为小写;如果类名称由多个单词组成,则每个单词的首字母均应为大写例如TestPage;如果类名称中包含单词缩写,则这个所写词的每个字母均应大写,如:XMLExample,还有一点命名技巧就是由于类是设计用来代表对象的,所以在命名类时应尽量选择名词。
 
    方法
 
    有调用关系的方法尽量放在相邻的位置,public和private方法可以交叉放置。
 
    方法名
 
    方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头。可以为动词或动词+名词组合。
    设置/获取某个值的Method,应该遵循setV/getV规范
    返回长度的Method,应该命名为length
    测试某个布尔值的Method,应该命名为isV
    将对象转换为某个特定类型的Mehod应该命名为toF
 
    数组
 
    总是使用以下方式定义数组:
    int[] arr = new int[10];
    禁止使用C语言的是形式:
    禁止  int arr[] = new int[10];
 
    集合
 
    数组或者容器推荐命名方式为名词+s的方式,例如:
    List<Person> persons = getPerson();  for(Person person : persons){  dosomeworks;  }
 
    泛型
 
    应该尽量简明扼要(最好是一个字母),以利于与普通的class或interface区分
    Container中的Element应该用E表示;Map里的key用K表示,value用V;Type用T表示;异常用X表示
 
    花括号
 
    花括号统一采用以下格式:
    if(bool experssion){  dosomework;  }
    除非花括号中为空,不然任何情况下不能省略花括号,并且花括号必须换行。
 
    括号
 
    括号的前,后一个字符不需要空格
 
    空格
 
    逗号之后紧跟一个空格。
 
    构造函数
 
    1) 参数为空的构造函数出现在最上方
    2) 有调用关系的构造函数相邻
    3) 参数尽量由少到多从上至下排序
 
    使用成员变量
 
    在类的方法内引用成员变量了命名冲突以外,不使用this。非特殊情况在类的方法内都不使用get和set方法存取成员变量。

转载至:http://blog.csdn.net/luo201227/article/details/24300615

发表在 Java | 留下评论

Java产生随机数

随机数在程序的开发中经常使用,最实在的现在好多的网站开始使用随机密码了,好啦,直接上代码,很简单的!

/**
 * @Description: 
 *
 * @Title: RandomUtil.java
 * @Package com.joyce.util
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-4-29 下午03:14:06
 * @version V2.0
 */
package com.joyce.util;

import java.util.Random;

/**
 * @Description: 生成随机数
 * 
 * @ClassName: RandomUtil
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-4-29 下午03:14:06
 * @version V2.0
 */
public class RandomUtil {
	/**
	 * 随机数的最小值
	 */
	private static final Integer rand_min = 0;
	/**
	 * 随机数的最大值
	 */
	private static final Integer rand_max = 100;
	/**
	 * 实例化对象
	 */
	private static final Random RANDOM = new Random();

	/**
	 * @Description: 生成指定范围的随机数[minValue,maxValue]
	 * 
	 * @param minValue
	 *            最小值
	 * @param maxValue
	 *            最大值
	 * @return 指定范围内的随机数
	 * 
	 * @Title: RandomUtil.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-4-29 下午03:23:09
	 * @version V2.0
	 * @throws Exception 
	 */
	public static Integer appointRangeValue(Integer minValue, Integer maxValue) throws Exception {
		if(maxValue <= 0){
			throw new RuntimeException("maxValue must more than zero");
		}
		return RANDOM.nextInt(maxValue) % (maxValue - minValue + 1) + minValue;
	}

	/**
	 * @Description: 生成指定结束值的随机数[startValue,100]
	 * 
	 * @param startValue
	 *            开始值
	 * @return 开始值~100之间的值
	 * 
	 * @Title: RandomUtil.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-4-29 下午03:37:21
	 * @version V2.0
	 * @throws Exception 
	 */
	public static Integer appointStartValue(Integer startValue) throws Exception {
		return appointRangeValue(startValue, rand_max);
	}

	/**
	 * @Description: 生成指定结束值的随机数[0,endValue]
	 * 
	 * @param endValue
	 *            结束值
	 * @return 0~指定结束值之间的值
	 * 
	 * @Title: RandomUtil.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-4-29 下午03:34:35
	 * @version V2.0
	 * @throws Exception 
	 */
	public static Integer appointEndValue(Integer endValue) throws Exception {
		return appointRangeValue(rand_min, endValue);
	}
}

以上就是简单封装了一些产生随机数的方法,至于怎么调用,就不用多说了!

转载至:http://blog.csdn.net/luo201227/article/details/25055167

发表在 Java | 留下评论

CPU使用率、内存空间等系统信息

在要获取计算机的一些基本信息时,让Java开发人员感觉无所适从,总觉得不好做,因为大家的第一反应可能是动态链接库,使用JNI或JNA来实现,但是求人不如求己,所以就简单的写了一个例子,实现的功能也很简单,只是玩玩。。。。

好啦,先写好bean或者说实体类吧

/**
 * @Description: 
 *
 * @Title: MonitorInfoBean.java
 * @Package com.joyce.bean
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-4-24 上午11:07:59
 * @version V2.0
 */
package com.joyce.bean;

/**
 * @Description:
 * 
 * @ClassName: MonitorInfoBean
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-4-24 上午11:07:59
 * @version V2.0
 */
public class MonitorInfoBean {
	/** 可使用内存. */
	private long totalMemory;

	/** 剩余内存. */
	private long freeMemory;

	/** 最大可使用内存. */
	private long maxMemory;

	/** 操作系统. */
	private String osName;

	/** 总的物理内存. */
	private long totalMemorySize;

	/** 剩余的物理内存. */
	private long freePhysicalMemorySize;

	/** 已使用的物理内存. */
	private long usedMemory;

	/** 线程总数. */
	private int totalThread;

	/** cpu使用率. */
	private double cpuRatio;

	/**
	 * @return the totalMemory
	 */
	public long getTotalMemory() {
		return totalMemory;
	}

	/**
	 * @param totalMemory
	 *            the totalMemory to set
	 */
	public void setTotalMemory(long totalMemory) {
		this.totalMemory = totalMemory;
	}

	/**
	 * @return the freeMemory
	 */
	public long getFreeMemory() {
		return freeMemory;
	}

	/**
	 * @param freeMemory
	 *            the freeMemory to set
	 */
	public void setFreeMemory(long freeMemory) {
		this.freeMemory = freeMemory;
	}

	/**
	 * @return the maxMemory
	 */
	public long getMaxMemory() {
		return maxMemory;
	}

	/**
	 * @param maxMemory
	 *            the maxMemory to set
	 */
	public void setMaxMemory(long maxMemory) {
		this.maxMemory = maxMemory;
	}

	/**
	 * @return the osName
	 */
	public String getOsName() {
		return osName;
	}

	/**
	 * @param osName
	 *            the osName to set
	 */
	public void setOsName(String osName) {
		this.osName = osName;
	}

	/**
	 * @return the totalMemorySize
	 */
	public long getTotalMemorySize() {
		return totalMemorySize;
	}

	/**
	 * @param totalMemorySize
	 *            the totalMemorySize to set
	 */
	public void setTotalMemorySize(long totalMemorySize) {
		this.totalMemorySize = totalMemorySize;
	}

	/**
	 * @return the freePhysicalMemorySize
	 */
	public long getFreePhysicalMemorySize() {
		return freePhysicalMemorySize;
	}

	/**
	 * @param freePhysicalMemorySize
	 *            the freePhysicalMemorySize to set
	 */
	public void setFreePhysicalMemorySize(long freePhysicalMemorySize) {
		this.freePhysicalMemorySize = freePhysicalMemorySize;
	}

	/**
	 * @return the usedMemory
	 */
	public long getUsedMemory() {
		return usedMemory;
	}

	/**
	 * @param usedMemory
	 *            the usedMemory to set
	 */
	public void setUsedMemory(long usedMemory) {
		this.usedMemory = usedMemory;
	}

	/**
	 * @return the totalThread
	 */
	public int getTotalThread() {
		return totalThread;
	}

	/**
	 * @param totalThread
	 *            the totalThread to set
	 */
	public void setTotalThread(int totalThread) {
		this.totalThread = totalThread;
	}

	/**
	 * @return the cpuRatio
	 */
	public double getCpuRatio() {
		return cpuRatio;
	}

	/**
	 * @param cpuRatio
	 *            the cpuRatio to set
	 */
	public void setCpuRatio(double cpuRatio) {
		this.cpuRatio = cpuRatio;
	}

	/**
	 * 
	 */
	public MonitorInfoBean() {
		super();
	}

	/**
	 * @param totalMemory
	 * @param freeMemory
	 * @param maxMemory
	 * @param osName
	 * @param totalMemorySize
	 * @param freePhysicalMemorySize
	 * @param usedMemory
	 * @param totalThread
	 * @param cpuRatio
	 */
	public MonitorInfoBean(long totalMemory, long freeMemory, long maxMemory,
			String osName, long totalMemorySize, long freePhysicalMemorySize,
			long usedMemory, int totalThread, double cpuRatio) {
		super();
		this.totalMemory = totalMemory;
		this.freeMemory = freeMemory;
		this.maxMemory = maxMemory;
		this.osName = osName;
		this.totalMemorySize = totalMemorySize;
		this.freePhysicalMemorySize = freePhysicalMemorySize;
		this.usedMemory = usedMemory;
		this.totalThread = totalThread;
		this.cpuRatio = cpuRatio;
	}
}

其实封装实体对象,只是为了后面开发更加方便,习惯问题,这里接着来实现类:

/**
 * @Description: 
 *
 * @Title: MonitorServiceImpl.java
 * @Package com.joyce.service.impl
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-4-24 上午11:14:53
 * @version V2.0
 */
package com.joyce.service.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.StringTokenizer;

import sun.management.ManagementFactory;

import com.joyce.bean.MonitorInfoBean;
import com.joyce.service.IMonitorService;
import com.joyce.util.Bytes;
import com.sun.management.OperatingSystemMXBean;

/**
 * @Description:
 * 
 * @ClassName: MonitorServiceImpl
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-4-24 上午11:14:53
 * @version V2.0
 */
public class MonitorServiceImpl implements IMonitorService {
	private static final int CPUTIME = 30;
	private static final int PERCENT = 100;
	private static final int FAULTLENGTH = 10;
	private static String linuxVersion = null;

	@Override
	public MonitorInfoBean getMonitorInfoBean() throws Exception {
		int kb = 1024;

		// 可使用内存
		long totalMemory = Runtime.getRuntime().totalMemory() / kb;
		// 剩余内存
		long freeMemory = Runtime.getRuntime().freeMemory() / kb;
		// 最大可使用内存
		long maxMemory = Runtime.getRuntime().maxMemory() / kb;

		OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();

		// 操作系统
		String osName = System.getProperty("os.name");
		// 总的物理内存
		long totalMemorySize = osmxb.getTotalPhysicalMemorySize() / kb;
		// 剩余的物理内存
		long freePhysicalMemorySize = osmxb.getFreePhysicalMemorySize() / kb;
		// 已使用的物理内存
		long usedMemory = (osmxb.getTotalPhysicalMemorySize() - osmxb
				.getFreePhysicalMemorySize())
				/ kb;

		// 获得线程总数
		ThreadGroup parentThread;
		for (parentThread = Thread.currentThread().getThreadGroup(); parentThread
				.getParent() != null; parentThread = parentThread.getParent())
			;
		int totalThread = parentThread.activeCount();

		double cpuRatio = 0;
		if (osName.toLowerCase().startsWith("windows")) {
			cpuRatio = this.getCpuRatioForWindows();
		} else {
			cpuRatio = this.getCpuRateForLinux();
		}

		// 构造返回对象
		MonitorInfoBean infoBean = new MonitorInfoBean();
		infoBean.setFreeMemory(freeMemory);
		infoBean.setFreePhysicalMemorySize(freePhysicalMemorySize);
		infoBean.setMaxMemory(maxMemory);
		infoBean.setOsName(osName);
		infoBean.setTotalMemory(totalMemory);
		infoBean.setTotalMemorySize(totalMemorySize);
		infoBean.setTotalThread(totalThread);
		infoBean.setUsedMemory(usedMemory);
		infoBean.setCpuRatio(cpuRatio);
		return infoBean;
	}
	
	/**
	 * @Description:获取Linux下CPU的使用率 
	 *
	 * @return
	 *
	 * @Title: MonitorServiceImpl.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-5 下午04:01:41
	 * @version V2.0
	 */
	private double getCpuRateForLinux() {
		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader brStat = null;
		StringTokenizer tokenStat = null;
		try {
			System.out.println("Get usage rate of CUP , linux version: "
					+ linuxVersion);

			Process process = Runtime.getRuntime().exec("top -b -n 1");
			is = process.getInputStream();
			isr = new InputStreamReader(is);
			brStat = new BufferedReader(isr);

			if (linuxVersion.equals("2.4")) {
				brStat.readLine();
				brStat.readLine();
				brStat.readLine();
				brStat.readLine();

				tokenStat = new StringTokenizer(brStat.readLine());
				tokenStat.nextToken();
				tokenStat.nextToken();
				String user = tokenStat.nextToken();
				tokenStat.nextToken();
				String system = tokenStat.nextToken();
				tokenStat.nextToken();
				String nice = tokenStat.nextToken();

				System.out.println(user + " , " + system + " , " + nice);

				user = user.substring(0, user.indexOf("%"));
				system = system.substring(0, system.indexOf("%"));
				nice = nice.substring(0, nice.indexOf("%"));

				float userUsage = new Float(user).floatValue();
				float systemUsage = new Float(system).floatValue();
				float niceUsage = new Float(nice).floatValue();

				return (userUsage + systemUsage + niceUsage) / 100;
			} else {
				brStat.readLine();
				brStat.readLine();

				tokenStat = new StringTokenizer(brStat.readLine());
				tokenStat.nextToken();
				tokenStat.nextToken();
				tokenStat.nextToken();
				tokenStat.nextToken();
				tokenStat.nextToken();
				tokenStat.nextToken();
				tokenStat.nextToken();
				String cpuUsage = tokenStat.nextToken();

				System.out.println("CPU idle : " + cpuUsage);
				Float usage = new Float(cpuUsage.substring(0, cpuUsage
						.indexOf("%")));

				return (1 - usage.floatValue() / 100);
			}

		} catch (IOException ioe) {
			System.out.println(ioe.getMessage());
			freeResource(is, isr, brStat);
			return 1;
		} finally {
			freeResource(is, isr, brStat);
		}

	}

	private static void freeResource(InputStream is, InputStreamReader isr,
			BufferedReader br) {
		try {
			if (is != null)
				is.close();
			if (isr != null)
				isr.close();
			if (br != null)
				br.close();
		} catch (IOException ioe) {
			System.out.println(ioe.getMessage());
		}
	}

	/**
	 * @Description: 获取Windows下CPU的使用率 
	 *
	 * @return
	 *
	 * @Title: MonitorServiceImpl.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-5 下午04:02:12
	 * @version V2.0
	 */
	private double getCpuRatioForWindows() { 
        try { 
            String procCmd = System.getenv("windir") 
                    + "\\system32\\wbem\\wmic.exe process get Caption,CommandLine," 
                    + "KernelModeTime,ReadOperationCount,ThreadCount,UserModeTime,WriteOperationCount"; 
            // 取进程信息 
            long[] c0 = readCpu(Runtime.getRuntime().exec(procCmd)); 
            Thread.sleep(CPUTIME); 
            long[] c1 = readCpu(Runtime.getRuntime().exec(procCmd)); 
            if (c0 != null && c1 != null) { 
                long idletime = c1[0] - c0[0]; 
                long busytime = c1[1] - c0[1]; 
                return Double.valueOf( 
                        PERCENT * (busytime) / (busytime + idletime)) 
                        .doubleValue(); 
            } else { 
                return 0.0; 
            } 
        } catch (Exception ex) { 
            ex.printStackTrace(); 
            return 0.0; 
        } 
    }
	
	/**
	 * @Description: 读取CPU信息
	 *
	 * @param proc
	 * @return
	 *
	 * @Title: MonitorServiceImpl.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-5 下午04:02:37
	 * @version V2.0
	 */
	private long[] readCpu(final Process proc) { 
        long[] retn = new long[2]; 
        try { 
            proc.getOutputStream().close(); 
            InputStreamReader ir = new InputStreamReader(proc.getInputStream()); 
            LineNumberReader input = new LineNumberReader(ir); 
            String line = input.readLine(); 
            if (line == null || line.length() < FAULTLENGTH) { 
                return null; 
            } 
            int capidx = line.indexOf("Caption"); 
            int cmdidx = line.indexOf("CommandLine"); 
            int rocidx = line.indexOf("ReadOperationCount"); 
            int umtidx = line.indexOf("UserModeTime"); 
            int kmtidx = line.indexOf("KernelModeTime"); 
            int wocidx = line.indexOf("WriteOperationCount"); 
            long idletime = 0; 
            long kneltime = 0; 
            long usertime = 0; 
            while ((line = input.readLine()) != null) { 
                if (line.length() < wocidx) { 
                    continue; 
                } 
                // 字段出现顺序:Caption,CommandLine,KernelModeTime,ReadOperationCount, 
                // ThreadCount,UserModeTime,WriteOperation 
                String caption = Bytes.substring(line, capidx, cmdidx - 1) 
                        .trim(); 
                String cmd = Bytes.substring(line, cmdidx, kmtidx - 1).trim(); 
                if (cmd.indexOf("wmic.exe") >= 0) { 
                    continue; 
                } 
                // log.info("line="+line); 
                if (caption.equals("System Idle Process") 
                        || caption.equals("System")) { 
                    idletime += Long.valueOf( 
                            Bytes.substring(line, kmtidx, rocidx - 1).trim()) 
                            .longValue(); 
                    idletime += Long.valueOf( 
                            Bytes.substring(line, umtidx, wocidx - 1).trim()) 
                            .longValue(); 
                    continue; 
                } 

                kneltime += Long.valueOf( 
                        Bytes.substring(line, kmtidx, rocidx - 1).trim()) 
                        .longValue(); 
                usertime += Long.valueOf( 
                        Bytes.substring(line, umtidx, wocidx - 1).trim()) 
                        .longValue(); 
            } 
            retn[0] = idletime; 
            retn[1] = kneltime + usertime; 
            return retn; 
        } catch (Exception ex) { 
            ex.printStackTrace(); 
        } finally { 
            try { 
                proc.getInputStream().close(); 
            } catch (Exception e) { 
                e.printStackTrace(); 
            } 
        } 
        return null; 
    }
	
	/**
	 * @Description: 测试
	 *
	 * @param args
	 * @throws Exception
	 *
	 * @Title: MonitorServiceImpl.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-5 下午04:02:50
	 * @version V2.0
	 */
	public static void main(String[] args) throws Exception {
		IMonitorService service = new MonitorServiceImpl();
		MonitorInfoBean monitorInfo = service.getMonitorInfoBean();
		System.out.println("cpu占有率=" + monitorInfo.getCpuRatio());

		System.out.println("可使用内存=" + monitorInfo.getTotalMemory());
		System.out.println("剩余内存=" + monitorInfo.getFreeMemory());
		System.out.println("最大可使用内存=" + monitorInfo.getMaxMemory());

		System.out.println("操作系统=" + monitorInfo.getOsName());
		System.out.println("总的物理内存=" + monitorInfo.getTotalMemorySize() + "kb");
		System.out.println("剩余的物理内存=" + monitorInfo.getFreeMemory() + "kb");
		System.out.println("已使用的物理内存=" + monitorInfo.getUsedMemory() + "kb");
		System.out.println("线程总数=" + monitorInfo.getTotalThread() + "kb");
	}
}

大家应该看出来了,怎么还有一个接口类没有呢,真的不想贴的,但是为了那些刚上手的或者很B的人还是贴上吧

/**
 * @Description: 
 *
 * @Title: IMonitorService.java
 * @Package com.joyce.service
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-4-24 上午11:11:17
 * @version V2.0
 */
package com.joyce.service;

import com.joyce.bean.MonitorInfoBean;

/**
 * @Description: 
 *
 * @ClassName: IMonitorService
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-4-24 上午11:11:17
 * @version V2.0
 */
public interface IMonitorService {
	public MonitorInfoBean getMonitorInfoBean() throws Exception;
}

 

是不是很简单呀,好啦!至于怎么在界面上去展示,我就不在这里说了,有完整的案例,包括了以上看到的部分内容,还有ExtJs4的图表,这里就把测试运行结果就大伙看看啦

cpu占有率=16.0
可使用内存=5056
剩余内存=4794
最大可使用内存=65088
操作系统=Windows Vista
总的物理内存=4148916kb
剩余的物理内存=4794kb
已使用的物理内存=3478464kb
线程总数=5kb

 

其实Java也可以的吧,别怕,亲们,放手去搞!完整demo已上传,如需请下载:http://download.csdn.net/download/luo201227/7297885

转载至:http://blog.csdn.net/luo201227/article/details/25057311

发表在 Java | 留下评论

如何在跨平台的情况下获取可执行文件的详细信息和属性

很多情况下我们需要获取到系统中一些可执行文件的版本号,以便做一些附加动作!其实可以使用jna获取,但是这将依赖人家,所以还是Java自己来吧!好啦,直接上代码吧

/**
 * @Description: 
 *
 * @Title: FileInfoUtil.java
 * @Package com.joyce.util
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-12 下午03:46:32
 * @version V2.0
 */
package com.joyce.util;

import java.io.File;
import java.io.RandomAccessFile;

/**
 * @Description:文件操作工具类
 * 
 * @ClassName: FileInfoUtil
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-5-12 下午03:46:32
 * @version V2.0
 */
public class FileInfoUtil {
	
	/**
	 * @Description: 获取文件版本信息 
	 *
	 * @param file
	 * @return
	 *
	 * @Title: FileInfoUtil.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-12 下午04:36:17
	 * @version V2.0
	 */
	public static String getVersion(File file) {
		RandomAccessFile raf = null;
		byte[] buffer;
		String str;
		try {
			raf = new RandomAccessFile(file, "r");
			buffer = new byte[64];
			raf.read(buffer);
			str = "" + (char) buffer[0] + (char) buffer[1];
			if (!"MZ".equals(str)) {
				return null;
			}

			int peOffset = unpack(new byte[] { buffer[60], buffer[61],
					buffer[62], buffer[63] });
			if (peOffset < 64) {
				return null;
			}

			raf.seek(peOffset);
			buffer = new byte[24];
			raf.read(buffer);
			str = "" + (char) buffer[0] + (char) buffer[1];
			if (!"PE".equals(str)) {
				return null;
			}
			int machine = unpack(new byte[] { buffer[4], buffer[5] });
			if (machine != 332) {
				return null;
			}

			int noSections = unpack(new byte[] { buffer[6], buffer[7] });
			int optHdrSize = unpack(new byte[] { buffer[20], buffer[21] });
			raf.seek(raf.getFilePointer() + optHdrSize);
			boolean resFound = false;
			for (int i = 0; i < noSections; i++) {
				buffer = new byte[40];
				raf.read(buffer);
				str = "" + (char) buffer[0] + (char) buffer[1]
						+ (char) buffer[2] + (char) buffer[3]
						+ (char) buffer[4];
				if (".rsrc".equals(str)) {
					resFound = true;
					break;
				}
			}
			if (!resFound) {
				return null;
			}

			int infoVirt = unpack(new byte[] { buffer[12], buffer[13],
					buffer[14], buffer[15] });
			int infoSize = unpack(new byte[] { buffer[16], buffer[17],
					buffer[18], buffer[19] });
			int infoOff = unpack(new byte[] { buffer[20], buffer[21],
					buffer[22], buffer[23] });
			raf.seek(infoOff);
			buffer = new byte[infoSize];
			raf.read(buffer);
			int nameEntries = unpack(new byte[] { buffer[12], buffer[13] });
			int idEntries = unpack(new byte[] { buffer[14], buffer[15] });
			boolean infoFound = false;
			int subOff = 0;
			for (int i = 0; i < (nameEntries + idEntries); i++) {
				int type = unpack(new byte[] { buffer[i * 8 + 16],
						buffer[i * 8 + 17], buffer[i * 8 + 18],
						buffer[i * 8 + 19] });
				if (type == 16) { // FILEINFO resource
					infoFound = true;
					subOff = unpack(new byte[] { buffer[i * 8 + 20],
							buffer[i * 8 + 21], buffer[i * 8 + 22],
							buffer[i * 8 + 23] });
					break;
				}
			}
			if (!infoFound) {
				return null;
			}

			subOff = subOff & 0x7fffffff;
			infoOff = unpack(new byte[] { buffer[subOff + 20],
					buffer[subOff + 21], buffer[subOff + 22],
					buffer[subOff + 23] }); // offset of first FILEINFO
			infoOff = infoOff & 0x7fffffff;
			infoOff = unpack(new byte[] { buffer[infoOff + 20],
					buffer[infoOff + 21], buffer[infoOff + 22],
					buffer[infoOff + 23] }); // offset to data
			int dataOff = unpack(new byte[] { buffer[infoOff],
					buffer[infoOff + 1], buffer[infoOff + 2],
					buffer[infoOff + 3] });
			dataOff = dataOff - infoVirt;

			int version1 = unpack(new byte[] { buffer[dataOff + 48],
					buffer[dataOff + 48 + 1] });
			int version2 = unpack(new byte[] { buffer[dataOff + 48 + 2],
					buffer[dataOff + 48 + 3] });
			int version3 = unpack(new byte[] { buffer[dataOff + 48 + 4],
					buffer[dataOff + 48 + 5] });
			int version4 = unpack(new byte[] { buffer[dataOff + 48 + 6],
					buffer[dataOff + 48 + 7] });
			return version2 + "." + version1 + "." + version4 + "." + version3;
		} catch (Exception e) {
			return null;
		} finally {
			if (raf != null) {
				try {
					raf.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static int unpack(byte[] b) {
		int num = 0;
		for (int i = 0; i < b.length; i++) {
			num = 256 * num + (b[b.length - 1 - i] & 0xff);
		}
		return num;
	}

	public static void main(String[] args) {
		try {
//			String filePath = "D:\\Program Files (x86)\\Wiz\\Wiz.exe";
			String filePath = "D:\\Program Files (x86)\\360\\360Chrome\\Chrome\\Application\\360chrome.exe";
			File file = new File(filePath);
			System.out.println("软件版本号:" + getVersion(file));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

真的很赞,效果美极了!

附上完整文件资源下载地址:http://download.csdn.net/download/luo201227/7336013

转载至:http://blog.csdn.net/luo201227/article/details/25638735

发表在 Java | 留下评论

Java使用javax.mail.jar发送邮件并允许发送附件

由于Java在开发网页上占有绝大优势,所以作为web端的领军人物,譬如发送短信和发送邮件这些就成了必然,网络安全一再安全我们需要把账号的安全级别提到更高!因此这些对于开发人员也就成了必须掌握的技能!我一直都觉得作为开发人员,不怕在开发的过程中遇到多少难题,而是有没有去解决它的勇气和决心,这里多学习就成为了关键,成为了一个程序猿发展的重中之重!好啦,闲话就说到这里,来点实际的!

当一个开发人员工作到一定程度后,面向对象的思维就会一直在他的老子里围绕!所以我们先把邮件的常量类、实体类以及工具类等等先该封装的封装,该继承的继承!代码来了,睁大眼睛:

/**
 * @Description: 
 *
 * @Title: SimpleMail.java
 * @Package com.joyce.bean
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-28 上午09:06:51
 * @version V2.0
 */
package com.joyce.mail.bean;

/**
 * @Description:邮件信息类
 * 
 * @ClassName: SimpleMail
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-5-28 上午09:06:51
 * @version V2.0
 */
public class Mail {
	/**
	 * 主题
	 */
	private String subject;
	/**
	 * 内容
	 */
	private String content;

	/**
	 * @return the subject
	 */
	public String getSubject() {
		return subject;
	}

	/**
	 * @param subject
	 *            the subject to set
	 */
	public void setSubject(String subject) {
		this.subject = subject;
	}

	/**
	 * @return the content
	 */
	public String getContent() {
		return content;
	}

	/**
	 * @param content
	 *            the content to set
	 */
	public void setContent(String content) {
		this.content = content;
	}
}

上面大家看到的就是发送邮件一般必不可少的邮件标题和邮件内容,作为在邮件使用广泛的那就免不了封装起来了,好啦,接下来咱们看邮箱的登录类:

/**
 * @Description: 
 *
 * @Title: MailAuthenticator.java
 * @Package com.joyce.bean
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-28 上午08:59:11
 * @version V2.0
 */
package com.joyce.mail.bean;

import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;

/**
 * @Description: 邮箱登录类
 *
 * @ClassName: MailAuthenticator
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-28 上午08:59:11
 * @version V2.0
 */
public class MailAuthenticator extends Authenticator {
	/**
     * 用户名(登录邮箱)
     */
    private String username;
    
    /**
     * 密码
     */
    private String password;

	/**
	 * @return the username
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * @param username the username to set
	 */
	public void setUsername(String username) {
		this.username = username;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @param username
	 * @param password
	 */
	public MailAuthenticator(String username, String password) {
		this.username = username;
		this.password = password;
	}
	
	@Override
	protected PasswordAuthentication getPasswordAuthentication() {
	    return new PasswordAuthentication(username, password);
    }
}

这时候我们需要的常量类就应该进来了

package com.joyce.mail.bean;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Description: 邮件发送常量类
 *
 * @ClassName: MailConstant
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-28 上午11:01:16
 * @version V2.0
 */
public class MailConstant {
	public static final String MAIL_USER = "luo201227@163.com"; 
	public static final String MAIL_PWD = "*****";
	public static final boolean MAIL_IFDEBUG = true;
	public static final String MAIL_CONTENT_CHARSET = "text/html;charset=utf-8"; 
	public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 E");
	public static final String MAIL_TITLE = "*********账号激活码" + sdf.format(new Date());//邮件标题
	public static String getMailContent(String content){
		StringBuffer sb = new StringBuffer();
		sb.append("<div style='width:1024px;height:auto;margin:0px auto;background-color:#66CCFF;font-size:14px;font-family:微软雅黑;border-radius:5px;padding:5px;'><center><h1>");
		sb.append("</h1></center><div style='margin-left:20px;margin-bottom:10px;'><b>尊敬的用户,您好!</b><br/><br/>");
		sb.append("    <b></b>"+content);		
		sb.append("</div></div>");
		return sb.toString();
	}
}

关键来了,看看发送邮件的关键部分吧

/**
 * @Description: 
 *
 * @Title: MailSender.java
 * @Package com.joyce.service.impl
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-28 上午09:03:08
 * @version V2.0
 */
package com.hupu.nac.mail.sender;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import com.hupu.nac.mail.bean.Mail;
import com.hupu.nac.mail.bean.MailAuthenticator;
import com.hupu.nac.mail.bean.MailConstant;

/**
 * @Description:邮件发送类
 * 
 * @ClassName: MailSender
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-5-28 上午09:03:08
 * @version V2.0
 */
public class MailSender {
	/**
	 * 发送邮件的props文件
	 */
	private final transient Properties props = new Properties();
	/**
	 * 邮件服务器登录验证
	 */
	private transient MailAuthenticator authenticator;

	/**
	 * 邮箱session
	 */
	private transient Session session;

	/**
	 * 初始化邮件发送器
	 * 
	 * @param smtpHostName
	 *            SMTP邮件服务器地址
	 * @param username
	 *            发送邮件的用户名(地址)
	 * @param password
	 *            发送邮件的密码
	 */
	public MailSender(final String smtpHostName, final String username,
			final String password) {
		init(username, password, smtpHostName);
	}

	/**
	 * 初始化邮件发送器
	 * 
	 * @param username
	 *            发送邮件的用户名(地址),并以此解析SMTP服务器地址
	 * @param password
	 *            发送邮件的密码
	 */
	public MailSender(final String username, final String password) {
		// 通过邮箱地址解析出smtp服务器,对大多数邮箱都管用
		final String smtpHostName = "smtp." + username.split("@")[1];
		init(username, password, smtpHostName);

	}

	/**
	 * @Description: 初始化
	 * 
	 * @param username
	 *            发送邮件的用户名(地址)
	 * @param password
	 *            密码
	 * @param smtpHostName
	 *            SMTP主机地址
	 * 
	 * @Title: MailSender.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-5-28 上午09:18:31
	 * @version V2.0
	 */
	private void init(String username, String password, String smtpHostName) {
		// 初始化props
		props.put("mail.smtp.host", smtpHostName);
		props.put("mail.smtp.auth", "true");
		// 验证
		authenticator = new MailAuthenticator(username, password);
		// 创建session
		session = Session.getInstance(props, authenticator);
		// 打印一些调试信息
		session.setDebug(MailConstant.MAIL_IFDEBUG);
	}

	/**
	 * @Description: 发送邮件
	 * 
	 * @param recipient
	 *            收件人邮箱地址
	 * @param subject
	 *            邮件主题
	 * @param content
	 *            邮件内容
	 * @throws AddressException
	 * @throws MessagingException
	 * 
	 * @Title: MailSender.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-5-28 上午09:19:16
	 * @version V2.0
	 */
	public void send(String recipient, String subject, Object content) throws Exception {
		send(recipient, subject, content, null);
	}

	/**
	 * 发送邮件
	 * 
	 * @param recipient
	 *            收件人邮箱地址
	 * @param subject
	 *            邮件主题
	 * @param content
	 *            邮件内容
	 * @param files
	 *            附件
	 * @throws Exception
	 * @author Joyce.Luo
	 * @date 2014-10-15 上午10:23:09
	 * @version V3.0
	 * @since Tomcat6.0,Jdk1.6
	 * @copyright: Copyright (c) 2014
	 */
	public void send(String recipient, String subject, Object content, Vector<File> files) throws Exception {
		// 创建mime类型邮件
		final MimeMessage message = new MimeMessage(session);
		// 设置发信人
		message.setFrom(new InternetAddress(authenticator.getUsername()));
		// 设置收件人
		message.setRecipient(Message.RecipientType.TO, new InternetAddress(
				recipient));
		// 设置主题
		message.setSubject(subject);
		// 设置邮件内容
		if (null == files || files.size() == 0) {
			message.setContent(content.toString(), MailConstant.MAIL_CONTENT_CHARSET);
		} else {
			//创建 Mimemultipart添加内容(可包含多个附件)
			MimeMultipart multipart = new MimeMultipart();
			//MimeBodyPart(用于信件内容/附件)
			BodyPart bodyPart = new MimeBodyPart();
			bodyPart.setContent(content.toString(), MailConstant.MAIL_CONTENT_CHARSET);
			//添加到MimeMultipart对象中
			multipart.addBodyPart(bodyPart);
			for (int i = 0; i < files.size(); i++) {
				File file = (File) files.elementAt(i);
				String fname = file.getName();
				//创建FileDAtaSource(用于添加附件)
				FileDataSource fds = new FileDataSource(file);
				BodyPart fileBodyPart = new MimeBodyPart();
				// 字符流形式装入文件
				fileBodyPart.setDataHandler(new DataHandler(fds));
				// 设置附件文件名
				fileBodyPart.setFileName(fname);
				multipart.addBodyPart(fileBodyPart);
				message.setContent(multipart);
			}
		}
		// 设置发信时间
		message.setSentDate(new Date());
		// 存储邮件信息
		message.saveChanges();
//		message.setFileName(filename)
		// 发送邮件
		Transport.send(message);
	}

	/**
	 * @Description: 群发邮件
	 * 
	 * @param recipients
	 *            收件人们
	 * @param subject
	 *            主题
	 * @param content
	 *            内容
	 * @throws AddressException
	 * @throws MessagingException
	 * 
	 * @Title: MailSender.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-5-28 上午09:20:24
	 * @version V2.0
	 */
	public void send(List<String> recipients, String subject, Object content) throws Exception {
		send(recipients, subject, content, null);
	}

	/**
	 * 群发邮件
	 * 
	 * @param recipients
	 *            收件人们
	 * @param subject
	 *            主题
	 * @param content
	 *            内容
	 * @param files
	 *            附件
	 * @throws Exception
	 * @author Joyce.Luo
	 * @date 2014-10-15 上午10:26:35
	 * @version V3.0
	 * @since Tomcat6.0,Jdk1.6
	 * @copyright: Copyright (c) 2014
	 */
	public void send(List<String> recipients, String subject, Object content, Vector<File> files) throws Exception {
		// 创建mime类型邮件
		final MimeMessage message = new MimeMessage(session);
		// 设置发信人
		message.setFrom(new InternetAddress(authenticator.getUsername()));
		// 设置收件人们
		final int num = recipients.size();
		InternetAddress[] addresses = new InternetAddress[num];
		for (int i = 0; i < num; i++) {
			addresses[i] = new InternetAddress(recipients.get(i));
		}
		message.setRecipients(Message.RecipientType.TO, addresses);
		// 设置主题
		message.setSubject(subject);
		// 设置邮件内容
		if (null == files || files.size() == 0) {
			message.setContent(content.toString(), MailConstant.MAIL_CONTENT_CHARSET);
		} else {
			 //创建 Mimemultipart添加内容(可包含多个附件)
			MimeMultipart multipart = new MimeMultipart();
			  //MimeBodyPart(用于信件内容/附件)
			BodyPart bodyPart = new MimeBodyPart();
			bodyPart.setContent(content.toString(), MailConstant.MAIL_CONTENT_CHARSET);
			 //添加到MimeMultipart对象中
			multipart.addBodyPart(bodyPart);
			for (int i = 0; i < files.size(); i++) {
				File file = (File) files.elementAt(i);
				String fname = file.getName();
				//创建FileDAtaSource(用于添加附件)
				FileDataSource fds = new FileDataSource(file);
				BodyPart fileBodyPart = new MimeBodyPart();
				// 字符流形式装入文件
				fileBodyPart.setDataHandler(new DataHandler(fds));
				// 设置附件文件名
				fname = new String(fname.getBytes("UTF-8"), "ISO-8859-1");
				fileBodyPart.setFileName(fname);
				multipart.addBodyPart(fileBodyPart);
				message.setContent(multipart);
			}
		}
		// 设置发信时间
		message.setSentDate(new Date());
		// 存储邮件信息
		message.saveChanges();
		// 发送邮件
		Transport.send(message);
	}

	/**
	 * @Description: 发送邮件
	 * 
	 * @param recipient
	 *            收件人邮箱地址
	 * @param mail
	 *            邮件对象
	 * @throws Exception
	 * 
	 * @Title: MailSender.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-5-28 上午09:20:54
	 * @version V2.0
	 */
	public void send(String recipient, Mail mail) throws Exception {
		send(recipient, mail.getSubject(), mail.getContent());
	}

	/**
	 * @Description: 群发邮件
	 * 
	 * @param recipients
	 *            收件人们
	 * @param mail
	 *            邮件对象
	 * @throws Exception
	 * 
	 * @Title: MailSender.java
	 * @Copyright: Copyright (c) 2014
	 * 
	 * @author Comsys-LZP
	 * @date 2014-5-28 上午09:21:19
	 * @version V2.0
	 */
	public void send(List<String> recipients, Mail mail) throws Exception {
		send(recipients, mail.getSubject(), mail.getContent());
	}

	/**
	 * 群发邮件
	 * 
	 * @param recipients
	 *            收件人们
	 * @param mail
	 *            邮件对象
	 * @param files
	 *            附件
	 * @throws Exception
	 * @author Joyce.Luo
	 * @date 2014-10-15 上午10:28:38
	 * @version V3.0
	 * @since Tomcat6.0,Jdk1.6
	 * @copyright: Copyright (c) 2014
	 */
	public void send(List<String> recipients, Mail mail, Vector<File> files) throws Exception {
		send(recipients, mail.getSubject(), mail.getContent(), files);
	}
}

在网页上的激活码或者验证码这类的就不用多说了吧

/**
 * @Description: 
 *
 * @Title: RandomCodeUtil.java
 * @Package com.joyce.mail.util
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-5-28 上午11:07:34
 * @version V2.0
 */
package com.joyce.mail.util;


/**
 * @Description: 随机码工具类
 * 
 * @ClassName: RandomCodeUtil
 * @Copyright: Copyright (c) 2014
 * 
 * @author Comsys-LZP
 * @date 2014-5-28 上午11:07:34
 * @version V2.0
 */
public class RandomCodeUtil {
	/**
	 * 随机码集合
	 */
	private static final String[] randCode = { "0", "1", "2", "3", "4", "5", "6",
			"7", "8", "9", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p",
			"a", "s", "d", "f", "g", "h", "j", "k", "l", "z", "x", "c", "v",
			"b", "n", "m" };
	
	/**
	 * @Description: 产生指定长度的随机码
	 *
	 * @param codeLength
	 * @return
	 *
	 * @Title: RandomCodeUtil.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-28 上午11:11:55
	 * @version V2.0
	 */
	public static String randomCode(Integer codeLength) throws Exception {
		try {
			StringBuffer code = new StringBuffer();
			if(null == codeLength || 0 == codeLength){
				codeLength = 4;
			}
			for (int i = 0; i < codeLength; i++) {
				code.append(randCode[(int)Math.floor(Math.random() * 36)]);
			}
			return code.toString();
		} catch (Exception e) {
			throw new RuntimeException("Random Code Error");
		}
	}
	
	/**
	 * @Description: 生成长度为4的随机码
	 *
	 * @return
	 * @throws Exception
	 *
	 * @Title: RandomCodeUtil.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-5-28 下午01:19:33
	 * @version V2.0
	 */
	public static String randomCode() throws Exception{
		return randomCode(null);
	}
}

Ok,利用javax.mail.jar发送邮件就大功告成了!是不是很easy呢!完整的demo资源;下载地址:http://download.csdn.net/download/luo201227/7446279

有段时间了,来更新一下,其实也没有什么很多要说的,只是跟大家提一下,如果有兴趣的,可以去了解一下James邮件服务器的搭建!

转载至:http://blog.csdn.net/luo201227/article/details/28425283

发表在 Java | 留下评论

jQuery经典面试题及答案精选

jQuery是一款非常流行的Javascript框架,如果你想要从事Web前端开发这个岗位,那么jQuery是你必须掌握而且能够熟练应用的一门技术。本文整理了一些关于jQuery的经典面试题及答案,分享给正要面试Web开发岗位的同学。

问题:jQuery的美元符号$有什么作用?

回答:其实美元符号$只是”jQuery”的别名,它是jQuery的选择器,如下代码:

$(document).ready(function(){

});

当然你也可以用jQuery来代替$,如下代码:

jQuery(document).ready(function(){

});

jQuery中就是通过这个美元符号来实现各种灵活的DOM元素选择的,例如$(“#main”)即选中id为main的元素。

问题:body中的onload()函数和jQuery中的document.ready()有什么区别?

回答:onload()和document.ready()的区别有以下两点:

1、我们可以在页面中使用多个document.ready(),但只能使用一次onload()。

2、document.ready()函数在页面DOM元素加载完以后就会被调用,而onload()函数则要在所有的关联资源(包括图像、音频)加载完毕后才会调用。

问题:jQuery中有哪几种类型的选择器?

回答:从我自己的角度来讲,可以有3种类型的选择器,如下:

1、基本选择器:直接根据id、css类名、元素名返回匹配的dom元素。

2、层次选择器:也叫做路径选择器,可以根据路径层次来选择相应的DOM元素。

3、过滤选择器:在前面的基础上过滤相关条件,得到匹配的dom元素。

问题:请使用jQuery将页面上的所有元素边框设置为2px宽的虚线?

回答:这正是jQuery选择器上场的时候了,代码如下:

<script language="javascript" type="text/javascript">

         $("*").css("border", "2px dotted red"); 

</script>

问题:当CDN上的jQuery文件不可用时,该怎么办?

回答:为了节省带宽和脚本引用的稳定性,我们会使用CDN上的jQuery文件,例如google的jquery cdn服务。但是如果这些CDN上的jQuery服务不可用,我们还可以通过以下代码来切换到本地服务器的jQuery版本:

<script type="text/javascript" language="Javascript" src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.4.1.min.js "></script>

<script type='text/javascript'>//<![CDATA[

if (typeof jQuery == 'undefined') {

document.write(unescape("%3Cscript src='/Script/jquery-1.4.1.min.js' type='text/javascript' %3E%3C/script%3E"));

}//]]>

</script>

问题:如何使用jQuery实现点击按钮弹出一个对话框?

回答:代码如下:

HTML:

<input id="inputField" type="text" size="12"/>

jQuery:

<script type="text/javascript"> 
$(document).ready(function () {
 $('#Button1').click(function () { 
alert($('#inputField').attr("value")); 
}); 
});
 </script>

问题:jQuery中的Delegate()函数有什么作用?

回答:delegate()会在以下两个情况下使用到:

1、如果你有一个父元素,需要给其下的子元素添加事件,这时你可以使用delegate()了,代码如下:

$("ul").delegate("li", "click", function(){

$(this).hide();

});

2、当元素在当前页面中不可用时,可以使用delegate()

问题:怎样用jQuery编码和解码URL?

回答:在jQuery中,我们可以使用以下方法实现URL的编码和解码。

encodeURIComponent(url) and decodeURIComponent(url)

问题:如何用jQuery禁用浏览器的前进后退按钮?

回答:实现代码如下:

<script type="text/javascript" language="javascript">

$(document).ready(function() {

     window.history.forward(1);
     //OR
     window.history.forward(-1);

});

</script>

转载至:http://blog.csdn.net/luo201227/article/details/39177195

发表在 Java | 留下评论

Apache Mina2实现类似QQ、飞秋的聊天功能

至于MINA2是什么,这里就不介绍了,有兴趣的可以自己去问一下度娘!

首先使用MINA2的时候,你最起码的知道怎么下载jar包和在工程中引入哪些jar包。

附上下载地址:http://mirrors.cnnic.cn/apache/mina/mina/2.0.7/dist/apache-mina-2.0.7-bin.zip 和必须引入工程的jar包:mina-core-2.0.7.jar;slf4j-api-1.6.6.jar;slf4j-log4j12-1.7.5.jar,不多说直接上代码:

 

第一步:编写通信要用的编码器类、解码器类和编解码工厂类

1、字符编码:

/**
 * @Description: 
 *
 * @Title: CharsetEncoder.java
 * @Package com.joyce.mina.code
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:48:53
 * @version V2.0
 */
package com.joyce.mina.code;

import java.nio.ByteOrder;
import java.nio.charset.Charset;

import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

/**
 * @Description: 字符编码
 *
 * @ClassName: CharsetEncoder
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:48:53
 * @version V2.0
 */
public class CharsetEncoder implements ProtocolEncoder {
	
	/**
	 * Log4j日志
	 */
	private static final Logger logger = Logger.getLogger(CharsetEncoder.class);
	
	private static final Charset charset = Charset.forName("UTF-8");

	@Override
	public void dispose(IoSession session) throws Exception {
		logger.info("#####################dispose#########################");
	}

	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
		logger.debug("#####################encode#########################");
		IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true);
		buff.order(ByteOrder.LITTLE_ENDIAN);
		String smsgbody = message.toString();
		byte[] bytearr = smsgbody.getBytes(charset);
		buff.put(bytearr);
		// 为下一次读取数据做准备
		buff.flip();
		out.write(buff);
	}
}

 

2、字符解码:

/**
 * @Description: 
 *
 * @Title: CharsetDecoder.java
 * @Package com.joyce.mina.code
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:35:49
 * @version V2.0
 */
package com.joyce.mina.code;

import java.nio.charset.Charset;

import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

/**
 * @Description:  字符解码
 *
 * @ClassName: CharsetDecoder
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:35:49
 * @version V2.0
 */
public class CharsetDecoder implements ProtocolDecoder {
	
	/**
	 * Log4j日志
	 */
	private final static Logger logger = Logger.getLogger(CharsetDecoder.class);
	
	private static final Charset charset = Charset.forName("UTF-8");
	// 可变的IoBuffer数据缓冲区
	private IoBuffer buff = IoBuffer.allocate(100).setAutoExpand(true);

	@Override
	public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		logger.debug("#####################decode#########################");
		// 如果有信息
		if (in.hasRemaining()) {
			// 判断消息是否是结束符,不同平台的结束符也不一样;
                        // windows换行符(\r\n)就认为是一个完整消息的结束符了; UNIX 是\n;MAC 是\r
			byte b = in.get();
			if (b == '\n') {
				buff.flip();
				byte[] bytes = new byte[buff.limit()];
				buff.get(bytes);
				String message = new String(bytes, charset);
				buff = IoBuffer.allocate(100).setAutoExpand(true);
				// 如果结束了,就写入转码后的数据
				out.write(message);
			} else {
				buff.put(b);
			}
		}
	}

	@Override
	public void dispose(IoSession session) throws Exception {
		logger.info("#####################dispose#########################");
		logger.info(session.getCurrentWriteMessage());
		
	}

	@Override
	public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
		logger.info("#####################完成解码#########################");
	}
}

上面的decode方法是解码方法,它主要是把读取到数据中的换行符去掉。因为在mina通信协议中以换行符为结束符,如果不定义结束符那么程序会在那里一直等待下一条发送的数据。

这里用到了IoBuffer,MiNa中传输的所有二进制信息都存放在IoBuffer中,IoBuffer是对Java NIO中ByteBuffer的封装(Mina2.0以前版本这个接口也是ByteBuffer),提供了更多操作二进制数据,对象的方法,并且存储空间可以自增长,用起来非常方便;简单理解,它就是个可变长度的byte字节数组!

1). static IoBuffer allocate(int capacity,boolean useDirectBuffer)

创建IoBuffer实例,第一个参数指定初始化容量,第二个参数指定使用直接缓冲区还是JAVA 内存堆的缓存区,默认为false。

2).IoBuffer setAutoExpand(boolean autoExpand)

这个方法设置IoBuffer 为自动扩展容量,也就是前面所说的长度可变,那么可以看出长度可变这个特性默认是不开启的。

3). IoBuffer flip()

limit=position, position=0,重置mask,为了读取做好准备,一般是结束buffer操作,将buffer写入输出流时调用;这个必须要调用,否则极有可能position!=limit,导致position后面没有数据;每次写入数据到输出流时,必须确保position=limit。

4). IoBuffer clear()与IoBuffer reset()

clear:limit=capacity , position=0,重置mark;它是不清空数据,但从头开始存放数据做准备—相当于覆盖老数据。

reset就是清空数据

5). int remaining()与boolean hasRemaining()

这两个方法一般是在调用了flip方法后使用的,remaining()是返回limt-position的值!hasRemaining()则是判断当前是否有数据,返回position < limit的boolean值!

3、编解码过滤工厂:

/**
 * @Description: 
 *
 * @Title: CharsetCodecFactory.java
 * @Package com.joyce.mina.code.factory
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:34:14
 * @version V2.0
 */
package com.joyce.mina.code.factory;

import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;

import com.joyce.mina.code.CharsetDecoder;
import com.joyce.mina.code.CharsetEncoder;

/**
 * @Description: 字符编码、解码工厂类,编码过滤工厂
 *
 * @ClassName: CharsetCodecFactory
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:34:14
 * @version V2.0
 */
public class CharsetCodecFactory implements ProtocolCodecFactory {

	@Override
	public ProtocolDecoder getDecoder(IoSession session) throws Exception {
		return new CharsetDecoder();
	}

	@Override
	public ProtocolEncoder getEncoder(IoSession session) throws Exception {
		return new CharsetEncoder();
	}
}

 

 

第二步:编写IoHandler实现类代码

IoHander是Io读写的事件驱动类,这里的Io操作都会触发里面的事件。所有的业务逻辑都应当在这个类中完成,个人建议如果能封装出来的尽量封装出来,否则业务代码会很多很大!我这里就实现了封装

/**
 * @Description: 
 *
 * @Title: ServerMessageReceived.java
 * @Package com.joyce.mina.message
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-21 上午08:54:13
 * @version V2.0
 */
package com.joyce.mina.message;

import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;

import org.apache.log4j.Logger;
import org.apache.mina.core.session.IoSession;

/**
 * @Description: 消息处理
 *
 * @ClassName: ServerMessageReceived
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-21 上午08:54:13
 * @version V2.0
 */
public class ServerMessageUtil {
	
	/**
	 * Log4j日志对象
	 */
	private static final Logger logger = Logger.getLogger(ServerMessageUtil.class);
	
	/**
	 * @Description: 服务器接收信息处理
	 *
	 * @param session
	 * @param message
	 *
	 * @Title: ServerMessageReceived.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-3-21 上午08:55:53
	 * @version V2.0
	 */
	public static void messageReceived(IoSession session, Object message){
		String content = message.toString();
		String datetime = ServerMessageUtil.getNowDate();
		// 拿到所有的客户端Session
		Collection<IoSession> sessions = ServerMessageUtil.getAllClient(session);
		// 向所有客户端发送数据
		if(content.trim().equals("1")){
			logger.info("在线用户列表:");
			session.write("在线用户列表:\r\n");
			for (IoSession sess : sessions) {
				logger.info("Client" + sess.getId() + ":\t" + sess.getRemoteAddress());
				session.write("Client" + sess.getId() + ":\t" + sess.getRemoteAddress() + "\r\n");
			}
		} else if("2".equals(content.trim())){
			logger.info("说明:");
			StringBuffer sb = new StringBuffer();
			sb.append("说明:");
			sb.append("\r\n");
			sb.append("1、使用@符号进行私聊(@127.0.0.1#你好!-- @ip#你好!)");
			sb.append("\r\n");
			sb.append("2、无@符号属于群聊");
			sb.append("\r\n");
			sb.append("3、新增quit和exit命令退出");
			sb.append("\r\n");
			session.write(sb);
		} else if("quit".equalsIgnoreCase(content.trim()) || "exit".equalsIgnoreCase(content.trim())){
			session.close(true);
		} else if(content.trim().indexOf("@") != -1 && content.trim().indexOf("#") != -1){
			String ip = content.substring(content.indexOf("@") + 1,content.indexOf("#"));
			for (IoSession sess : sessions) {
				logger.info(sess.getRemoteAddress() + "-->IP:" + ip);
				if(sess.getRemoteAddress().toString().indexOf(ip) != -1){
					logger.info("Clinet:" + session.getRemoteAddress() + "即将对" + sess.getRemoteAddress() + "私聊");
					session.write("您对客户端:" + sess.getRemoteAddress() + "说:" + content.substring(content.indexOf("#")+1) + "\n");
					sess.write("客户端:" + session.getRemoteAddress() + "对您说:" + content.substring(content.indexOf("#")+1) + "\n");
				}
			}
		} else if(!"\r".equals(content)) {
			for (IoSession sess : sessions) {
				logger.info("转发给:" + session.getRemoteAddress() + "客户端   messageReceived: " + datetime + "\t" + content);
				sess.write("客户端:" + session.getRemoteAddress() + "在" + datetime + "对所有人说:\t" + content + "\n");
			}
		} else {
			session.write("\r");
		}
	}
	
	/**
	 * @Description: 获取连接所有客户端 
	 *
	 * @param session
	 * @return
	 *
	 * @Title: ServerMessageUtil.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-3-21 上午11:26:47
	 * @version V2.0
	 */
	public static Collection<IoSession> getAllClient(IoSession session){
		// 拿到所有的客户端Session
        	return session.getService().getManagedSessions().values();
	}
	
	/**
	 * @Description: 获取现在时间,并且格式为yyyy-MM-dd hh:mm:ss
	 *
	 * @return
	 *
	 * @Title: ServerMessageUtil.java
	 * @Copyright: Copyright (c) 2014
	 *
	 * @author Comsys-LZP
	 * @date 2014-3-21 上午11:34:09
	 * @version V2.0
	 */
	public static String getNowDate(){
		 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		 return sdf.format(new Date());
	}
}

 

上面的就是封装的一些常用的方法和业务处理等。接下来就是IoHandler实现类了

/**
 * @Description: 
 *
 * @Title: ServerMessageHandler.java
 * @Package com.joyce.mina.server.message
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:54:31
 * @version V2.0
 */
package com.joyce.mina.server.message;

import java.util.Collection;

import org.apache.log4j.Logger;
import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import com.joyce.mina.message.ServerMessageUtil;

/**
 * @Description: 处理服务器端消息
 *
 * @ClassName: ServerMessageHandler
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 上午11:54:31
 * @version V2.0
 */
public class ServerMessageHandler implements IoHandler {
	
	private static final Logger logger = Logger.getLogger(ServerMessageHandler.class);

	@Override
	public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
		logger.info("服务器发生异常:" + cause.getMessage());
	}

	@Override
	public void messageReceived(IoSession session, Object message) throws Exception {
		logger.info("服务器接收到数据: " + message);
		ServerMessageUtil.messageReceived(session, message);
	}

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		logger.info("服务器发送消息:" + message);
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		logger.info("关闭当前session:" + session.getId() + "#" + session.getRemoteAddress());
        	CloseFuture closeFuture = session.close(true);
        	closeFuture.addListener(new IoFutureListener<IoFuture>() {
			@Override
			public void operationComplete(IoFuture future) {
				if (future instanceof CloseFuture) {
					((CloseFuture) future).setClosed();
					logger.info("sessionClosed CloseFuture setClosed--> " + future.getSession().getId());
				}
			}
		});
        
        	// 获取所有客户端
        	Collection<IoSession> sessions = ServerMessageUtil.getAllClient(session);
        	// 给每个客户端发送离开信息
        	for (IoSession sess : sessions) {
			sess.write("客户端:" + session.getRemoteAddress() + "在" + ServerMessageUtil.getNowDate() + "离开");
		}
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		logger.info("创建一个新连接:" + session.getRemoteAddress());
		StringBuffer info = new StringBuffer();
		info.append("welcome to the joyce chat room !");
		info.append("\r\n\r\n");
		session.write(info);
		
		// 获取所有在线客户端
		Collection<IoSession> sessions = ServerMessageUtil.getAllClient(session);
		for (IoSession sess : sessions) {
			StringBuffer welcome = new StringBuffer();
			welcome.append("客户端:" + session.getRemoteAddress() + "在" + ServerMessageUtil.getNowDate() + "加入!");
			welcome.append("\r\n\r\n");
			sess.write(welcome);
		}
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
		logger.info("当前连接" + session.getRemoteAddress() + "处于空闲状态:" + status);
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		logger.info("打开一个session:" + session.getId() + "#" + session.getBothIdleCount());
		StringBuffer info = new StringBuffer();
		info.append("1:获取所有在线用户\t2:说明");
		info.append("\r\n");
		session.write(info);
	}
}

sessionCreated:当一个新的连接建立时,由I/O processor thread调用;

sessionOpened:当连接打开是调用;

messageReceived: 当接收了一个消息时调用;

messageSent:当一个消息被(IoSession#write)发送出去后调用;

sessionIdle:当连接进入空闲状态时调用;

sessionClosed:当连接关闭时调用;

exceptionCaught:实现IoHandler的类抛出异常时调用;

一般情况下,我们最关心的只有messageReceived方法,接收消息并处理,然后调用IoSession的write方法发送出消息!(注意:这里接收到的消息都是Java对象,在IoFilter中所有二进制数据都被解码)一般情况下很少有人实现IoHandler接口,而是继承它的一个实现类IoHandlerAdapter,这样不用覆盖它的7个方法,只需要根据具体需求覆盖其中的几个方法就可以!

Iohandler的7个方法其实是根据session的4个状态值间变化来调用的:

 Connected:会话被创建并使用;

 Idle:会话在一段时间(可配置)内没有任何请求到达,进入空闲状态;

 Closing:会话将被关闭(剩余message将被强制flush);

 Closed:会话被关闭;

 

第三步:编写Server启动类,bind端口,设置编码过程和核心业务处理器

/**
 * @Description: 
 *
 * @Title: MinaServer.java
 * @Package com.joyce.mina.server
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 下午12:35:48
 * @version V2.0
 */
package com.joyce.mina.server;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LogLevel;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import com.joyce.mina.code.factory.CharsetCodecFactory;
import com.joyce.mina.server.message.ServerMessageHandler;

/**
 * @Description: 服务器启动类 
 *
 * @ClassName: MinaServer
 * @Copyright: Copyright (c) 2014
 *
 * @author Comsys-LZP
 * @date 2014-3-19 下午12:35:48
 * @version V2.0
 */
public class MinaServer {
	
	private SocketAcceptor acceptor;

	/**
	 * 
	 */
	public MinaServer() {
		// 创建非阻塞的server端的Socket连接
        acceptor = new NioSocketAcceptor();
	}
	
	public boolean start(){
		// 获取过滤器链
		DefaultIoFilterChainBuilder filterChain = acceptor.getFilterChain();
		// 添加编码过滤器 处理乱码、编码问题
		filterChain.addLast("codec", new ProtocolCodecFilter(new CharsetCodecFactory()));
		
		// 添加日志过滤器
		LoggingFilter loggingFilter = new LoggingFilter();
        	loggingFilter.setMessageReceivedLogLevel(LogLevel.INFO);
        	loggingFilter.setMessageSentLogLevel(LogLevel.INFO);
        	filterChain.addLast("logger", loggingFilter);
        
		// 设置核心消息业务处理器
        	acceptor.setHandler(new ServerMessageHandler());
		// 设置session配置,30秒内无操作进入空闲状态
		acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
		try {
			// 绑定端口3456
            		acceptor.bind(new InetSocketAddress(3456));
        	} catch (IOException e) {
            		return false;
        	}
		return true;
	}
	
	public static void main(String[] args) {
		MinaServer server = new MinaServer();
        	server.start();
	}
}

这里只是一个简单的MINA2服务器的启动,框架很成熟很强大,可以通过Spring配置!这方面的先不扯开,有机会再分享!

此时就可以通过输入http://localhost:3456 来访问了,在控制台会有大量数据输出,绝大部分是浏览器的相关信息和服务器发出去的信息!如果懂telnet的程序猿可以去通过telnet连接上去,效果会更佳!

就说到这里了!

 

来附上完整项目的地址:http://download.csdn.net/download/luo201227/7101013

 

转载至:http://blog.csdn.net/luo201227/article/details/22150043

发表在 Java | 留下评论