日志分类:J2EE

java中文乱码

2010年04月1日 10:05 上午  |  分类:J2EE

作为一个java程序员,你一定都经受过java中文乱码的摧残,尤其是Jsp中文显示,相信是每个初做J2EE人的必过的坎。更要命的是,代码上明明觉得都没有什么问题,可显示出来就是乱码,更有甚者,调试的时候好好的,当你兴冲冲的部署到服务器上时,乱码了,你也就傻眼了,贼郁闷。ps:还有一个问题,我一直都没想明白,为什么java设计者要把java流设计的如此复杂,每一次在做流的处理时,我都要查jdk文档,要不然云里雾里的。

最近手头上一个项目,其中牵扯了很多的编码的问题,整个流程从网页源码的抓取,document对象转化,xml生成,到字节流抽取,页面显示,其中还伴随着输入输出流的交换。更要命的是,这个项目三个模块是由不同人完成了。所以跑下一个流程,五花八门,屏幕一堆火星文。

好吧,我只好拿出一天的时间来读每个人的代码,然后解决这个问题。还好,项目不是很大,类不是很多,函数也不是很复杂,折腾到下午问题总算解决。一个字,累。我没有什么好办法,只能用一个接口,把所有抓取到的页面源码统一成utf-8格式,然后其他模块中每一个牵扯到字符流,编码的地方,我都加上utf-8硬编码。很累,不过还是有点收获,在这里总结下:

1、整个项目编码格式要统一。

大家不要笑,这是一个老生常谈的东西,但是我敢打包票,一个团队做出来的项目总会在某个地方出问题。比如要用UTF-8编码,那你每一个文件都要加上UTF-8编码,包括java类,xml文件,jsp,html页面等等,总之所有的文件,最好借助于IDE工具进行统一编码。其中还要注意一点,如果用UTF-8编码,那么每一处都要用大写,而不要有的地方,用utf-8,有的用UTF-8。因为残酷的经验告诉我们,这在一个页面包含另一个页面时,会出问题的。

2、禁忌toString(),getBytes(),new String().

通常遇到字符乱码时,每个人都会说,我这里是好好的,不信你看。但是,请你相信,也许问题就出在你认为对的地方。项目是一个整体,如果你不遵守项目组制定的规范,那么在你的模块里再好也是徒劳无益的。所以在使用toString(),getBytes(),new String()这些函数时,请一定设定它们的编码格式,比如getBytes(”UTF-8″),toString(”UTF-8″),如果缺省,系统会采用java默认的编码格式,这往往就为你的团队埋下了苦果。

3、请你相信htmlStr=new String(htmlStr.getBytes(”utf-8″),”utf-8″);

真的,有时候你得到的字符串明明是utf-8的,但是打印出来就是乱码,好吧我不知道这是为什么,但你可以试一下htmlStr=new String(htmlStr.getBytes(”UTF-8″),”UTF-8″);也许问题就会解决。

总之,解决java中文乱码是一个细活,规范的开发手册+良好的编程习惯+细心也许会让你不至于有些时候手忙脚乱。

重构–模式

2009年12月5日 3:58 下午  |  分类:J2EE

代码的坏味道大家一看都不会陌生,绝对是在我们的编程中如影随形的,如何把相应的重构方法和我们学习的设计模式结合起来,以达到事半功倍,是我们每个程序员所关心的。网上一个很好的总结表格,贴出来,方便自己参考查阅,也希望对大家有所帮助。

代码的坏味道

一般重构方法

使用模式重构

重复代码

提炼方法
提取类
方法上移
替换算法
链构造方法

构造Template Method
以Composite取代一/多之分
引入Null Object
用Adapter统一接口
用Fatory Method引入多态创建

过长方法

提取方法
组合方法
以查询取代临时变量
引入参数对象
保持对象完整

转移聚集操作到Vistor
以Strategy取代条件逻辑
以Command取代条件调度程序
转移聚集操作到Collecting Parameter

过长参数列

以方法取代参数
引入参数对象
保持对象完整

条件逻辑过度复杂

分解条件式
合并条件式
合并重复的条件片段
移除控制标记
以卫语句取代嵌套条件式
以多态取代条件式
引入断言

以Strategy取代条件逻辑
转移装饰功能到Decorator
以State取代状态改变条件语句
引入Null Object

分支语句

提取方法
转移方法
以子类取代类型代码
以多态取代条件式
已明确方法取代参数

以State/Strategy取代类型代码
引入Null Object
以Command替换条件调度程序
转移聚集操作到Visitor

基本类型迷恋
程序代码过于依赖基本类型(int,string,double,array等低层次语言要素)

以对象取代数据值
以类型取代类型代码
以子类取代类型代码
提取类
引入参数对象
以对象取代数组

以State取代状态改变条件语句
以Strategy取代条件逻辑
以Composite取代隐含树
以Interpreter取代隐式语言
转移装饰功能到Decorator
用Builder封装Composite

数据泥团
在类的字段和参数列中,总是一起出现的数据

提取类
引入参数对象
保持对象完整

令人迷惑的临时字段

提取类

引入Null Object

组合爆炸
许多段代码使用不同种类或数量的数据或对象做同样的事情(例如使用特定条件和数据库查询)

以Interpreter取代隐式语言

过大类

提取类
提取子类
提取接口
复制被监视数据

以Command取代条件调度程序
以State取代状态改变条件语句
以Interpreter取代隐式语言

冗赘类
不再做很多工作或没有用的类

折叠继承关系
内联Singleton

不恰当的暴露
在客户代码中不应看到类的字段和方法,却是公开可见的

封装字段
封装群集
移除设置方法
隐藏方法

用Factory封装类

发散式变化
类经常因为不同的原因在不同方向上发生变化,显然是违反了单一职责原则

提取类

霰弹式修改
如果遇到变化,必须在不同的类中作出相应的修改

转移方法
转移字段
内联类

将创建知识搬移到Factory

依恋情结
方法对于某个类的兴趣高过对自己所处的宿主类

转移方法
提取方法

引入Strategy
引入Visitor

平行继承体系
当为一个类增加一个子类时,也必须在另一个类中增加一个相应的子类

转移方法
转移字段

夸夸其谈未来性

折叠继承关系
内联类
移除参数
移除方法

过度耦合的消息连
不断的向一个对象索求另一个对象

隐藏委托
提取方法
转移方法

使用抽象引入Chain Of Responsibility

中间转手人
类接口中有很多方法都委托给其他类

移除中间转手人
内联方法
以继承取代委托

狎昵关系
类之间彼此依赖于其private成员

转移方法
将双向关联改为单向
提取类
隐藏委托
以继承取代委托

异曲同工的类

重命名方法
转移方法
提取超类

用Adapter统一接口

不完善的程序库类

引入外加方法
引入本地扩展

用Adapter统一接口
用Facade封装类

纯稚的数据类
只拥有字段的数据类

封装字段
封装集合
移除设置方法
转移方法
隐藏方法

被拒绝的遗赠
继承父类时,子类想要选择继承的成员

以委托取代继承

过多的注释
为糟糕的代码写大量的注释

使用一起重构方法,使方法本身达到自说明的效果,让注释显得多余

怪异解决方案
在同一系统中使用不同的方式解决同一问题

替换算法

用Adapter统一接口

出来混的不求甚解早晚要还之tomcat

2009年04月26日 4:34 下午  |  分类:J2EE

废话题目都说了,做学问不求甚解,早晚有你抓耳挠腮的一天。

关于如何在tomcat下为不同的service配置不同的端口,实验室玉姐已经在她的博客中总结的很清楚了,我在这里不多说,详情大家可以访问:http://blog.sina.com.cn/s/blog_49f485700100egj0.html

我在这里主要介绍一下tomcat中server.xml中的service节点。

打开server.xml文件,会出现如下内容:

<Service name="Catalina">

<Connector

port="8080"

maxHttpHeaderSize="8192"

maxThreads="150" minSpareThreads="25" maxSpareThreads="75"

enableLookups="false" redirectPort="8443" acceptCount="100"

connectionTimeout="20000" disableUploadTimeout="true" />

<Connector port="8009"

enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />

<Engine name="Catalina" defaultHost="localhost">

<Realm className="org.apache.catalina.realm.UserDatabaseRealm"

resourceName="UserDatabase"/>

<Host name="localhost" appBase="webapps"

unpackWARs="true" autoDeploy="true"

xmlValidation="false" xmlNamespaceAware="false">

<!–自己加的,后面要用到–>

<Context path="" docBase="C:/Program Files/Apache Software Foundation/Tomcat 5.5/test/bb" debug="0" reloadable="true">

</Context>

</Host>

</Engine>
</Service>

Service由一个或者多个Connector,以及一个Engine组成。

Connector负责在指定端口上侦听客户请求,并将获得的请求交给Engine来处理,从Engine处获得回应并返回客户
TOMCAT有两个典型的Connector,一个直接侦听来自browser的http请求(8080端口),一个负责其它WebServer(Apache)的servlet/jsp代理请求(8009端口).

Engine负责处理所有Connector所获得的客户请求,可以配置多个虚拟主机Virtual Host,每个虚拟主机都有一个域名。当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理。当请求无法匹配到任何一个Host上的时候,将交给默认Host来处理。

Host—-代表一个Virtual Host,虚拟主机,每个虚拟主机和某个网络域名Domain Name相匹配
每个虚拟主机下都可以部署(deploy)一个或者多个Web App,每个Web App对应于一个Context,有一个Context path当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理,匹配的方法是“最长匹配”,所以一个path==""的Context将成为该Host的默认Context。所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配.

关于service中其他的标签,网上有很详细的赘述,我在这里不再多说,
那么随之而来的一个问题是,我们在brower中输入http://127.0.0.1:8080/时,系统默认的启动目录是root,如果我们开启了8081端口时,如果我们也想要这种默认效果时,我们可以通过在host文件夹下加入:
<Context path="" docBase="C:/Program Files/Apache Software Foundation/Tomcat 5.5/test/bb" debug="0" reloadable="true">

</Context>
这样在浏览器中输入http://127.0.0.1:8081就会自动运行bb项目。
ps:有时候设置了重启tomcat不好用,我们可以通过删除tomcat work文件夹下的东西,重启就可以了(work文件夹下存放的是jsp生成的servlet文件)

java io系统学习小结

2009年04月13日 10:57 上午  |  分类:J2EE, Java

1、java io 中提供了四种基类,reader writer inputstream outputstream,其中前两者为字符输入与输出,后

两者为字节输入输出。InputStreamReader 是字节流通向字符流的桥梁,它使用指定的 charset 读取字节并将其解码为字符,同理,OutputStreamWriter 是字符流通向字节流的桥梁:使用指定的 charset 将要向其写入的字符编码为字节。
2、运用上面介绍的io中的api(具体实现由子类完成)就可以完成任何操作了。但是,通过FilterInputStream和FileterOutStream的子类,我们可以为Stream添加属性,完成一些我们想要的额外功能。如下所示:如果我们要往一个文件中写入数据,我们可以这样操作:

FileOutStream fs = new FileOutStream(“test.txt”);
然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是,如果我们想实现“先把

要写入文件的数据先缓存到内存中,再把缓存中的数据写入文件中”的功能时,上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类,为FileOutStream添加我们所需要的功能。

FilterInputStream的各种类型
用于封装以字节为导向的InputStream
1) DataInputStream:从stream中读取基本类型(intchar等)数据。
2) BufferedInputStream:使用缓冲区
3) LineNumberInputStream:会记录input stream内的行数,然后可以调用getLineNumber()setLineNumber(int)
4) PushbackInputStream:很少用到,一般用于编译器开发
用于封装以字符为导向的InputStream
1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader,否则使用DataInputStream
2) BufferedReader:与BufferedInputStream对应
3) LineNumberReader:与LineNumberInputStream对应
4) PushBackReader:与PushbackInputStream对应
FilterOutStream的各种类型
用于封装以字节为导向的OutputStream
1) DataIOutStream:往stream中输出基本类型(intchar等)数据。
2) BufferedOutStream:使用缓冲区
3) PrintStream:产生格式化输出
用于封装以字符为导向的OutputStream
1) BufferedWrite:与对应
2) PrintWrite:与对应

这样我们在使用一个输入输出流时,一般不直接使用inputstream和outputsteam的子类们,而是先把他们包装在文件过滤器中,方便我们使用一些附加的功能:

我们以接收键盘输入为例,介绍一下用法:

Java语言: 实例
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a line:");
System.out.println(stdin.readLine());

具体关于java中io系统的总结,北大青鸟中有篇总结很好的文章,大家可以参考一下:http://www.cd-accp.net/News_detail.asp?N_id=175

context-param和init-param区别

2008年12月24日 9:50 上午  |  分类:J2EE

昨天泉哥给我们讲了一下他做得一个mvc的例子,学了很多东西,也终于发现了自己一直忽略的一个关于web-xml配置的一个小问题:

在web-xml中可以定义两种参数

(1)application参数,存放在servletcontext中,在web-xml中格式如下:

<context-param>

<param-name>context/param</param-name>

<param-value>avalible during application</param-value>

</context-param>

这种参数在servlet中是通过getServletContext().getInitParameter("context/param")方法得到

(2)servlet中的参数,只能在servlet的init()方法中得到,在web.xml中配置如下:

<servlet>

<servlet-name>MainServlet</servlet-name>

<servlet-class>com.wes.controller.MainServlet</servlet-class>

<init-param>

<param-name>param1</param-name>

<param-value>avalible in servlet init()</param-value>

</init-param>

<load-on-startup>0</load-on-startup>
</servlet>

我们通过函数this.getInitParameter("param1")得到。

关于jndi的一些认识

2008年11月22日 9:39 下午  |  分类:J2EE

论坛里看到网友写的很好的一篇文章,转载一下:

JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识。
那么,JNDI到底起什么作用?

要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。

没有JNDI的做法:
程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。
就像以下代码这样:

Java code
Connection conn=null; try { Class.forName("com.mysql.jdbc.Driver", true, Thread.currentThread().getContextClassLoader()); conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue"); ...... conn.close(); } catch(Exception e) { e.printStackTrace(); } finally { if(conn!=null) { try { conn.close(); } catch(SQLException e) {} } }

这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。

没有JNDI的做法存在的问题:
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整;
4、……

解决办法:
程序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。

由此,就有了JNDI。

用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。
具体操作如下(以JBoss为例):
1、配置数据源
在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:\jboss420GA\server\default\deploy。
修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS </jndi-name>
<connection-url>jdbc:mysql://localhost:3306/lw </connection-url>
<driver-class>com.mysql.jdbc.Driver </driver-class>
<user-name>root </user-name>
<password>rootpassword </password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter </exception-sorter-class-name>
<metadata>
<type-mapping>mySQL </type-mapping>
</metadata>
</local-tx-datasource>
</datasources>

这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。

2、在程序中引用数据源:

Java code
Connection conn=null; try { Context ctx=new InitialContext(); Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源 DataSource ds=(Datasource)datasourceRef; conn=ds.getConnection(); ...... c.close(); } catch(Exception e) { e.printStackTrace(); } finally { if(conn!=null) { try { conn.close(); } catch(SQLException e) { } } }

直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。

由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。

JNDI的扩展:
JNDI在满足了数据源配置的要求的基础上,还进一步扩充了作用:所有与系统外部的资源的引用,都可以通过JNDI定义和引用。

所以,在J2EE规范中,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。

EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。外部资源”。

总结:
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。

在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。

Server.MapPath的用法

2008年11月20日 2:22 下午  |  分类:J2EE

./当前目录
/根目录
../上层目录(相对当前来说)

如果当前的网站目录为D:\wwwroot

浏览的页面路径为D:\wwwroot\company\news\show.asp
在show.asp页面中使用
Server.MapPath("./")

返回路径为:D:\wwwroot\company\news
Server.MapPath("/")

返回路径为:D:\wwwroot
Server.MapPath("../")

返回路径为:D:\wwwroot\company

eclipse下加载svn

2008年10月25日 10:35 下午  |  分类:J2EE

eclipse下已经有自己非常好的版本仓库软件了cvs,我没用过CVS,SVN只是项目中跟着用了一下,没有比较,也就谈不上谁优谁劣。不过一篇博客里说,CVS很酷,但SVN更酷。从另一个方面讲,如果SVN不如CVS的话,也没有必要费那么多事开发这个插件。废话不多说,还是介绍一下怎样在eclipse下安装SVN插件吧。

其实归根结底,安装SVN就是安装插件。无非就是在线安装、覆盖拷贝安装和link安装三种方式。覆盖拷贝安装是一种最笨的方法,其缺点是在删除插件时你就等着哭吧(哭也没用)。在线安装、卸载都很方便,但是其最大缺点是受到网络的限制。而且其也是一种变相的copy安装。link安装方式是现在安装插件最流行的一种方式,其好处是我们可以单独建一个文件夹,把所有插件都在其中建一个单独的文件夹,然后在eclipse根目录下建一个links文件夹,里面为每个插件建一个link文件,设置内容为“path=插件文件夹路径”,就万事ok了,这样卸载插件就只需删除link文件。网上关于link安装方式介绍的很详细,这里就不再赘述。不过要补充一点,我看网上大部分介绍是删除插件时直接删除link文件,其有个弊端是如果需要再次使用此插件,就得再建一次link文件,有点啥。。。。。其实我们大可不必如此傻,只需在我们的links文件夹下新建一个“无用link”文件夹,这样我们要删除一个link,只需将其移到“无用link”文件夹中,当需要再次使用时就移出来就可以了,如图:

废话不多说,下面介绍一下怎样安装svn插件:

1、下载subversion插件压缩包,在这个网站上很全http://subclipse.tigris.org/servlets/ProjectDocumentList?folderID=2240,不过大家要对应自己的eclipse版本号。

2、下载完成后,解压缩得到包,按照link方式安装插件(过程不在这里赘述)。

3、在eclipse中使用subversion插件.ps:我们装的只是客户端,需要一个服务器给你分配一个账号、密码;(什吗?没服务器,那你装它干嘛。。。)

4、安装完毕后,打开eclipse,在透视图中我们就会发现SVN资源研究库了,如图所示:

下面我们就可以利用已经分配份额账号和密码在eclipse中连接SVN服务器了,方法如下:

1、新建–>项目–>SVN–>从SVN中检出项目,进入新建选择位置对话框,如图,如果已经有服务器路径,直接选择,进行下一步,否则,点击创建新的资源库位置,输入url进行创建,

2、然后按照提示进行操作,选择完要下载的页面,进入从SVN窗口,如图;选择新项目检出方式,会提示你一步步建立一个新项目,完全是建一个新项目。选择作为工作空间中的项目检出,会在你的eclipse的workplace中建一个文件夹,拷贝过去。

2、然后点击完成,就是等进度了。。。。。

3、这样大功就已告成。在右侧的包管理其中就有你的新建的那个项目。对于管理,可以通过右击项目,选择“小组”,进行项目的提交、与资源库同步、更新、合并等所有的SVN操作。(SVN操作在这里就不做过多赘述)。

这就是eclipse下安装subversion插件的几点心得,有误人子弟之处,欢迎大家指正。

Pages: 1 2 Next