| 志岩's profile我的技术空间BlogListsNetwork | Help |
我的技术空间用心去体验生活、用心去体验世界! 4/2/2007 JSP连接mysql数据库一. 软件下载 JDBC驱动 Mysql界面插件:mysql-front 二. 软件安装 http://info.mysql.cn/install/2006/0208/81.html 2.JDBC驱动:mysql-connector-java-3.1.8 3. Mysql界面插件:mysql-front 三. 环境配置 在这里,需要配置环境变量的是JDBC驱动.在配置前先要把刚才说到的mysql-connector-java-3.1.8-bin.jar本地硬盘某处(我放的地方:D:\Program Files\Java\mysqlforjdbc),然后根据你放的地方,配置classpath,我的配置是这样的: D:\Program files\Java\j2sdk\lib\tools.jar; D:\Program Files\Java\j2sdk\lib\mysql-connector-java-3.1.8-bin-g.jar; D:\Program Files\Java\mysqlforjdbc\mysql-connector-java-3.1.8-bin.jar 配置这个的目的是让你的java应用程序找到连接mysql的驱动. 配置完环境变量后还有很重要一步就是为JSP连接数据库配置驱动,这个其实很简单,就是把mysql-connector-java-3.1.8- bin.jar拷到某些文件夹里就行了,我在网上看了很多资料问了很多人,各种说法都有,我综合了一下,为了保险,我都全做了,呵呵,反正就是拷一个 400K的文件而已,现列出要把mysql-connector-java-3.1.8-bin.jar拷进去的文件夹,如下: 四. 数据库的使用 Mysql安装完毕以后,还有一些要注意的地方(参考): http://info.mysql.cn/install/2006/0208/82.html 就象在文章提到的,mysql安装好后最重要一样就是要看数据库有没有作为系统服务启动了,所以在大家进行数据库操作前,应要看看,在操作系统的开始->运行->输入services.msc,确定你在安装时你设置的关于mysql的那个服务已经启动,这样你在操作数据库时不会报连接不上的错误. 上面提到了一个较方便的mysql界面插件,但是这个界面是我在已经开始使用mysql后才找到的,刚开始我是在dos下用命令行进行操作的.虽然那个界面也可以进行建库啊,设定权限等操作,但是,我觉得懂得在使用命令行也是一个很重要的技能,所以我先从命令行开始说,怎样简单使用mysql.到后面会谈及mysql-front的使用. 现在我想在mysql里建一个数据库shujuku,以及在数据库里建一个表biao.具体的命令如下(假设mysql我是刚安装好的) 1. 进入dos状态(记住命令行的要运行在mysql的安装目录下的bin目录的) 2. 连接mysql 3. 使用mysql的基本命令(在mysql命令行编辑每输入完命令后最后一定要有分号,不然会报错) 4.建库 5.为数据库设置权限(用户和密码) 6.建表 剩下来的与标准sqsl命令基本上是一样的,具体操作略 呵呵,那样,我们还可以知道退出,就是"exit",呵呵! 五. 关于mysql-front的使用 首先,安装就不用说了,有向导,而且很简单.安装好后第一次运行时会跳出来一个要求添加对话的框,在这里你可以添加例如上面已经设定好的shujuku,过程如下: 要说明的是,你还可以把root用户也加进去,这要你在mysql-fron的界面上选设置->对话->新建,再按上面进行就可以,出了root你还可以加入更多的用户,方法还是一样的,设置不同的用户,是方便对不同数据库进行管理,呵呵,root是权限最高的,可不要随便让别人使用你的root用户,保正你数据库的安全. 六. JSP连接mysql <%@ page contentType="text/html; charset=gb2312" %> <%@ page language="java" %> <%@ page import="com.mysql.jdbc.Driver" %> <%@ page import="java.sql.*" %> <% //驱动程序名 String driverName="com.mysql.jdbc.Driver"; //数据库用户名 String userName="cl41"; //密码 String userPasswd="123456"; //数据库名 String dbName="db"; //表名 String tableName="dbtest"; //联结字符串 String url="jdbc:mysql://localhost/"+dbName+"?user="+userName+"&password="+userPasswd; Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection connection=DriverManager.getConnection(url); Statement statement = connection.createStatement(); String sql="SELECT * FROM "+tableName; ResultSet rs = statement.executeQuery(sql); //获得数据结果集合 ResultSetMetaData rmeta = rs.getMetaData(); //确定数据集的列数,亦字段数 int numColumns=rmeta.getColumnCount(); // 输出每一个数据值 out.print("id"); out.print("|"); out.print("num"); out.print("<br>"); while(rs.next()) { out.print(rs.getString(1)+" "); out.print("|"); out.print(rs.getString(2)); out.print("<br>"); } out.print("<br>"); out.print("数据库操作成功,恭喜你"); rs.close(); statement.close(); connection.close(); %> 然后把test??_mysql.jsp部署到tomcat处,如何部署可参考"配置Eclpise+tomcat并实现JSP的编写与部署",在浏览器中就可以看到结果了。 如何学习Spring1、如何学习Spring? 你可以通过下列途径学习spring: (1) spring下载包中doc目录下的MVC-step-by-step和sample目录下的例子都是比较好的spring开发的例子。 (2) AppFuse集成了目前最流行的几个开源轻量级框架或者工具Ant,XDoclet,Spring,Hibernate(iBATIS),JUnit,Cactus,StrutsTestCase,Canoo's WebTest,Struts Menu,Display Tag Library,OSCache,JSTL,Struts 。 你可以通过AppFuse源代码来学习spring。 AppFuse网站:http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse (3)Spring 开发指南(夏昕)(http://www.xiaxin.net/Spring_Dev_Guide.rar) 一本spring的入门书籍,里面介绍了反转控制和依赖注射的概念,以及spring的bean管理,spring的MVC,spring和hibernte,iBatis的结合。 (4) spring学习的中文论坛 SpringFramework中文论坛(http://spring.jactiongroup.net) Java视线论坛(http://forum.javaeye.com)的spring栏目 2、利用Spring框架编程,console打印出log4j:WARN Please initialize the log4j system properly? 说明你的log4j.properties没有配置。请把log4j.properties放到工程的classpath中,eclipse的classpath为bin目录,由于编译后src目录下的文件会拷贝到bin目录下,所以你可以把log4j.properties放到src目录下。 这里给出一个log4j.properties的例子: log4j.rootLogger=DEBUG,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %5p (%F:%L) - %m%n 3、出现 java.lang.NoClassDefFoundError? 一般情况下是由于你没有把必要的jar包放到lib中。 比如你要采用spring和hibernate(带事务支持的话),你除了spring.jar外还需要hibernat.jar、aopalliance.jar、cglig.jar、jakarta-commons下的几个jar包。 http://www.springframework.org/download.html下载spring开发包,提供两种zip包 spring-framework-1.1.3-with-dependencies.zip和spring-framework-1.1.3.zip,我建议你下载spring-framework-1.1.3-with-dependencies.zip。这个zip解压缩后比后者多一个lib目录,其中有hibernate、j2ee、dom4j、aopalliance、jakarta-commons等常用包。 4、java.io.FileNotFoundException: Could not open class path resource [....hbm.xml],提示找不到xml文件? 原因一般有两个: (1)该xml文件没有在classpath中。 (2)applicationContext-hibernate.xml中的xml名字没有带包名。比如: <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource"><ref bean="dataSource"/></property> <property name="mappingResources"> <list> <value>User.hbm.xml</value> 错,改为: <value>com/yz/spring/domain/User.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect"> net.sf.hibernate.dialect.MySQLDialect </prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean> 5、org.springframework.beans.NotWritablePropertyException: Invalid property 'postDao' of bean class? 出现异常的原因是在application-xxx.xml中property name的错误。 <property name="...."> 中name的名字是与bean的set方法相关的,而且要注意大小写。 比如 public class PostManageImpl extends BaseManage implements PostManage { private PostDAO dao = null; public void setPostDAO(PostDAO postDAO){ this.dao = postDAO; } } 那么xml的定义应该是: <bean id="postManage" parent="txProxyTemplate"> <property name="target"> <bean class="com.yz.spring.service.implement.PostManageImpl"> <property name="postDAO"><ref bean="postDAO"/></property> 对 <property name="dao"><ref bean="postDAO"/></property> 错 </bean> </property> </bean> 6、Spring中如何实现事务管理? 首先,如果使用mysql,确定mysql为InnoDB类型。 事务管理的控制应该放到商业逻辑层。你可以写个处理商业逻辑的JavaBean,在该JavaBean中调用DAO,然后把该Bean的方法纳入spring的事务管理。 比如:xml文件定义如下: <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="transactionAttributes"> <props> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="remove*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="userManage" parent="txProxyTemplate"> <property name="target"> <bean class="com.yz.spring.service.implement.UserManageImpl"> <property name="userDAO"><ref bean="userDAO"/></property> </bean> </property> </bean> com.yz.spring.service.implement.UserManageImpl就是我们的实现商业逻辑的JavaBean。我们通过parent元素声明其事务支持。 7、如何管理Spring框架下更多的JavaBean? JavaBean越多,spring配置文件就越大,这样不易维护。为了使配置清晰,我们可以将JavaBean分类管理,放在不同的配置文件中。 应用启动时将所有的xml同时加载。 比如: DAO层的JavaBean放到applicationContext-hibernate.xml中,商业逻辑层的JavaBean放到applicationContext-service.xml中。然后启动类中调用以下代码载入所有的ApplicationContext。 String[] paths = { "com/yz/spring/dao/hibernate/applicationContext-hibernate.xml", "com/yz/spring/service/applicationContext-service.xml"}; ctx = new ClassPathXmlApplicationContext(paths); 8、web应用中如何加载ApplicationContext? 可以通过定义web.xml,由web容器自动加载。 <servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext-hibernate.xml</param-value> <param-value>/WEB-INF/applicationContext-service.xml</param-value> </context-param> 9、在spring中如何配置的log4j? 在web.xml中加入以下代码即可。 <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/log4j.properties</param-value> </context-param> 10、Spring框架入门的编程问题解决了,我该如何更深地领会Spring框架呢? 这两本书你该去看看。这两本书是由Spring的作者Rod Johnson编写的。 Expert One on one J2EE Design and Development Expert One on one J2EE Development Without EJB 你也该看看martinfowler的Inversion of Control Containers and the Dependency Injection pattern。 http://www.martinfowler.com/articles/injection.html 再好好研读一下spring的文档。 http://www.jactiongroup.net/reference/html/index.html(中文版,未全部翻译) 将阿拉伯数字转化成大写的人民币数字将阿拉伯数字转化成大写的人民币数字 public String getNumberToRMB(String m){ String num = "零壹贰叁肆伍陆柒捌玖"; String dw = "圆拾佰仟万亿"; String mm[] = null; mm = m.split("\."); String money = mm[0];![]() String result = num.charAt(Integer.parseInt("" + mm[1].charAt(0))) + "角" + num.charAt(Integer.parseInt("" + mm[1].charAt(1))) + "分";![]() for (int i = 0; i < money.length(); i++) { String str = ""; int n = Integer.parseInt(money.substring(money.length() - i - 1, money.length() - i)); str = str + num.charAt(n); if (i == 0) { str = str + dw.charAt(i); } else if ( (i + 4) % 8 == 0) { str = str + dw.charAt(4); } else if (i % 8 == 0) { str = str + dw.charAt(5); } else { str = str + dw.charAt(i % 4); } result = str + result; } result = result.replaceAll("零([^亿万圆角分])", "零"); result = result.replaceAll("亿零+万","亿零"); result = result.replaceAll("零+", "零"); result = result.replaceAll("零([亿万圆])", ""); result =result.replaceAll("壹拾","拾"); return result; }编 程 之 道编 程 之 道 第五篇 维护 关于23种设计模式的有趣见解一、创建型模式 1、FACTORY 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 2、BUILDER 建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 3、FACTORY METHOD 工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 4、PROTOTYPE 原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 5、SINGLETON 单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。 二、结构型模式 6、ADAPTER 适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 7、BRIDGE 桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。 8、COMPOSITE 合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。 9、DECORATOR 装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 10、FACADE 门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。
11、FLYWEIGHT 享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。
12、PROXY 代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
三、行为模式
13、CHAIN OF RESPONSIBLEITY 责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。
14、COMMAND 命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。
15、INTERPRETER 解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。
16、ITERATOR Mary:“想要我跟你结婚,得答应我的条件”
我:“什么条件我都答应,你说吧”
Mary:“我看上了那个一克拉的钻石”
我:“我买,我买,还有吗?”
Mary:“我看上了湖边的那栋别墅”
我:“我买,我买,还有吗?”
Mary:“你的小弟弟必须要有50cm长”
我脑袋嗡的一声,坐在椅子上,一咬牙:“我剪,我剪,还有吗?”
……
迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。
17、MEDIATOR 调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。
18、MEMENTO 备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
19、OBSERVER 观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
20、STATE 状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
21、STRATEGY 策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
22、TEMPLATE METHOD 模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
23、VISITOR 访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。 ANT打包编译部署工具1.安装apache-ant-1.5.4或其他版本 2.配置环境变量: ANT_HOME=ant安装目录 PATH=%PATH%;%ANT_HOME%\bin 3.build.xml <!--Ant(another neat tool---另一个整洁的工具)-->![]() <?xml version="1.0"?> <project default="dist" name="Project A"> <description> 一段描述信息,没有实际作用。 </description> <!--property 元素相当于变量,存放一定的值,一旦给定以后不能改变,只能通过控制台命令行给初始值。 一般用 value 给其赋值,这里用 location 代替 value 是因为 location 属性专门设 计用于以平台无关的方式包含文件系统路径,也就是当前路径。--> <property name="srcDir" location="."/> <property name="buildDir" location="classes"/> <property name="distDir" location="dist"/> <!--一个target 元素就是一个任务,一个步骤,如果命令行没有指定开始任务,则由 project 元素的 default 属性决定要执行的任务--> <target name="init" description="在元素中指定描述信息!"> <echo message="这里的信息将被输出到控制台!"> <!--tstamp 元素一般不定义属性和内容,不产生任何输出;相反,它根据当前系统时间和日期设置 Ant 以下属性: 属性 说明 例子 DSTAMP 设置为当前日期,默认格式为yyyymmdd 20031217 TSTAMP 设置为当前时间,默认格式为hhmm 1603 TODAY 设置为当前日期,带完整的月份 2003 年12 月17 日--> <tstamp/> <!--mkdir 元素用来创建目录 dir 属性用来指定目录路径--> <!--delete 元素用来删除目录 ${ buildDir} 引用 property 元素指定的 name 为 buildDir 的属性变量--> <mkdir dir="${ buildDir}"/> <mkdir dir="${ distDir}"/> <!--文件操作: <copy file="src/Test.java" tofile="src/TestCopy.java"/> 把文件 Test.java 拷贝一份 TestCopy.java <move file="src/Test.java" tofile="src/TestCopy.java"/> 把文件 Test.java 重新命名为 TestCopy.java <copy file="src/Test.java" todir="archive"/> 把文件 Test.java 从 src 目录复制一份到 archive 目录下 <move file="src/Test.java" todir="archive"/> 把文件 Test.java 从 src 目录下移动到 archive 目录下 <replace file="input.txt" token="old" value="new"/>replace 任务,它执行文件中的查找和替换操作。 token 属性指定要查找的字符串, value 属性指定一个新的字符串, 查找到的标记字符串的所有实例都被替换为这个新的字符串. 替换操作将在文件本身之内的适当位置进行。为了提供更详细的输出,可把 summary 属性设置为 true。这将导致该任务输出找到和替换的标记字符串实例的数目。 <copy todir="archive">---使用模式匹配复制多个文件 <fileset dir="src">--fileset 代替 file 属性 <include name="*.java"/>--包含 src 目录下所有的 *.java 文件 <exclude name="*.class"/>--不包含 src 目录下所有的 *.class 文件 </fileset> </copy>--> </target>![]() <!--target 元素的 depends 属性用来设置多个 target 元素之间的依赖关系,如果要执行某个任务,必须先执行 depends 属性指定的所有 它所依赖的 target 元素,只有它所依赖的所有 target 元素都执行完了才能执行它自己--> <target name="com" depends="init"> <!--javac 元素把 srcdir 指定目录下所有 *.java 文件编译成 *.class 文件到 destdir 指定的目录下! 如果不指定 destdir 属性,那么默认编译到同一目录下。其他属性: classpath:等价于javac 的-classpath 选项。 debug="true":指示编译器应该带调试信息编译源文件。 fork="true":当你希望指定编译器的某些内存选项,或者需要使用一种不同级别的编译器的时候。该属性设置为“true” executable="d:sdk141injavac":指定一个不同的 javac 可执行文件。 memoryMaximumSize="128m":向上边指定的 javac 传递一个最大内存设置--> <javac srcdir="${ srcDir}" destdir="${ buildDir}"/> </target>![]() <target name="dist" depends="com"> <!--jar 元素:在编译 Java 源文件之后,结果类文件通常被打包到一个 JAR 文件中,这个文件类似 zip 归档文 件。每个 JAR 文件都包含一个清单文件,它可以指定该 JAR 文件的属性。 还可以把目录下的所有内容打包为 WAR 文件 destfile:打包后的文件全名 basedir:要打包的文件路径--> <jar destfile="${ distDir}/package-${ DSTAMP}.jar" basedir="${ buildDir}"> <!--manifest 属性允许指定一个用作该 JAR 文件的清单的文件。清单文件的内容还可以使用 manifest 任务在生成文件中指定。这个任务能够像文件系统写入一个清单文件,或者能够实际嵌套在 jar 之 内,以便一次性地创建清单文件和 JAR 文件。--> <manifest> <attribute name="Built-By" value="${ user.name}"/> <attribute name="Main-Class" value="package.Main"/> </manifest> </jar> <jar destfile="${ distDir}/package-src-${ DSTAMP}.jar" basedir="${ srcDir}"/> </target>![]() <target name="zip" depends="dist"> <!--zip 元素用来创建 zip 包,和 jar 元素相似--> <zip destfile="output.zip" basedir="output"/> <!--相同的语法也可用于创建 tar 文件。 还可以使用 GZip 和 BZip 任务来压缩文件。--> <tar destfile="output.tar" basedir="output"/> <gzip src="output.tar" zipfile="output.tar.gz"/> <!--解压缩和提取文件同样也很简单.--> <unzip src="output.tar.gz" dest="extractDir"/> <!--还可以包括 overwrite 属性来控制覆盖行为。默认设置是覆盖与正在被提取的归档文件中的条目相 匹配的所有现有文件。相关的任务名称是 untar、unjar、gunzip 和 bunzip2。--> </target>![]() <!--编写一个 clean目标来从目标目录移除生成的任何类文件是个很好的习惯。如果想要确 保所有源文件都已编译,就可以使用这个任务。这种行为刻画了 Ant 的许多任务的特点: 如果某个任务能够确定所请求的操作不需要执行,那么该操作就会被跳过。--> <target name="clean"> <delete dir="${ buildDir}"/> <delete dir="${ distDir}"/> </target> </project>网页常用小技巧1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键 <table border oncontextmenu=return(false)><td>no</table> 可用于Table 2. <body onselectstart="return false"> 取消选取、防止复制 3. onpaste="return false" 不准粘贴 4. oncopy="return false;" oncut="return false;" 防止复制 5. <link rel="Shortcut Icon" href="favicon.ico"> IE地址栏前换成自己的图标 6. <link rel="Bookmark" href="favicon.ico"> 可以在收藏夹中显示出你的图标 7. <input style="ime-mode:disabled"> 关闭输入法 8. 永远都会带着框架 <script language="javascript"><!-- if (window == top)top.location.href = "frames.htm"; //frames.htm为框架网页 // --></script> 9. 防止被人frame <script LANGUAGE=javascript><!-- if (top.location != self.location)top.location=self.location; // --></script> 10. 网页将不能被另存为 <noscript><iframe src=*.html></iframe></noscript> 11. <input type=button value=查看网页源代码 onclick="window.location = "view-source:"+ "http://www.gonet8.com""> 12.删除时确认 <a href="javascript:if(confirm("确实要删除吗?"))location="boos.asp?&areyou=删除&page=1"">删除</a> 13. 取得控件的绝对位置 //javascript <script language="javascript"> function getIE(e){ var t=e.offsetTop; var l=e.offsetLeft; while(e=e.offsetParent){ t+=e.offsetTop; l+=e.offsetLeft; } alter("top="+t+"/nleft="+l); } </script> //VBscript <script language="VBscript"><!-- function getIE() dim t,l,a,b set a=document.all.img1 t=document.all.img1.offsetTop l=document.all.img1.offsetLeft while a.tagName<>"BODY" set a = a.offsetParent t=t+a.offsetTop l=l+a.offsetLeft wend msgbox "top="&t&chr(13)&"left="&l,64,"得到控件的位置" end function --></script> 14. 光标是停在文本框文字的最后 <script language="javascript"> function cc() { var e = event.srcElement; var r =e.createTextRange(); r.moveStart("character",e.value.length); r.collapse(true); r.select(); } </script> <input type=text name=text1 value="123" onfocus="cc()"> 15. 判断上一页的来源 javascript: document.referrer 16. 最小化、最大化、关闭窗口 <object id=hh1 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11"> <param name="Command" value="Minimize"></object> <object id=hh2 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11"> <param name="Command" value="Maximize"></object> <OBJECT id=hh3 classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11"> <PARAM NAME="Command" VALUE="Close"></OBJECT> <input type=button value=最小化 onclick=hh1.Click()> <input type=button value=最大化 onclick=hh2.Click()> <input type=button value=关闭 onclick=hh3.Click()> 本例适用于IE 17.屏蔽功能键Shift,Alt,Ctrl <script> function look(){ if(event.shiftKey) alter("禁止按Shift键!"); //可以换成ALT CTRL } document.onkeydown=look; </script> 18. 网页不会被缓存 <META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> <META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT"> 或者<META HTTP-EQUIV="expires" CONTENT="0"> 19.怎样让表单没有凹凸感? <input type=text style="border:1 solid #000000"> 或 <input type=text style="border-left:none; border-right:none; border-top:none; border-bottom: 1 solid #000000"></textarea> 20.<div><span>&<layer>的区别? <div>(division)用来定义大段的页面元素,会产生转行 <span>用来定义同一行内的元素,跟<div>的唯一区别是不产生转行 <layer>是ns的标记,ie不支持,相当于<div> 21.让弹出窗口总是在最上面: <body onblur="this.focus();"> 22.不要滚动条? 让竖条没有: <body style="overflow:scroll;overflow-y:hidden"> </body> 让横条没有: <body style="overflow:scroll;overflow-x:hidden"> </body> 两个都去掉?更简单了 <body scroll="no"> </body> 23.怎样去掉图片链接点击后,图片周围的虚线? <a href="#" onFocus="this.blur()"><img src="logo.jpg" border=0></a> 24.电子邮件处理提交表单 <form name="form1" method="post" action="mailto:****@***.com" enctype="text/plain"> <input type=submit> </form> 25.在打开的子窗口刷新父窗口的代码里如何写? window.opener.location.reload() 26.如何设定打开页面的大小 <body onload="top.resizeTo(300,200);"> 打开页面的位置<body onload="top.moveBy(300,200);"> 27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动 <STYLE> body { background-image:url(logo.gif); background-repeat:no-repeat; background-position:center;background-attachment: fixed} </STYLE> 28. 检查一段字符串是否全由数字组成 <script language="javascript"><!-- function checkNum(str){ return str.match(//D/)==null} alter(checkNum("1232142141")) alter(checkNum("123214214a1")) // --></script> 29. 获得一个窗口的大小 document.body.clientWidth; document.body.clientHeight 30. 怎么判断是否是字符 if (/[^/x00-/xff]/g.test(s)) alter("含有汉字"); else alter("全是字符"); 31.TEXTAREA自适应文字行数的多少 <textarea rows=1 name=s1 cols=27 onpropertychange="this.style.posHeight=this.scrollHeight"> </textarea> 32. 日期减去天数等于第二个日期 <script language=javascript> function cc(dd,dadd) { //可以加上错误处理 var a = new Date(dd) a = a.valueOf() a = a - dadd * 24 * 60 * 60 * 1000 a = new Date(a) alter(a.getFullYear() + "年" + (a.getMonth() + 1) + "月" + a.getDate() + "日") } cc("12/23/2002",2) JSP生成图片验证码1。建立一个JSP页面(image.jsp),把下面的代码全部复制到image.jsp下。
<%@ pagecontentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %> <%! Color getRandColor(int fc,int bc){ //给定范围获得随机颜色 Random random = new Random(); if(fc>255) fc=255; if(bc>255) bc=255; int r=fc+random.nextInt(bc-fc); int g=fc+random.nextInt(bc-fc); int b=fc+random.nextInt(bc-fc); return new Color(r,g,b); } %> <% //设置页面不缓存 response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires", 0);![]() // 在内存中创建图象 int width=60, height=20; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);![]() // 获取图形上下文 Graphics g = image.getGraphics();![]() //生成随机类 Random random = new Random();![]() // 设定背景色 g.setColor(getRandColor(200,250)); g.fillRect(0, 0, width, height);![]() //设定字体 g.setFont(new Font("Comic Sans MS",Font.PLAIN,20));![]() //画边框 //g.setColor(new Color()); //g.drawRect(0,0,width-1,height-1);![]() ![]() // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到 g.setColor(getRandColor(160,200)); for (int i=0;i<155;i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x,y,x+xl,y+yl); }![]() // 取随机产生的认证码(4位数字) String sRand=""; for (int i=0;i<4;i++){ String rand=String.valueOf(random.nextInt(10)); sRand+=rand; // 将认证码显示到图象中 g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110))); //调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成 g.drawString(rand,13*i+6,16); }![]() // 将认证码存入SESSION session.setAttribute("rand",sRand);![]() // 图象生效 g.dispose();![]() // 输出图象到页面 ImageIO.write(image, "JPEG", response.getOutputStream()); %>![]() 2。然后在要使用的地方象引用图片一样引用此JSP文件: <img src="image.jsp">struts+hibernate项目debug总结javax.servlet.ServletException: Cannot retrieve mapping for action /companyNews struts-config.xml中没有写相关companyNews的action. ============================================ commit到cvs服务器。myeclipse有以下提示信息: 解决办法是,关闭myeclipse,用notepad打开项目目录下的 .classpath, 手动修改成相对路径,并且检查,指定的相对路径中是否真正添加了jar包,
原因是如果是jdk1.4的话,只能使用junit3.8以下版本,如果是使用jdk1.5的话,必须使用junit4.0以上版本。
Cannot find ActionMappings or ActionformBeans collection
org.apache.jasper.JasperException: The absolute uri: http://java.sun.com/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
<taglib> <taglib> <taglib> <taglib> <taglib> <taglib> <taglib>
Failed to load or instantiate TagLibraryValidator class: org.apache.taglibs.standard.tlv.JstlCoreTLV
创建oracle表的时候,使用了,role和comment关键字,建议不要将这两个关键字作为表名和字段名。 [ERROR] XMLHelper - Error parsing XML: XML InputStream(18) Attribute name "column" associated with an element type "key" must be followed by the ' = ' character. must be followed by the ' = ' character. Nested exception: Attribute name "column" associated with an element type "key" must be followed by the ' = ' character.>org.dom4j.DocumentException: Error on line 18 of document : Attribute name "column" associated with an element type "key" must be followed by the ' = ' character. Nested exception: Attribute name "column" associated with an element type "key" must be followed by the ' = ' character.
xml 语法错误,key语法中应该类似这样的写法 <key column="id"> ============================================================= <many to one >中的设置应该设置为not-null="false" ,设置为not-null="true"则报以上错误
在save的同时也需要save其他的表,然后再flush() Parse Fatal Error at line 12 column 1 struts-config.xml文件被修改,语法错误。检查语法。 ======================================== org.hibernate.QueryException: could not resolve property: userid of: com.xxx.yyy.Pay 使用到外键userid的时候,必须使用userinfo.userid方法才能得到。 ========================================= ========================================== [WARN] RequestProcessor - Unhandled Exception thrown: class java.lang.NullPointerException ============================================ =========================================== 要级联保存,多次session.save() session.load(Company.class,payForm.getCompanyId());
>org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.xxx.yyy.Company#0]
======================================================== =================================================== ==================================================== 是编码的问题!用ANSI编码另存后就好了。 ==================================================== [INFO] DefaultLoadEventListener - Error performing load command <org.hibernate.exception.SQLGrammarException: could not load an entity: [com.xxx.yyy.Sellinfo#1] >org.hibernate.exception.SQLGrammarException: could not load an entity: [com.xxx.yyy.Sellinfo#1] ***.hbm.xml文件中的某个列名和数据库中的不同。
======================================================= 原因:父亲对象(one方)设置cascade="save-update" 时,直接删除子对象时,会报错, Struts 应用转移到 Struts 2 一 有很多人都很熟悉 Struts, 无论是从项目中直接获得的实战经验还是从书中了解到的。我们这一系列文章,将通过一个由 Stuts 转移到 Struts2 简单的例子向大家展现Struts2的所有特征。
在我们开始这个例子之前,你需要去知道一点 Struts2的背景知识。 在第一部分的文章中,我们将介绍Struts2与Struts的核心框架的不同点,以助于更好地了解其他方面的整合。第二部分中,我们将深入探讨 actions 的差别, action相关的框架特征,和action配置。在最后一部分中,我们将会讲述 user interface,我们也会讲到其架构,UI构件,themes 和标签。 还有如何为你的应用加上新的外观。 我们并不打算谈及迁移过程的所有细节方面,我们只是从出发点开始介绍Struts2 的概念和现在可用的所有特征。但拥有这些知识,你将在以后Struts2的应用中无往而不利。 Struts的历史 Struts的第一个版本 是在 2001年5月份发布。它提供了一个Web应用的解决方案,如何让 JSPs 和 servlets 共存去提供清晰的分离视图和业务和应用逻辑的架构。在Struts之前,最通常的做法是在JSP中加入业务和应用逻辑,或者在servlets中生成视图。 自从第一个版本的发布, Struts 实际上已成为业界公认的Web应用标准。但随着时间的推移,Web应用框架经常变化的需求,产生了几个下一代 Struts的解决方案。其中两个可选方案是Shale 和 Struts Ti。 Shale 是一个基于构建的框架,并在最近成为 Apache 中的重要项目。而 Struts Ti 则是继续坚持 MVC模式的基础上改进,继续Struts的成功经验。 WebWork项目是在2002年3月发布的,它对Struts式框架进行了革命性改进,引进了不少新的思想,概念和功能,但和原Struts代码并不兼容。WebWork是一个成熟的框架,经过了好几次重大的改进与发布。在2005年12月,WebWork与Struts Ti决定合拼, 再此同时, Struts Ti 改名为 Struts Action Framework 2.0,成为Struts真正的下一代。 请求如何运作
在我们开始详细探讨如何转移Struts到Struts2之前,让我们来看看整个请求流程在新架构中是如何运作的。你会注意到在整个请求的生命周期,仍是以controller作主体,而且所有的概念还都是你以前所熟悉的, 就如: 通过URL请求的参数来调用Actions来把数据传给server. 所有的Servlet objects (request, response, session,之类.) 仍然可以在Action中获取 下图展示了整个请求的概要过程: 整个请求过程可以分为六步骤: 一个请求产生并经由框架处理 - 框架根据请求匹配相应的配置,如使用哪些拦截器,action 类和结果。 请求通过一系列的拦截器 - 拦截器,和拦截器组经配置后,能处理不同等级的请求,它们为请求提供了各种预处理,切面处理。这和Struts的使用 Jakarta Commons Chain 构件的 RequestProcessor类很相似。 调用 Action - 产生一个新的action对象实例,并提供请求所调用的处理逻辑的方法。Struts2 可以在配置action时为请求分配其指定的方法。我们在第二部文章中将对这步骤进行进一步讨论; 调用产生的结果 - 获取通过action的方法处理后返回来的结果,匹配其result class并调用产生的实例。有种情况是在UI模板去生成HTML时才去处理这些结果。如果在这种情况下,在Struts2 模板中的tags能直接返回到 action 中,取结果来呈现界面。 请求再次经过一系列的拦截器处理后返回 - 请求反顺序通过与原来进入时的拦截器链, 当然,你也可以配置在这个过程中减少或增加拦截器处理. 请求返回到用户 - 最后一步是由 control 返回到servlet。通常是生成HTML返回到user, 但你也可以指定返回的HTTP头或HTTP重定向。 你应该已注意到,Struts2与Struts的差别。最明显的就是Struts2是pull-MVC 架构,就是可以直接从Action中获取所需要的数据,而不是像Struts那样必须把 beans 存到page, request,或者session中才能获取。这个我们将在下一章中详细提及。 配置框架
首先最重要的是,让框架能通过web.xml在servlet containers里运行。 下面这个就是大家都熟悉的 Struts在 web.xml里的配置方法 <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping>
<servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> 在 Struts2 中,这个有少许改变,最明显的是dispatcher 由servlet转为servlet filter, 其配置和servlet一样简单,如下:
<filter> <filter-name>webwork</filter-name> <filter-class> org.apache.struts.action2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping>
<filter-name>webwork</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 和servlet配置一样,filter配置定义了名称(供关联)和filter的类。filter mapping让URI匹配成功的的请求调用该filter。默认情况下,扩展名为".action"。
这个是在default.properties文件里的"struts.action.extension" 属性定义的。 工具箱: "default.properties"是配置选项定义文件。通过在classpath中包含一个叫"struts.properties"的文件,并设置不同的属性值,你可以覆盖这个默认的配置,实现自己的配置。 对于Struts, servlet配置提供了初始化tag的参数和使用的文件,而Struts2没有这样的配置参数,取而代之的是在classpath下的默认配置文件"struts.xml"。 工具箱/提示: Struts actions(扩展名".do"),Struts2 actions(扩展名".action"),所以Struts和Struts2可以在一个系统中共存。所以最好是保持原先的系统,在新功能的开发上用Struts2, 如果时间和资源允许的情况下再逐步迁移。另一种方法是只是把Struts2的扩展名改为".do",可重用JSPs. 分析Actions 在上面介绍的请求运作流程中,我们谈及了一些Struts和Struts2的不同点。现在我们将较深入地探讨这两个框架中action结构的具体差别。 让我们来回顾一下 Struts 的 action 结构, 主要的形式如下: public class MyAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // do the work return (mapping.findForward("success")); } } 当实现一个Struts action时, 你需要注意一下问题:
所有的action 都必须继承于base Action 类. 所有的action都必须是线程安全的,因为action是单例的,singleton的. 因为所有的action都必须是线程安全的,所以所有的对象都不能是类属性, 都必须以方法参数的形式传值。 调用action的方法必须命名为 "execute" ( 在Struts中的 DispatchAction 类好像可以用其它方法去执行同一个action ,但实际上在框架中调用的仍然是 "execute" 方法。). ActionForward 的结果是通过ActionMapping 类中的方法来产生的,通常是"findForward"方法. 相比较之下, Struts2的action 提供了很多简单的实现。下面就是个例子 public class MyAction { public String execute() throws Exception { // do the work return "success"; } } 首先你会注意到的是,Struts2中的action不再继承于任何类或需要实现任何接口。实际上,它还远不只这些。按照惯例,只有"execute"方法能调用action, 但在Struts2中并非必要,任何声明为public String methodName() 方法都能通过配置来调用action.
另外,你会注意到返回值不再是"ActionForward ",而是String, 如果你需喜欢String的形式,那在Action接口里有个帮助方法可以提供简单的结果常量,如"success", "none", "error", "input" 和 "login"。 最后,和Struts最大的革命性的不同是, 调用action不再是带参数的。那你如何在获得你所需要的值呢?答案是"inversion of control" 或 "dependency injection", 反转控制(想了解更多可以看Martin Fowler的文章 http://www.martinfowler.com/articles/injection.html)。 为了更好地了解反转控制,让我们来看看一个例子,如何在action处理过程中可以访问到HttpServerRequest 。在我们的例子中,我们用ServletRequestAware 接口,这个接口包含了相应属性的setter,如下 public interface ServletRequestAware { public void setServletRequest(HttpServletRequest request); } 当我们继承这个接口时,我们需要通过setter为我们的HttpServerRequest 属性变量赋值:
public class MyAction implements ServletRequestAware { private HttpServletRequest request; public void setServletRequest(HttpServletRequest request) { this.request = request; } public String execute() throws Exception { // do the work using the request return Action.SUCCESS; } } 看起来现在这些属性是类级别的,并不是线程安全的,但是在Struts2里并没有问题,因为每个请求过来的时候都会产生一个新的action对象实例,它并没有和其他请求共享一个对象,所以不需要考虑线程安全问题。
现在我们还有最后一步,就是把action关联上ServletConfigInterceptor 拦截器。这个拦截器继承了ServletRequestAware 接口,并提供了把HttpServletRequest 注入到action中的功能。但是你现在不用担心如何配置这些,我们将在下一篇文章中具体讲述。最重要的是我们明白了拦截器和接口共同为action提供了反转控制的功能。
这个设计的好处是能让action完全和框架解耦。action仅仅是一个被框架使用的简单的POJO。这对于单元测试但来极大的好处, 你能方便的为Struts action实现 StrutsTestCase 或 MockStrutsTestCase 单元测试。 总结
By到现在为止,你应该已经了解了Struts2的整个请求流程,还有高层的框架概念, 你也应该能自己动手配置Struts2的action,和讲出Struts和Struts2的差别了。 在下篇文章中,我们将会介绍一个详细的Struts应用向Struts2迁移的例子,同时我们也会介绍迁移中相关的知识,会讲述如何综合使用JSTL, JSP 和 Struts2,进一步讲述Struts和Struts2的action的差别,Struts2的配置和其他框架元素,和谈到更多的其他相关框架的特征。 在这篇文章中,我们将会更详细地讲述如何由Struts 的action转为Struts 2的action。 一个应用的例子
这个例子选择了大家都熟悉的 - weblog. 简单地介绍下这例子的功能需求: 增加一个新的日志
察看一个日志 修改一个日志 删除一个日志 列出所有日至 增删修改(CRUD),是项目中最为普遍的应用。 业务逻辑类在Struts 和 Struts2 应用都是可共用的。如: public class BlogService { private static List<Blog> blogs = new ArrayList<Blog>(); public List<Blog> list() { ... } public Blog create(Blog blog) { ... } public void update(Blog blog) { ... } public void delete(int id){ ... } public Blog findById(int id) { ... } } BlogService 只是个简单的业务逻辑类,并不是接口,Struts 和 Struts2 的action皆可调用其实例。虽然这样设计在实际项目中会带来不必要的耦合,但我们的例子只是集中在讨论web层上,所以无关重要。
工具箱: 在第一篇文章中,我们谈论了在Struts2 actions中的依赖注入的接口注入方式。这个是servlet 相关类(HttpServletRequest, HttpServletResponse, PrincipalProxy, 等.)的主要注入方式,但这并不是唯一的方式。 Struts2 可以使用Spring框架作为默认的容器时,依赖注入的setter方法就可用了。通过在action中加入setter方法(如下演示), Struts2 框架将能从Spring框架中获得正确的信息,并通过setter加载在action中。 public void setBlogService(BlogService service) { this.blogService = service; } 和接口注入方式类似,我们需要一个拦截器来帮助我们完成任务,这就是 ActionAutowiringInterceptor 拦截器。这样我们的业务逻辑类就通过Spring框架管理自动在action被调用之前注入到Struts2得action中。有多种的配置参数(如by name, by type 或 automatically)可供选择,可以让对象和setter匹配的注入的方式根据你的需要而定。
Struts 应用中的代码 我们首先从Struts讲起。在Struts中,最普遍的做法是,对于每个需求用例(如save,update,remove,list)来说都会有对应的action类,同时也会有相应的action form类。在我们的应用中的这个方式或许不是最佳的实现方式(其他的解决方案包括使用dynamic form或者使用request来分发action),但我们例子中的做法是所有Struts开发者最熟悉的一种形式。了解了这种简单的实现方法,你有能力在迁移到Struts2时,使用更为优秀的方法。 在第一篇文章中我们谈过Struts 和 Struts2 中action的区别。现在我们从UML中再次看看他们的差别。一般来说form在Struts action中的表现形式是: 这action form将会在多个action中使用,让我们来看看它:
public class BlogForm extends ActionForm {
private String id;
private String title; private String entry; private String created; // public setters and getters for all properties
} 如UML中展示的那样,其中一个限制就是必须继承ActionForm类,另外一个限制就是form中所有属性都必须是String类型,所以所有的getter和setter都必须只能接受String参数和返回String结果。
然后我们来看看action。我们这个例子中的action有view, create 和 update action。
The View Action: The Create Action: public class ViewBlogAction extends Action { public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BlogService service = new BlogService();
String id = request.getParameter("id"); request.setAttribute("blog",service.findById(Integer.parseInt(id))); return (mapping.findForward("success"));
} } public class SaveBlogEntryAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BlogService service = new BlogService();
BlogForm blogForm = (BlogForm) form; Blog blog = new Blog(); BeanUtils.copyProperties( blog, blogForm ); service.create( blog );
return (mapping.findForward("success"));
} } The Update Action: public class UpdateBlogEntryAction extends Action { public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BlogService service = new BlogService();
BlogForm blogForm = (BlogForm) form; Blog blog = service.findById( Integer.parseInt(blogForm.getId()));
BeanUtils.copyProperties( blog, blogForm ); service.update( blog ); request.setAttribute("blog",blog); return (mapping.findForward("success"));
} } 这三个action都跟随着同一个模式: 产生一个新的业务逻辑对象实例 - 如前面所提到的,我们使用最直接的方式在action中使用业务逻辑对象,这表示在每个action中都会产生新的业务逻辑对象实例。
从请求中获得数据 - 这是两种形式之一。在view action中,"id"是从HttpServletRequest 对象中直接获取的。而在create 和 update action 中,则从ActionForm 中取值。ActionForm 与 HttpServletRequest 的调用方式其实很相似,唯一不同的ActionForm 是bean的从field中取值。 调用业务逻辑- 现在开始生成调用业务逻辑所需的参数并调用逻辑。如果参数(在view action中)是一个简单对象类型,则转换值时会自动转为正确的类型(如从String转到Integer)。如果参数是复杂的对象类型,,则ActionForm 需要通过BeanUtil 来帮忙转成相应的对象。 设定返回的数据 - 如果需要把数据返回显示给用户,那则要把这个数据设在HttpServletRequest 的attribute 中返回。 返回一个 ActionForward - 所有 Struts action的最后都需要找到并返回其相应的 ActionForward 对象. 最后的两个action,remove和list action, 只有很少的差别。remove action如下所示,没有用BlogForm类. 通过从request的attribute中获取"id"(和view action相似),就能调用业务逻辑完成其需要的工作。在下面我们介绍配置时,你可以看到它并没有返回任何数据,因为它的"success"返回结果其实是执行remove后再执行了list action来返回信息的。 public class RemoveBlogEntryAction extends Action { public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BlogService service = new BlogService();
String id = request.getParameter("id"); service.delete(Integer.parseInt(id)); return (mapping.findForward("success"));
} } list action并不需要任何的用户输入,它只是简单地调用了业务逻辑的无参方法,同时返回所有的Blog对象。 public class ListBlogsAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { BlogService service = new BlogService();
request.setAttribute("bloglist",service.list()); return (mapping.findForward("success")); } } Struts 应用转移到 Struts 2 二向 Struts2 迁移
提示: 当调用action时,如果发现不明原因使不能正确地通过setter注入值情况下,第一步最好是先检查下各拦截器,确保它们都已作用于该action。因为这些意外通常有时由拦截器设置不当形成的,检查是否各个拦截器都已起作用,并看看它们作用的顺序,因为有些情况下它们间会相互影响而产生错误。 现在我们已经有基于String类型的form bean中取值的方法或者是自动把request的attributes 注入到action的方法,那下一步就是如何把值传入 domain object 或 value / transfer object的属性中去。其实这很简单,你只需要实现ModelDriven 接口(即实现getModel()方法)就可以了,确保ModelDrivenInterceptor 拦截器已作用于action。 ![]() public class BlogAction extends ActionSupport implements ModelDriven, Preparable, ServletRequestAware { ![]() private int blogId; private Blog blog; private BlogService service = new BlogService(); private HttpServletRequest request;![]() public void setServletRequest(HttpServletRequest httpServletRequest){ this.request = httpServletRequest; }![]() public void setId(int blogId) { this.blogId = blogId; }![]() public void prepare() throws Exception { if( blogId==0 ) { blog = new Blog(); } else { blog = service.findById(blogId); } }![]() public Object getModel() { return blog; }![]() public String save() { service.create(blog); return SUCCESS; } public String update() { service.update(blog); request.setAttribute("blog",blog); return SUCCESS; }![]() public String remove() { service.delete(blogId); return SUCCESS; }![]() public String execute() { request.setAttribute("blog",blog); return SUCCESS; }![]() ![]() } ![]()
public class ListBlogsAction extends ActionSupport implements ServletRequestAware { ![]() private BlogService service = new BlogService(); private HttpServletRequest request;![]() public void setServletRequest(HttpServletRequest httpServletRequest) { this.request = httpServletRequest; }![]() public String execute() { request.setAttribute("bloglist",service.list()); return SUCCESS; }![]() }![]()
<struts-config>![]() <form-beans> <form-bean name="blogForm" type="com.fdar.articles.infoq.conversion.struts.BlogForm"/> </form-beans> ...![]() </struts-config> ![]()
<struts-config> ...![]() <action-mappings>![]() <action path="/struts/add" forward="/struts/add.jsp"/> ...![]() </action-mappings> </struts-config> ![]()
<struts> <include file="struts-default.xml"/>![]() <package name="struts2" extends="struts-default" namespace="/struts2">![]() <action name="add" > <result>/struts2/add.jsp</result> </action> ...![]() </package> </struts> ![]()
<struts-config> ...![]() <action-mappings>![]() <action path="/struts/list" scope="request" type="com.fdar.articles.infoq.conversion.struts.ListBlogsAction" > <forward name="success" path="/struts/list.jsp"/> </action> ...![]() </action-mappings> </struts-config>![]()
<struts> ...![]() <package name="struts2" extends="struts-default" namespace="/struts2">![]() <default-interceptor-ref name="defaultStack"/>![]() <action name="list" class="com.fdar.articles.infoq.conversion.struts2.ListBlogsAction"> <result>/struts2/list.jsp</result> <interceptor-ref name="basicStack"/> </action> ...![]() </package> </struts>![]()
<action name="update" method="update" class="com.fdar.articles.infoq.conversion.struts2.BlogAction" > ... </action>
<struts-config> ...![]() <action-mappings> <action path="/struts/save" type="com.fdar.articles.infoq.conversion.struts.SaveBlogEntryAction" name="blogForm" scope="request"> <forward name="success" redirect="true" path="/struts/list.do"/> </action> ...![]() </action-mappings> </struts-config> ![]()
<struts> ...![]() <package name="struts2" extends="struts-default" namespace="/struts2">![]() <action name="save" method="save" class="com.fdar.articles.infoq.conversion.struts2.BlogAction" > <result type="redirect">list.action</result> <interceptor-ref name="defaultStack"/> </action> ...![]() </package> </struts>
总结
也许到现在为,也许你有个疑问,"迁移后我们的界面是否可以完全重用呢?",答案是yes。你能从这里, 下载到我这篇文章中的完整源代码,你可以自己尝试把URL的扩展名由".do" 改为 ".action",使用的页面时一样的。除此之外,其实用JSTL来代替Struts taglib也是很容易的。 4/1/2007 Thinking:Java中static、this、super、final用法一、static
请先看下面这段程序: public class Hello{ 看过这段程序,对于大多数学过Java 的从来说,都不陌生。即使没有学过Java,而学过其它的高级语言,例如C,那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”,一点别的用处都没有,然而,它却展示了static关键字的主要用法。 在1处,我们定义了一个静态的方法名为main,这就意味着告诉Java编译器,我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗?一般,我们都是在命令行下,打入如下的命令(加下划线为手动输入): javac Hello.java 这就是你运行的过程,第一行用来编译Hello.java这个文件,执行完后,如果你查看当前,会发现多了一个Hello.class文件,那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。执行结果如你所料。在2中,你可能会想,为什么要这样才能输出。好,我们来分解一下这条语句。(如果没有安装Java文档,请到Sun的官方网站浏览J2SE API)首先,System是位于java.lang包中的一个核心类,如果你查看它的定义,你会发现有这样一行:public static final PrintStream out;接着在进一步,点击PrintStream这个超链接,在METHOD页面,你会看到大量定义的方法,查找println,会有这样一行: public void println(String x)。 好了,现在你应该明白为什么我们要那样调用了,out是System的一个静态变量,所以可以直接使用,而out所属的类有一个println方法。 静态方法 通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法。如下所示: class Simple{ 调用一个静态方法就是“类名.方法名”,静态方法的使用很简单如上所示。一般来说,静态方法常常为应用程序中的其它类提供一些实用工具所用,在Java的类库中大量的静态方法正是出于此目的而定义的。 静态变量 静态变量与静态方法类似。所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间,当然对于final则另当别论了。看下面这段代码: class Value{ 结果如下: v1.c=0 v2.c=0 由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序: class Value{ public static void main(String[] args){ 运行结果如下: v1.c=0 v2.c=0 这个程序展示了静态初始化的各种特性。如果你初次接触Java,结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是,static定义的变量会优先于任何其它非static变量,不论其出现的顺序如何。正如在程序中所表现的,虽然v出现在v1和v2的前面,但是结果却是v1和v2的初始化在v的前面。在static{ 后面跟着一段代码,这是用来进行显式的静态变量初始化,这段代码只会初始化一次,且在类被第一次装载时。如果你能读懂并理解这段代码,会帮助你对static关键字的认识。在涉及到继承的时候,会先初始化父类的static变量,然后是子类的,依次类推。非静态变量不是本文的主题,在此不做详细讨论,请参考Think in Java中的讲解。 静态类 通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。如下代码所示: public class StaticCls{ 输出结果会如你所料: InnerCls 和普通类一样。内部类的其它用法请参阅Think in Java中的相关章节,此处不作详解。 二、this & super 在上一篇拙作中,我们讨论了static的种种用法,通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的。好了,现在开始讨论this&super这两个关键字的意义和用法。 在Java中,this通常指当前对象,super则指父类的。当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当然,this的另一个用途是调用当前对象的另一个构造函数,这些马上就要讨论。如果你想引用父类的某种东西,则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系,所以我们在这一块儿来讨论,希望能帮助你区分和掌握它们两个。 在一般方法中 最普遍的情况就是,在你的方法中的某个形参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便需要明确使用this关键字来指明你要使用某个成员,使用方法是“this.成员名”,而不带this的那个便是形参。另外,还可以用“this.方法名”来引用当前对象的某个方法,但这时this就不是必须的了,你可以直接用方法名来访问那个方法,编译器会知道你要调用的是那一个。下面的代码演示了上面的用法: public class DemoThis{ 这段代码很简单,不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它,两者效果一样。下面我们修改这个程序,来演示super的用法。 class Person{ 在DemoSuper中,重新定义的print方法覆写了父类的print方法,它首先做一些自己的事情,然后调用父类的那个被覆写了的方法。输出结果说明了这一点: DemoSuper: 这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问,那你可以像使用this一样使用它,用“super.父类中的成员名”的方式,但常常你并不是这样来访问父类中的成员名的。 在构造函数中 构造函数是一种特殊的方法,在对象初始化的时候自动调用。在构造函数中,this和super也有上面说的种种使用方式,并且它还有特殊的地方,请看下面的例子: class Person{ 在这段程序中,this和super不再是像以前那样用“.”连接一个方法或成员,而是直接在其后跟上适当的参数,因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数,如1和2处。this后加参数则调用的是当前具有相同参数的构造函数,如3处。当然,在Chinese的各个重载构造函数中,this和super在一般方法中的各种用法也仍可使用,比如4处,你可以将它替换为“this.prt”(因为它继承了父类中的那个方法)或者是“super.prt”(因为它是父类中的方法且可被子类访问),它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。 最后,写了这么多,如果你能对“this通常指代当前对象,super通常指代父类”这句话牢记在心,那么本篇便达到了目的,其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承,请参阅相关Java教程。 三、final final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。 final成员 当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。其初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。下面这段代码演示了这一点: import java.util.List; 此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法,这使你有了一点灵活性。如Bat的两个重载构造函数所示,第一个缺省构造函数会为你提供默认的值,重载的那个构造函数会根据你所提供的值或类型为final变量初始化。然而有时你并不需要这种灵活性,你只需要在定义时便给定其值并永不变化,这时就不要再用这种方法。在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型,输出结果中显示了这一点: I=100 List Type:class java.util.LinkedList 还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。 public class INClass{ final方法 将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。 final类 当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。 下面的程序演示了final方法和final类的用法: final class final{ 从程序中可以看出,final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。 final在设计模式中的应用 在设计模式中有一种模式叫做不变模式,在Java中通过final关键字可以很容易的实现这个模式,在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。如果你对此感兴趣,可以参考阎宏博士编写的《Java与模式》一书中的讲解。 到此为止,this,static,supert和final的使用已经说完了,如果你对这四个关键字已经能够大致说出它们的区别与用法,那便说明你基本已经掌握。然而,世界上的任何东西都不是完美无缺的,Java提供这四个关键字,给程序员的编程带来了很大的便利,但并不是说要让你到处使用,一旦达到滥用的程序,便适得其反,所以在使用时请一定要认真考虑。
请先看下面这段程序: public class Hello{ 看过这段程序,对于大多数学过Java 的从来说,都不陌生。即使没有学过Java,而学过其它的高级语言,例如C,那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”,一点别的用处都没有,然而,它却展示了static关键字的主要用法。 在1处,我们定义了一个静态的方法名为main,这就意味着告诉Java编译器,我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗?一般,我们都是在命令行下,打入如下的命令(加下划线为手动输入): javac Hello.java 这就是你运行的过程,第一行用来编译Hello.java这个文件,执行完后,如果你查看当前,会发现多了一个Hello.class文件,那就是第一行产生的Java二进制字节码。第二行就是执行一个Java程序的最普遍做法。执行结果如你所料。在2中,你可能会想,为什么要这样才能输出。好,我们来分解一下这条语句。(如果没有安装Java文档,请到Sun的官方网站浏览J2SE API)首先,System是位于java.lang包中的一个核心类,如果你查看它的定义,你会发现有这样一行:public static final PrintStream out;接着在进一步,点击PrintStream这个超链接,在METHOD页面,你会看到大量定义的方法,查找println,会有这样一行: public void println(String x)。 好了,现在你应该明白为什么我们要那样调用了,out是System的一个静态变量,所以可以直接使用,而out所属的类有一个println方法。 静态方法 通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法。如下所示: class Simple{ 调用一个静态方法就是“类名.方法名”,静态方法的使用很简单如上所示。一般来说,静态方法常常为应用程序中的其它类提供一些实用工具所用,在Java的类库中大量的静态方法正是出于此目的而定义的。 静态变量 静态变量与静态方法类似。所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间,当然对于final则另当别论了。看下面这段代码: class Value{ 结果如下: v1.c=0 v2.c=0 由此可以证明它们共享一块存储区。static变量有点类似于C中的全局变量的概念。值得探讨的是静态变量的初始化问题。我们修改上面的程序: class Value{ public static void main(String[] args){ 运行结果如下: v1.c=0 v2.c=0 这个程序展示了静态初始化的各种特性。如果你初次接触Java,结果可能令你吃惊。可能会对static后加大括号感到困惑。首先要告诉你的是,static定义的变量会优先于任何其它非static变量,不论其出现的顺序如何。正如在程序中所表现的,虽然v出现在v1和v2的前面,但是结果却是v1和v2的初始化在v的前面。在static{ 后面跟着一段代码,这是用来进行显式的静态变量初始化,这段代码只会初始化一次,且在类被第一次装载时。如果你能读懂并理解这段代码,会帮助你对static关键字的认识。在涉及到继承的时候,会先初始化父类的static变量,然后是子类的,依次类推。非静态变量不是本文的主题,在此不做详细讨论,请参考Think in Java中的讲解。 静态类 通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。如下代码所示: public class StaticCls{ 输出结果会如你所料: InnerCls 和普通类一样。内部类的其它用法请参阅Think in Java中的相关章节,此处不作详解。 二、this & super 在上一篇拙作中,我们讨论了static的种种用法,通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的。好了,现在开始讨论this&super这两个关键字的意义和用法。 在Java中,this通常指当前对象,super则指父类的。当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当然,this的另一个用途是调用当前对象的另一个构造函数,这些马上就要讨论。如果你想引用父类的某种东西,则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系,所以我们在这一块儿来讨论,希望能帮助你区分和掌握它们两个。 在一般方法中 最普遍的情况就是,在你的方法中的某个形参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便需要明确使用this关键字来指明你要使用某个成员,使用方法是“this.成员名”,而不带this的那个便是形参。另外,还可以用“this.方法名”来引用当前对象的某个方法,但这时this就不是必须的了,你可以直接用方法名来访问那个方法,编译器会知道你要调用的是那一个。下面的代码演示了上面的用法: public class DemoThis{ 这段代码很简单,不用解释你也应该能看明白。在构造函数中你看到用this.print(),你完全可以用print()来代替它,两者效果一样。下面我们修改这个程序,来演示super的用法。 class Person{ 在DemoSuper中,重新定义的print方法覆写了父类的print方法,它首先做一些自己的事情,然后调用父类的那个被覆写了的方法。输出结果说明了这一点: DemoSuper: 这样的使用方法是比较常用的。另外如果父类的成员可以被子类访问,那你可以像使用this一样使用它,用“super.父类中的成员名”的方式,但常常你并不是这样来访问父类中的成员名的。 在构造函数中 构造函数是一种特殊的方法,在对象初始化的时候自动调用。在构造函数中,this和super也有上面说的种种使用方式,并且它还有特殊的地方,请看下面的例子: class Person{ 在这段程序中,this和super不再是像以前那样用“.”连接一个方法或成员,而是直接在其后跟上适当的参数,因此它的意义也就有了变化。super后加参数的是用来调用父类中具有相同形式的构造函数,如1和2处。this后加参数则调用的是当前具有相同参数的构造函数,如3处。当然,在Chinese的各个重载构造函数中,this和super在一般方法中的各种用法也仍可使用,比如4处,你可以将它替换为“this.prt”(因为它继承了父类中的那个方法)或者是“super.prt”(因为它是父类中的方法且可被子类访问),它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。 最后,写了这么多,如果你能对“this通常指代当前对象,super通常指代父类”这句话牢记在心,那么本篇便达到了目的,其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承,请参阅相关Java教程。 三、final final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。 final成员 当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。其初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。下面这段代码演示了这一点: import java.util.List; 此程序很简单的演示了final的常规用法。在这里使用在构造函数中进行初始化的方法,这使你有了一点灵活性。如Bat的两个重载构造函数所示,第一个缺省构造函数会为你提供默认的值,重载的那个构造函数会根据你所提供的值或类型为final变量初始化。然而有时你并不需要这种灵活性,你只需要在定义时便给定其值并永不变化,这时就不要再用这种方法。在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型,输出结果中显示了这一点: I=100 List Type:class java.util.LinkedList 还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。 public class INClass{ final方法 将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。 final类 当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。 下面的程序演示了final方法和final类的用法: final class final{ 从程序中可以看出,final类与普通类的使用几乎没有差别,只是它失去了被继承的特性。final方法与非final方法的区别也很难从程序行看出,只是记住慎用。 final在设计模式中的应用 在设计模式中有一种模式叫做不变模式,在Java中通过final关键字可以很容易的实现这个模式,在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。如果你对此感兴趣,可以参考阎宏博士编写的《Java与模式》一书中的讲解。 到此为止,this,static,supert和final的使用已经说完了,如果你对这四个关键字已经能够大致说出它们的区别与用法,那便说明你基本已经掌握。然而,世界上的任何东西都不是完美无缺的,Java提供这四个关键字,给程序员的编程带来了很大的便利,但并不是说要让你到处使用,一旦达到滥用的程序,便适得其反,所以在使用时请一定要认真考虑。 JAVA学习之路:不走弯路,就是捷径 0.引言 在ChinaITLAB导师制辅导中,笔者发现问得最多的问题莫过于"如何学习编程?JAVA该如何学习?"。类似的问题回答多了,难免会感觉厌烦,就萌生了写下本文的想法。到时候再有人问起类似的问题,我可以告诉他(她),请你去看看《JAVA学习之路》。拜读过台湾蔡学镛先生的《JAVA夜未眠》,有些文章如《JAVA学习之道》等让我们确实有共鸣,本文题目也由此而来。 软件开发之路是充满荆棘与挑战之路,也是充满希望之路。JAVA学习也是如此,没有捷径可走。梦想像《天龙八部》中虚竹一样被无崖子醍醐灌顶而轻松获得一甲子功力,是很不现实的。每天仰天大叫"天神啊,请赐给我一本葵花宝典吧",殊不知即使你获得了葵花宝典,除了受自宫其身之苦外,你也不一定成得了"东方不败",倒是成"西方失败"的几率高一点。 "不走弯路,就是捷径",佛经说的不无道理。 1.如何学习程序设计? JAVA是一种平台,也是一种程序设计语言,如何学好程序设计不仅仅适用于JAVA,对C++等其他程序设计语言也一样管用。有编程高手认为,JAVA也好C也好没什么分别,拿来就用。为什么他们能达到如此境界?我想是因为编程语言之间有共通之处,领会了编程的精髓,自然能够做到一通百通。如何学习程序设计理所当然也有许多共通的地方。 1.1 培养兴趣 兴趣是能够让你坚持下去的动力。如果只是把写程序作为谋生的手段的话,你会活的很累,也太对不起自己了。多关心一些行业趣事,多想想盖茨。不是提倡天天做白日梦,但人要是没有了梦想,你觉得有味道吗?可能像许多深圳本地农民一样,打打麻将,喝喝功夫茶,拜拜财神爷;每个月就有几万十几万甚至更多的进帐,凭空多出个"食利阶层"。你认为,这样有味道吗?有空多到一些程序员论坛转转,你会发现,他们其实很乐观幽默,时不时会冒出智慧的火花。 1.2 慎选程序设计语言 男怕入错行,女怕嫁错郎。初学者选择程序设计语言需要谨慎对待。软件开发不仅仅是掌握一门编程语言了事,它还需要其他很多方面的背景知识。软件开发也不仅仅局限于某几个领域,而是已经渗透到了各行各业几乎每一个角落。 如果你对硬件比较感兴趣,你可以学习C语言/汇编语言,进入硬件开发领域。如果你对电信的行业知识及网络比较熟悉,你可以在C/C++等之上多花时间,以期进入电信软件开发领域。如果你对操作系统比较熟悉,你可以学习C/Linux等等,为Linux内核开发/驱动程序开发/嵌入式开发打基础。如果你想介入到应用范围最广泛的应用软件开发(包括电子商务电子政务系统)的话,你可以选择J2EE或.NET,甚至LAMP组合。每个领域要求的背景知识不一样。做应用软件需要对数据库等很熟悉。总之,你需要根据自己的特点来选择合适你的编程语言。 1.3 要脚踏实地,快餐式的学习不可取 先分享一个故事。 有一个小朋友,他很喜欢研究生物学,很想知道那些蝴蝶如何从蛹壳里出来,变成蝴蝶便会飞。 有一次,他走到草原上面看见一个蛹,便取了回家,然后看着,过了几天以后,这个蛹出了一条裂痕,看见里面的蝴蝶开始挣扎,想抓破蛹壳飞出来。 这个过程达数小时之久,蝴蝶在蛹里面很辛苦地拼命挣扎,怎么也没法子走出来。这个小孩看着看着不忍心,就想不如让我帮帮它吧,便随手拿起剪刀在蛹上剪开,使蝴蝶破蛹而出。 但蝴蝶出来以后,因为翅膀不够力,变得很臃肿,飞不起来。 这个故事给我们的启示是:欲速则不达。 浮躁是现代人最普遍的心态,能怪谁?也许是贫穷落后了这么多年的缘故,就像当年的大跃进一样,都想大步跨入共产主义社会。现在的软件公司、客户、政府、学校、培训机构等等到处弥漫着浮躁之气。就拿笔者比较熟悉的深圳IT培训行业来说吧,居然有的打广告宣称"参加培训,100%就业",居然报名的学生不少,简直是藐视天下程序员。社会环境如是,我们不能改变,只能改变自己,闹市中的安宁,弥足珍贵。许多初学者C++/JAVA没开始学,立马使用VC/JBuilder,会使用VC/JBuilder开发一个Hello World程序,就忙不迭的向世界宣告,"我会软件开发了",简历上也大言不惭地写上"精通VC/JAVA"。结果到软件公司面试时要么被三两下打发走了,要么被驳的体无完肤,无地自容。到处碰壁之后才知道捧起《C++编程思想》《JAVA编程思想》仔细钻研,早知如此何必当初呀。 "你现在讲究简单方便,你以后的路就长了",好象也是佛经中的劝戒。 1.4 多实践,快实践 彭端淑的《为学一首示子侄》中有穷和尚与富和尚的故事。 从前,四川边境有两个和尚,一个贫穷,一个有钱。一天,穷和尚对富和尚说:"我打算去南海朝圣,你看怎么样?"富和尚说:"这里离南海有几千里远,你靠什么去呢?"穷和尚说:"我只要一个水钵,一个饭碗就够了。"富和尚为难地说:"几年前我就打算买条船去南海,可至今没去成,你还是别去吧!" 一年以后,富和尚还在为租赁船只筹钱,穷和尚却已经从南海朝圣回来了。 这个故事可解读为:任何事情,一旦考虑好了,就要马上上路,不要等到准备周全之后,再去干事情。假如事情准备考虑周全了再上路的话,别人恐怕捷足先登了。软件开发是一门工程学科,注重的就是实践,"君子动口不动手"对软件开发人员来讲根本就是错误的,他们提倡"动手至上",但别害怕,他们大多温文尔雅,没有暴力倾向,虽然有时候蓬头垢面的一副"比尔盖茨"样。有前辈高人认为,学习编程的秘诀是:编程、编程、再编程,笔者深表赞同。不仅要多实践,而且要快实践。我们在看书的时候,不要等到你完全理解了才动手敲代码,而是应该在看书的同时敲代码,程序运行的各种情况可以让你更快更牢固的掌握知识点。 1.5 多参考程序代码 程序代码是软件开发最重要的成果之一,其中渗透了程序员的思想与灵魂。许多人被《仙剑奇侠传》中凄美的爱情故事感动,悲剧的结局更有一种缺憾美。为什么要以悲剧结尾?据说是因为写《仙剑奇侠传》的程序员失恋而安排了这样的结局,他把自己的感觉融入到游戏中,却让众多的仙剑迷扼腕叹息。 多多参考代码例子,对JAVA而言有参考文献[4.3],有API类的源代码(JDK安装目录下的src.zip文件),也可以研究一些开源的软件或框架。 1.6 加强英文阅读能力 对学习编程来说,不要求英语, 但不能一点不会,。最起码像JAVA API文档(参考文献[4.4])这些东西还是要能看懂的,连猜带懵都可以;旁边再开启一个"金山词霸"。看多了就会越来越熟练。在学JAVA的同时学习英文,一箭双雕多好。另外好多软件需要到英文网站下载,你要能够找到它们,这些是最基本的要求。英语好对你学习有很大的帮助。口语好的话更有机会进入管理层,进而可以成为剥削程序员的"周扒皮"。 1.7 万不得已才请教别人 笔者在ChinaITLab网校的在线辅导系统中解决学生问题时发现,大部分的问题学生稍做思考就可以解决。请教别人之前,你应该先回答如下几个问题。 你是否在google中搜索了问题的解决办法? 你是否查看了JAVA API文档? 你是否查找过相关书籍? 你是否写代码测试过? 如果回答都是"是"的话,而且还没有找到解决办法,再问别人不迟。要知道独立思考的能力对你很重要。要知道程序员的时间是很宝贵的。 1.8 多读好书 书中自有颜如玉。比尔·盖茨是一个饱读群书的人。虽然没有读完大学,但九岁的时候比尔·盖茨就已经读完了所有的百科全书,所以他精通天文、历史、地理等等各类学科,可以说比尔·盖茨不仅是当今世界上金钱的首富,而且也可以称得上是知识的巨富。 笔者在给学生上课的时候经常会给他们推荐书籍,到后来学生实在忍无可忍开始抱怨,"天呐,这么多书到什么时候才能看完了","学软件开发,感觉上了贼船"。这时候,我的回答一般是,"别着急,什么时候带你们去看看我的书房,到现在每月花在技术书籍上的钱400元,这在软件开发人员之中还只能够算是中等的",学生当场晕倒。(注:这一部分学生是刚学软件开发的) 对于在JAVA开发领域的好书在笔者另外一篇文章中会专门点评。该文章可作为本文的姊妹篇。 1.9 使用合适的工具 工欲善其事必先利其器。软件开发包含各种各样的活动,需求收集分析、建立用例模型、建立分析设计模型、编程实现、调试程序、自动化测试、持续集成等等,没有工具帮忙可以说是寸步难行。工具可以提高开发效率,使软件的质量更高BUG更少。组合称手的武器。到飞花摘叶皆可伤人的境界就很高了,无招胜有招,手中无剑心中有剑这样的境界几乎不可企及。在笔者另外一篇文章中会专门阐述如何选择合适的工具(该文章也可作为本文的姊妹篇)。
当前Java软件开发中几种认识误区越来越多人开始使用Java,但是他们大多数人没有做好足够的思想准备(没有接受OO思想体系相关培训),以致不能很好驾驭Java项目,甚至 导致开发后的Java系统性能缓慢甚至经常当机。很多人觉得这是Java复杂导致,其实根本原因在于:我们原先掌握的关于软件知识(OO方面)不是太贫乏就是不恰当,存在认识上和方法上的误区。 软件的生命性 软件是有生命的,这可能是老调重弹了,但是因为它事关分层架构的原由,反复强调都不过分。 一个有生命的软件首先必须有一个灵活可扩展的基础架构,其次才是完整的功能。 目前很多人对软件的思想还是焦点落在后者:完整的功能,觉得一个软件功能越完整越好,其实关键还是架构的灵活性,就是前者,基础架构好,功能添加只是时间和工作量问题,但是如果架构不好,功能再完整,也不可能包括未来所有功能,软件是有生命的,在未来成长时,更多功能需要加入,但是因为基础架构不灵活不能方便加入,死路一条。 那么选择现在一些流行的框架如Hibernate、Spring/Jdonframework是否就表示基础架构打好了呢?其实还不尽然,关键还是取决于你如何使用这些框架来搭建你的业务系统。 存储过程和复杂SQL语句的陷阱 首先谈谈存储过程使用的误区,使用存储过程架构的人以为可以解决性能问题,其实它正是导致性能问题的罪魁祸首之一,打个比喻:如果一个人频临死亡,打一针可以让其延长半年,但是打了这针,其他所有医疗方案就全部失效,请问你会使用这种短视方案吗? 为什么这样说呢?如果存储过程都封装了业务过程,那么运行负载都集中在数据库端,要中间J2EE应用服务器干什么?要中间服务器的分布式计算和集群能力做什么?只能回到过去集中式数据库主机时代。现在软件都是面向互联网的,不象过去那样局限在一个小局域网,多用户并发访问量都是无法确定和衡量,依靠一台数据库主机显然是不能够承受这样恶劣的用户访问环境的。(当然搞数据库集群也只是五十步和百步的区别)。 从分层角度来看,现在三层架构:表现层、业务层和持久层,三个层次应该分割明显,职责分明:持久层职责持久化保存业务模型对象,业务层对持久层的调用只是帮助我们激活曾经委托其保管的对象,所以,不能因为持久层是保管者,我们就以其为核心围绕其编程,除了要求其归还模型对象外,还要求其做其做复杂的业务组合。打个比喻:你在火车站将水果和盘子两个对象委托保管处保管,过了两天来取时,你还要求保管处将水果去皮切成块,放在盘子里,做成水果盘给你,合理吗? 上面是谈过分依赖持久层的一个现象,还有一个正好相反现象,持久层散发出来,开始挤占业务层,腐蚀业务层,整个业务层到处看见的是数据表的影子(包括数据表的字段),而不是业务对象。这样程序员应该多看看OO经典PoEAA。PoEAA 认为除了持久层,不应该在其他地方看到数据表或表字段名。 当然适量使用存储过程,使用数据库优点也是允许的。按照Evans DDD理论,可以将SQL语句和存储过程作为规则Specification一部分。 Hibernate等ORM问题 “最近本人正搞一个项目,项目中我们用到了struts1.2+hibernate3, 由于关系复杂表和表之间的关系很多,在很多地方把lazy都设置false,所以导致数据一加载很慢,而且查询一条数据更是非常的慢。” Hibernate是一个基于对象模型持久化的技术,因此,关键是我们需要设计出高质量的对象模型,遵循DDD领域建模原则,减少降低关联,通过分层等有效办法处理关联。如果采取围绕数据表进行设计编程,加上表之间关系复杂(没有科学方法处理、侦察或减少这些关系),必然导致 系统运行缓慢,其实同样问题也适用于当初对EJB的实体Bean的CMP抱怨上,实体Bean是Domain Model持久化,如果不首先设计Domain Model,而是设计数据表,和持久化工具设计目标背道而驰,能不出问题吗?关于这个问题N多年就在Jdon争论过。 这里同样延伸出另外一个问题:数据库设计问题,数据库是否需要在项目开始设计? 当然,也有脑力相当发达的人可以 实现,但是这种围绕数据库实现映射的结果必然扭曲业务对象,这类似于两个板块(数据表和业务对象)相撞,必然产生地震,地震的结果是两败俱伤, 软的一方吃亏,业务对象是代码,相当于数据表结构,属于软的一方,最后导致业务对象变成数据传输对象DTO, DTO满天飞,性能和维护问题随之而来。 领域建模解决了上述众多不协调问题,特别是ORM痛苦使用问题,关于ORM/Hibernate使用还是那句老话:如果你不掌握领域建模方法,那么就不要用Hibernate,对于这个层次的你:也许No ORM 更是一个简单之道: No ORM: The simplest solution Spring分层矛盾问题 具体举例如下:当我们实现购物车之类业务功能时,需要将购物场合保存到Session中,由于业务层没有方便的Session支持,我们只得将购物车保存到 HttpSession,而HttpSession只有通过HttpRequest才能获得,再因为在Spring业务层容器中是无法访问到HttpRequest这个对象的,所以, 最后我们只能将“购物车保存到HttpSession”这个功能放在表现层中实现,而这个功能明显应该属于业务层功能,这就导致我们的Java项目层次混乱,维护性差。 违背了使用Spring和分层架构最初目的。 相关案例:请教一个在完整提交前临时保存的问题: 领域驱动设计DDD 我们现在很多人知道Java项目基本有三层:表现层 业务层和持久层,当我们执著于讨论各层框架如何选择之时,实际上我们真正的项目开发工作还没有开始, 就是我们选定了某种框架的组合(如Struts+Spring+Hibernate或Struts+EJB或Struts+JdonFramework),我们还没有意识到业务层工作还需要大量工作,DDD提供了在业务层中再划分新的层次思想,如领域层和服务层,甚至再细分为作业层、能力层、策略层等等。通过层次细化方式达到复杂软件的松耦合。DDD提供了如何细分层次的方式 当我们将精力花费在架构技术层面的讨论和研究上时,我们可能忘记以何种依据选择这些架构技术?选择标准是什么?领域驱动设计DDD 回答了这样的问题,DDD会告诉你如果一个框架不能协助你实现分层架构,那就抛弃它,同时,DDD也指出选择框架的考虑目的,使得你不会 人云亦云,陷入复杂的技术细节迷雾中,迷失了架构选择的根本方向。 现在也有些人误以为DDD是一种新的理论,其实DDD和设计模式一样,不是一种新的理论,而是实战经验的总结,它将前人 使用面向模型设计的方法经验提炼出来,供后来者学习,以便迅速找到驾驭我们软件项目的根本之道。 现在Evans DDD概念很火,因为它将著名的PoEAA进行了具化,实现了PoEAA可操作性,这也是MF大力推崇的原因。最近(8月8日)一位老外博客上用微软的.NET架构和Evans DDD比较的文章:http://weblogs.asp.net/pgielens/archive/2006/08/08/Organizing-Domain-Logic.aspx,这篇文章比较了微软的三层服务应用架构[Microsoft TLSA]和Evans DDD的架构, 使用Microsoft .NET Pet Shop 4为例子,解释两个目标的区别,并且表明 另外一本关于.NET的DDD书籍也已经出版,这些都说明Evans DDD这把火已经烧到.NET领域,当然DDD在Java领域生根开花多年,Evans的DDD书籍就是以Java为例子的,笔者板桥里人也率先在2005年推出DDD框架JdonFramework 1.3版本,这些都说明,Java在整个软件业先进思想的实践上总是领先一步。 Unicode,GBK,GB2312,UTF-8概念基础为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问题,特别是乱码问题,我觉得组成一个系列来描述和分析更好一些,包括三篇文章:
第一篇:JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础
第二篇:JAVA字符编码系列二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换
第三篇:JAVA字符编码系列三:Java应用中的编码问题 第一篇:JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础
本部分采用重用,转载一篇文章来完成这部分的目标。
来源:holen'blog 对字符编码与Unicode,ISO 10646,UCS,UTF8,UTF16,GBK,GB2312的理解
地址:http://blog.donews.com/holen/archive/2004/11/30/188182.aspx
Unicode:
unicode.org制定的编码机制, 要将全世界常用文字都函括进去. 在1.0中是16位编码, 由U+0000到U+FFFF. 每个2byte码对应一个字符; 在2.0开始抛弃了16位限制, 原来的16位作为基本位平面, 另外增加了16个位平面, 相当于20位编码, 编码范围0到0x10FFFF. UCS: ISO制定的ISO10646标准所定义的 Universal Character Set, 采用4byte编码. Unicode与UCS的关系: ISO与unicode.org是两个不同的组织, 因此最初制定了不同的标准; 但自从unicode2.0开始, unicode采用了与ISO 10646-1相同的字库和字码, ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值, 使得两者保持一致. UCS的编码方式: UTF: Unicode/UCS Transformation Format * 与CPU字节顺序无关, 可以在不同平台之间交流 * 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码) UTF-16是unicode的preferred encoding. UTF与unicode的关系: Unicode是一个字符集, 可以看作为内码. 而UTF是一种编码方式, 它的出现是因为unicode不适宜在某些场合直接传输和处理. UTF-16直接就是unicode编码, 没有变换, 但它包含了0x00在编码内, 头256字节码的第一个byte都是0x00, 在操作系统(C语言)中有特殊意义, 会引起问题. 采用UTF-8编码对unicode的直接编码作些变换可以避免这问题, 并带来一些优点. 中国国标编码: GB 18030是中国所有非手持/嵌入式计算机系统的强制实施标准.
什么是 UCS 和 ISO 10646?国际标准 ISO 10646 定义了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集标准的一个超集. 它保证与其他字符集是双向兼容的. 就是说, 如果你将任何文本字符串翻译到 UCS格式, 然后再翻译回原编码, 你不会丢失任何信息. UCS 包含了用于表达所有已知语言的字符. 不仅包括拉丁语,希腊语, 斯拉夫语,希伯来语,阿拉伯语,亚美尼亚语和乔治亚语的描述, 还包括中文, 日文和韩文这样的象形文字, 以及 平假名, 片假名, 孟加拉语, 旁遮普语果鲁穆奇字符(Gurmukhi), 泰米尔语, 印.埃纳德语(Kannada), Malayalam, 泰国语, 老挝语, 汉语拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他数也数不清的语. 对于还没有加入的语言, 由于正在研究怎样在计算机中最好地编码它们, 因而最终它们都将被加入. 这些语言包括 Tibetian, 高棉语, Runic(古代北欧文字), 埃塞俄比亚语, 其他象形文字, 以及各种各样的印-欧语系的语言, 还包括挑选出来的艺术语言比如 Tengwar, Cirth 和 克林贡语(Klingon). UCS 还包括大量的图形的, 印刷用的, 数学用的和科学用的符号, 包括所有由 TeX, Postscript, MS-DOS,MS-Windows, Macintosh, OCR 字体, 以及许多其他字处理和出版系统提供的字符. ISO 10646 定义了一个 31 位的字符集. 然而, 在这巨大的编码空间中, 迄今为止只分配了前 65534 个码位 (0x0000 到 0xFFFD). 这个 UCS 的 16位子集称为 基本多语言面 (Basic Multilingual Plane, BMP). 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字), 且只有专家在历史和科学领域里才会用到它们. 按当前的计划, 将来也许再也不会有字符被分配到从 0x000000 到 0x10FFFF 这个覆盖了超过 100 万个潜在的未来字符的 21 位的编码空间以外去了. ISO 10646-1 标准第一次发表于 1993 年, 定义了字符集与 BMP 中内容的架构. 定义 BMP 以外的字符编码的第二部分 ISO 10646-2 正在准备中, 但也许要过好几年才能完成. 新的字符仍源源不断地加入到 BMP 中, 但已经存在的字符是稳定的且不会再改变了. UCS 不仅给每个字符分配一个代码, 而且赋予了一个正式的名字. 表示一个 UCS 或 Unicode 值的十六进制数, 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大写字母A". UCS 字符 U+0000 到 U+007F 与 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 与 ISO 8859-1(Latin-1) 也是一致的. 从 U+E000 到 U+F8FF, 已经 BMP 以外的大范围的编码是为私用保留的. 什么是组合字符?UCS里有些编码点分配给了 组合字符.它们类似于打字机上的无间隔重音键. 单个的组合字符不是一个完整的字符. 它是一个类似于重音符或其他指示标记, 加在前一个字符后面. 因而, 重音符可以加在任何字符后面. 那些最重要的被加重的字符, 就象普通语言的正字法(orthographies of common languages)里用到的那种, 在 UCS 里都有自己的位置, 以确保同老的字符集的向后兼容性. 既有自己的编码位置, 又可以表示为一个普通字符跟随一个组合字符的被加重字符, 被称为 预作字符(precomposed characters). UCS 里的预作字符是为了同没有预作字符的旧编码, 比如 ISO 8859, 保持向后兼容性而设的. 组合字符机制允许在任何字符后加上重音符或其他指示标记, 这在科学符号中特别有用, 比如数学方程式和国际音标字母, 可能会需要在一个基本字符后组合上一个或多个指示标记. 组合字符跟随着被修饰的字符. 比如, 德语中的元音变音字符 ("拉丁大写字母A 加上分音符"), 既可以表示为 UCS 码 U+00C4 的预作字符, 也可以表示成一个普通 "拉丁大写字母A" 跟着一个"组合分音符":U+0041 U+0308 这样的组合. 当需要堆叠多个重音符, 或在一个基本字符的上面和下面都要加上组合标记时, 可以使用多个组合字符. 比如在泰国文中, 一个基本字符最多可加上两个组合字符. 什么是 UCS 实现级别?不是所有的系统都需要支持象组合字符这样的 UCS 里所有的先进机制. 因此 ISO 10646 指定了下列三种实现级别:
什么是 Unicode?历史上, 有两个独立的, 创立单一字符集的尝试. 一个是国际标准化组织(ISO)的 ISO 10646 项目, 另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 Unicode 项目. 幸运的是, 1991年前后, 两个项目的参与者都认识到, 世界不需要两个不同的单一字符集. 它们合并双方的工作成果, 并为创立一个单一编码表而协同工作. 两个项目仍都存在并独立地公布各自的标准, 但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 标准的码表兼容, 并紧密地共同调整任何未来的扩展. 那么 Unicode 和 ISO 10646 不同在什么地方?Unicode 协会公布的 Unicode 标准 严密地包含了 ISO 10646-1 实现级别3的基本多语言面. 在两个标准里所有的字符都在相同的位置并且有相同的名字. Unicode 标准额外定义了许多与字符有关的语义符号学, 一般而言是对于实现高质量的印刷出版系统的更好的参考. Unicode 详细说明了绘制某些语言(比如阿拉伯语)表达形式的算法, 处理双向文字(比如拉丁与希伯来文混合文字)的算法和 排序与字符串比较 所需的算法, 以及其他许多东西. 另一方面, ISO 10646 标准, 就象广为人知的 ISO 8859 标准一样, 只不过是一个简单的字符集表. 它指定了一些与标准有关的术语, 定义了一些编码的别名, 并包括了规范说明, 指定了怎样使用 UCS 连接其他 ISO 标准的实现, 比如 ISO 6429 和 ISO 2022. 还有一些与 ISO 紧密相关的, 比如 ISO 14651 是关于 UCS 字符串排序的. 考虑到 Unicode 标准有一个易记的名字, 且在任何好的书店里的 Addison-Wesley 里有, 只花费 ISO 版本的一小部分, 且包括更多的辅助信息, 因而它成为使用广泛得多的参考也就不足为奇了. 然而, 一般认为, 用于打印 ISO 10646-1 标准的字体在某些方面的质量要高于用于打印 Unicode 2.0的. 专业字体设计者总是被建议说要两个标准都实现, 但一些提供的样例字形有显著的区别. ISO 10646-1 标准同样使用四种不同的风格变体来显示表意文字如中文, 日文和韩文 (CJK), 而 Unicode 2.0 的表里只有中文的变体. 这导致了普遍的认为 Unicode 对日本用户来说是不可接收的传说, 尽管是错误的. 什么是 UTF-8?首先 UCS 和 Unicode 只是分配整数给字符的编码表. 现在存在好几种将一串字符表示为一串字节的方法. 最显而易见的两种方法是将 Unicode 文本存储为 2 个 或 4 个字节序列的串. 这两种方法的正式名称分别为 UCS-2 和 UCS-4. 除非另外指定, 否则大多数的字节都是这样的(Bigendian convention). 将一个 ASCII 或 Latin-1 的文件转换成 UCS-2 只需简单地在每个 ASCII 字节前插入 0x00. 如果要转换成 UCS-4, 则必须在每个 ASCII 字节前插入三个 0x00. 在 Unix 下使用 UCS-2 (或 UCS-4) 会导致非常严重的问题. 用这些编码的字符串会包含一些特殊的字符, 比如 '' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义. 另外, 大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的. 基于这些原因, 在文件名, 文本文件, 环境变量等地方, UCS-2 不适合作为 Unicode 的外部编码. 在 ISO 10646-1 Annex R 和 RFC 2279 里定义的 UTF-8 编码没有这些问题. 它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法. UTF-8 有一下特性:
下列字节串用来表示一个字符. 用到哪个串取决于该字符在 Unicode 中的序号.
xxx 的位置由字符编码数的二进制表示的位填入. 越靠右的 x 具有越少的特殊意义. 只用最短的那个足够表达一个字符编码数的多字节串. 注意在多字节串中, 第一个字节的开头"1"的数目就是整个串中字节的数目. 例如: Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为:
而字符 U+2260 = 0010 0010 0110 0000 (不等于) 编码为:
这种编码的官方名字拼写为 UTF-8, 其中 UTF 代表 UCS Transformation Format. 请勿在任何文档中用其他名字 (比如 utf8 或 UTF_8) 来表示 UTF-8, 当然除非你指的是一个变量名而不是这种编码本身. 什么编程语言支持 Unicode?在大约 1993 年之后开发的大多数现代编程语言都有一个特别的数据类型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char. ISO C 也详细说明了处理多字节编码和宽字符 (wide characters) 的机制, 1994 年 9 月 Amendment 1 to ISO C 发表时又加入了更多. 这些机制主要是为各类东亚编码而设计的, 它们比处理 UCS 所需的要健壮得多. UTF-8 是 ISO C 标准调用多字节字符串的编码的一个例子, wchar_t 类型可以用来存放 Unicode 字符. Unicode,ISO-8859,GBK,UTF-8编码及相互转换为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问题,特别是乱码问题,我觉得组成一个系列来描述和分析更好一些,包括三篇文章:
第一篇:JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础
第二篇:JAVA字符编码系列二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换
第三篇:JAVA字符编码系列三:Java应用中的编码问题 第二篇:JAVA字符编码系列二:Unicode,ISO-8859-1,GBK,UTF-8编码及相互转换
1、函数介绍
在Java中,字符串用统一的Unicode编码,每个字符占用两个字节,与编码有关的两个主要函数为:
1)将字符串用指定的编码集合解析成字节数组,完成Unicode-〉charsetName转换
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException
2)将字节数组以指定的编码集合构造成字符串,完成charsetName-〉Unicode转换
public String(byte[] bytes, String charsetName) throws UnsupportedEncodingException
2、Unicode与各编码之间的直接转换
下面以对中文字符串"a中文"的编码转换为例,来了解各种编码之间的转换
1)Unicode和GBK
测试结果如下,每个汉字转换为两个字节,且是可逆的,即通过字节可以转换回字符串
String-GBK〉ByteArray:\u0061\u4E2D\u6587(a中文)-〉0x61 0xD6 0xD0 0xCE 0xC4
ByteArray-GBK〉String:0x61 0xD6 0xD0 0xCE 0xC4-〉\u0061\u4E2D\u6587(a中文)
2)Unicode和UTF-8
测试结果如下,每个汉字转换为三个字节,且是可逆的,即通过字节可以转换回字符串
String-UTF-8〉ByteArray:\u0061\u4E2D\u6587(a中文)-〉0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87
ByteArray-UTF-8〉String:0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87-〉\u0061\u4E2D\u6587(a中文)
3)Unicode和ISO-8859-1
测试结果如下,当存在汉字时转换失败,非可逆,即通过字节不能再转换回字符串
String-ISO-8859-1〉ByteArray:\u0061\u4E2D\u6587(a中文)-〉0x61 0x3F 0x3F
ByteArray-ISO-8859-1〉String:0x61 0x3F 0x3F-〉\u0061\u003F\u003F(a??)
3、Unicode与各编码之间的交叉转换
在上面直接转换中,由字符串(Unicode)生成的字节数组,在构造回字符串时,使用的是正确的编码集合,如果使用的不是正确的编码集合会怎样呢?会正确构造吗?如果不能正确构造能有办法恢复吗?会信息丢失吗?
下面我们就来看看这种情况,这部分可以说明在某些情况下虽然我们最终正确显示了结果,但其间仍然进行了不正确的转换。
1)能够正确显示的中间不正确转换
我们知道String-GBK〉ByteArray-GBK〉String是正确的,但如果我们采用String-GBK〉ByteArray-ISO-8859-1〉String呢?通过测试结果如下:
String-GBK〉ByteArray-ISO-8859-1〉String:\u0061\u4E2D\u6587(a中文)-〉0x61 0xD6 0xD0 0xCE 0xC4-〉\u0061\u00D6\u00D0\u00CE\u00C4(a????)
这时我们得到的字符串为?乱码“a????”,但是通过继续转换我们仍然可以复原回正确的字符串“a中文”,过程如下:
String-GBK〉ByteArray-ISO-8859-1〉String-ISO-8859-1〉ByteArray-GBK〉String
对应:\u0061\u4E2D\u6587(a中文)-〉0x61 0xD6 0xD0 0xCE 0xC4-〉\u0061\u00D6\u00D0\u00CE\u00C4(a????)-〉0x61 0xD6 0xD0 0xCE 0xC4-〉\u0061\u4E2D\u6587(a中文)
也就是我们在首次构造字符串时,我们用了错误的编码集合得到了错误的乱码,但是我们通过错上加错,再用错误的编码集合获取字节数组,然后再用正确的编码集合构造,就又恢复了正确的字符串。这时就属于是“能够正确显示的中间不正确转换”。在Jsp页面提交数据处理时常常发生这种情况。
此外能够正确显示的中间不正确转换还有:
String-UTF-8〉ByteArray-ISO-8859-1〉String-ISO-8859-1〉ByteArray-UTF-8〉String
和
String-UTF-8〉ByteArray-GBK〉String-GBK〉ByteArray-UTF-8〉String
对应:\u0061\u4E2D\u6587(a中文)-〉0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87-〉\u0061\u6D93\uE15F\u6783(a涓枃)-〉0x61 0xE4 0xB8 0xAD 0xE6%0x96 0x87-〉\u0061\u4E2D\u6587(a中文)
4、编码过程中错误诊断参考
1)一个汉字对应一个问号
在通过ISO-8859-1从字符串获取字节数组时,由于一个Unicode转换成一个byte,当遇到不认识的Unicode时,转换为0x3F,这样无论用哪种编码构造时都会产生一个?乱码。
2)一个汉字对应两个问号
在通过GBK从字符串获取字节数组时,由于一个Unicode转换成两个byte,如果此时用ISO-8859-1或用UTF-8构造字符串就会出现两个问号。
若是通过ISO-8859-1构造可以再通过上面所说的错上加错恢复(即再通过从ISO-8859-1解析,用GBK构造);
若是通过UTF-8构造则会产生Unicode字符"\uFFFD",不能恢复,若再通过String-UTF-8〉ByteArray-GBK〉String,则会出现杂码,如a锟斤拷锟斤拷
3)一个汉字对应三个问号
在通过UTF-8从字符串获取字节数组时,由于一个Unicode转换成三个byte,如果此时用ISO-8859-1构造字符串就会出现三个问号;用GBK构造字符串就会出现杂码,如a涓枃。 Java应用中的编码问题为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问题,特别是乱码问题,我觉得组成一个系列来描述和分析更好一些,包括三篇文章:
第一篇:JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础
第二篇:JAVA字符编码系列二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换
第三篇:JAVA字符编码系列三:Java应用中的编码问题 第三篇:JAVA字符编码系列三:Java应用中的编码问题
这部分采用重用机制,引用一篇文章来完整本部分目标。
来源: Eceel东西在线 问题研究--字符集编码
地址:http://china.eceel.com/article/study_for_character_encoding_java.htm
问题研究--字符集编码 1. 概述 本文主要包括以下几个方面:编码基本知识,java,系统软件,url,工具软件等。 在下面的描述中,将以"中文"两个字为例,经查表可以知道其GB2312编码是"d6d0 cec4",Unicode编码为"4e2d 6587",UTF编码就是"e4b8ad e69687"。注意,这两个字没有iso8859-1编码,但可以用iso8859-1编码来"表示"。 2. 编码基本知识 最早的编码是iso8859-1,和ascii编码相似。但为了方便表示各种各样的语言,逐渐出现了很多标准编码,重要的有如下几个。 2.1. iso8859-1 属于单字节编码,最多能表示的字符范围是0-255,应用于英文系列。比如,字母'a'的编码为0x61=97。 很明显,iso8859-1编码表示的字符范围很窄,无法表示中文字符。但是,由于是单字节编码,和计算机最基础的表示单位一致,所以很多时候,仍旧使用iso8859-1编码来表示。而且在很多协议上,默认使用该编码。比如,虽然"中文"两个字不存在iso8859-1编码,以gb2312编码为例,应该是"d6d0 cec4"两个字符,使用iso8859-1编码的时候则将它拆开为4个字节来表示:"d6 d0 ce c4"(事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是UTF编码,则是6个字节"e4 b8 ad e6 96 87"。很明显,这种表示方法还需要以另一种编码为基础。 2.2. GB2312/GBK 这就是汉子的国标码,专门用来表示汉字,是双字节编码,而英文字母和iso8859-1一致(兼容iso8859-1编码)。其中gbk编码能够用来同时表示繁体字和简体字,而gb2312只能表示简体字,gbk是兼容gb2312编码的。 2.3. unicode 这是最统一的编码,可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。所以可以说它是不兼容iso8859-1编码的,也不兼容任何编码。不过,相对于iso8859-1编码来说,uniocode编码只是在前面增加了一个0字节,比如字母'a'为"00 61"。 需要说明的是,定长编码便于计算机处理(注意GB2312/GBK不是定长编码),而unicode又可以用来表示所有字符,所以在很多软件内部是使用unicode编码来处理的,比如java。 2.4. UTF 考虑到unicode编码不兼容iso8859-1编码,而且容易占用更多的空间:因为对于英文字母,unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码,utf编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不过,utf编码是不定长编码,每一个字符的长度从1-6个字节不等。另外,utf编码自带简单的校验功能。一般来讲,英文字母都是用一个字节表示,而汉字使用三个字节。 注意,虽然说utf是为了使用更少的空间而使用的,但那只是相对于unicode编码来说,如果已经知道是汉字,则使用GB2312/GBK无疑是最节省的。不过另一方面,值得说明的是,虽然utf编码对汉字使用3个字节,但即使对于汉字网页,utf编码也会比unicode编码节省,因为网页中包含了很多的英文字符。 3. java对字符的处理 在java应用软件中,会有多处涉及到字符集编码,有些地方需要进行正确的设置,有些地方需要进行一定程度的处理。 3.1. getBytes(charset) 这是java字符串处理的一个标准函数,其作用是将字符串所表示的字符按照charset编码,并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文",正常情况下(即没有错误的时候)存储为"4e2d 6587",如果charset为"gbk",则被编码为"d6d0 cec4",然后返回字节"d6 d0 ce c4"。如果charset为"utf8"则最后是"e4 b8 ad e6 96 87"。如果是"iso8859-1",则由于无法编码,最后返回 "3f 3f"(两个问号)。 3.2. new String(charset) 这是java字符串处理的另一个标准函数,和上一个函数的作用相反,将字节数组按照charset编码进行组合识别,最后转换为unicode存储。参考上述getBytes的例子,"gbk" 和"utf8"都可以得出正确的结果"4e2d 6587",但iso8859-1最后变成了"003f 003f"(两个问号)。 因为utf8可以用来表示/编码所有字符,所以new String( str.getBytes( "utf8" ), "utf8" ) === str,即完全可逆。 3.3. setCharacterEncoding() 该函数用来设置http请求或者相应的编码。 对于request,是指提交内容的编码,指定后可以通过getParameter()则直接获得正确的字符串,如果不指定,则默认使用iso8859-1编码,需要进一步处理。参见下述"表单输入"。值得注意的是在执行setCharacterEncoding()之前,不能执行任何getParameter()。java doc上说明:This method must be called prior to reading request parameters or reading input using getReader()。而且,该指定只对POST方法有效,对GET方法无效。分析原因,应该是在执行第一个getParameter()的时候,java将会按照编码分析所有的提交内容,而后续的getParameter()不再进行分析,所以setCharacterEncoding()无效。而对于GET方法提交表单是,提交的内容在URL中,一开始就已经按照编码分析所有的提交内容,setCharacterEncoding()自然就无效。 对于response,则是指定输出内容的编码,同时,该设置会传递给浏览器,告诉浏览器输出内容所采用的编码。 3.4. 处理过程 下面分析两个有代表性的例子,说明java对编码有关问题的处理方法。 3.4.1. 表单输入 User input *(gbk:d6d0 cec4) browser *(gbk:d6d0 cec4) web server iso8859-1(00d6 00d 000ce 00c4) class,需要在class中进行处理:getbytes("iso8859-1")为d6 d0 ce c4,new String("gbk")为d6d0 cec4,内存中以unicode编码则为4e2d 6587。 l 用户输入的编码方式和页面指定的编码有关,也和用户的操作系统有关,所以是不确定的,上例以gbk为例。 l 从browser到web server,可以在表单中指定提交内容时使用的字符集,否则会使用页面指定的编码。而如果在url中直接用?的方式输入参数,则其编码往往是操作系统本身的编码,因为这时和页面无关。上述仍旧以gbk编码为例。 l Web server接收到的是字节流,默认时(getParameter)会以iso8859-1编码处理之,结果是不正确的,所以需要进行处理。但如果预先设置了编码(通过request. setCharacterEncoding ()),则能够直接获取到正确的结果。 l 在页面中指定编码是个好习惯,否则可能失去控制,无法指定正确的编码。 3.4.2. 文件编译 假设文件是gbk编码保存的,而编译有两种编码选择:gbk或者iso8859-1,前者是中文windows的默认编码,后者是linux的默认编码,当然也可以在编译时指定编码。 Jsp *(gbk:d6d0 cec4) java file *(gbk:d6d0 cec4) compiler read uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) compiler write utf(gbk: e4b8ad e69687; iso8859-1: *) compiled file unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4) class。所以用gbk编码保存,而用iso8859-1编译的结果是不正确的。 class unicode(4e2d 6587) system.out / jsp.out gbk(d6d0 cec4) os console / browser。 l 文件可以以多种编码方式保存,中文windows下,默认为ansi/gbk。 l 编译器读取文件时,需要得到文件的编码,如果未指定,则使用系统默认编码。一般class文件,是以系统默认编码保存的,所以编译不会出问题,但对于jsp文件,如果在中文windows下编辑保存,而部署在英文linux下运行/编译,则会出现问题。所以需要在jsp文件中用pageEncoding指定编码。 l Java编译的时候会转换成统一的unicode编码处理,最后保存的时候再转换为utf编码。 l 当系统输出字符的时候,会按指定编码输出,对于中文windows下,System.out将使用gbk编码,而对于response(浏览器),则使用jsp文件头指定的contentType,或者可以直接为response指定编码。同时,会告诉browser网页的编码。如果未指定,则会使用iso8859-1编码。对于中文,应该为browser指定输出字符串的编码。 l browser显示网页的时候,首先使用response中指定的编码(jsp文件头指定的contentType最终也反映在response上),如果未指定,则会使用网页中meta项指定中的contentType。 3.5. 几处设置 对于web应用程序,和编码有关的设置或者函数如下。 3.5.1. jsp编译 指定文件的存储编码,很明显,该设置应该置于文件的开头。例如:<%@page pageEncoding="GBK"%>。另外,对于一般class文件,可以在编译的时候指定编码。 3.5.2. jsp输出 指定文件输出到browser是使用的编码,该设置也应该置于文件的开头。例如:<%@ page contentType="text/html; charset= GBK" %>。该设置和response.setCharacterEncoding("GBK")等效。 3.5.3. meta设置 指定网页使用的编码,该设置对静态网页尤其有作用。因为静态网页无法采用jsp的设置,而且也无法执行response.setCharacterEncoding()。例如:<META http-equiv="Content-Type" content="text/html; charset=GBK" /> 如果同时采用了jsp输出和meta设置两种编码指定方式,则jsp指定的优先。因为jsp指定的直接体现在response中。 需要注意的是,apache有一个设置可以给无编码指定的网页指定编码,该指定等同于jsp的编码指定方式,所以会覆盖静态网页中的meta指定。所以有人建议关闭该设置。 3.5.4. form设置 当浏览器提交表单的时候,可以指定相应的编码。例如:<form accept-charset= "gb2312">。一般不必不使用该设置,浏览器会直接使用网页的编码。 4. 系统软件 下面讨论几个相关的系统软件。 4.1. mysql数据库 很明显,要支持多语言,应该将数据库的编码设置成utf或者unicode,而utf更适合与存储。但是,如果中文数据中包含的英文字母很少,其实unicode更为适合。 数据库的编码可以通过mysql的配置文件设置,例如default-character-set=utf8。还可以在数据库链接URL中设置,例如: useUnicode=true&characterEncoding=UTF-8。注意这两者应该保持一致,在新的sql版本里,在数据库链接URL里可以不进行设置,但也不能是错误的设置。 4.2. apache appache和编码有关的配置在httpd.conf中,例如AddDefaultCharset UTF-8。如前所述,该功能会将所有静态页面的编码设置为UTF-8,最好关闭该功能。 另外,apache还有单独的模块来处理网页响应头,其中也可能对编码进行设置。 4.3. linux默认编码 这里所说的linux默认编码,是指运行时的环境变量。两个重要的环境变量是LC_ALL和LANG,默认编码会影响到java URLEncode的行为,下面有描述。 建议都设置为"zh_CN.UTF-8"。 4.4. 其它 为了支持中文文件名,linux在加载磁盘时应该指定字符集,例如:mount /dev/hda5 /mnt/hda5/ -t ntfs -o iocharset=gb2312。 另外,如前所述,使用GET方法提交的信息不支持request.setCharacterEncoding(),但可以通过tomcat的配置文件指定字符集,在tomcat的server.xml文件中,形如:<Connector ... URIEncoding="GBK"/>。这种方法将统一设置所有请求,而不能针对具体页面进行设置,也不一定和browser使用的编码相同,所以有时候并不是所期望的。 5. URL地址 URL地址中含有中文字符是很麻烦的,前面描述过使用GET方法提交表单的情况,使用GET方法时,参数就是包含在URL中。 5.1. URL编码 对于URL中的一些特殊字符,浏览器会自动进行编码。这些字符除了"/?&"等外,还包括unicode字符,比如汉子。这时的编码比较特殊。 IE有一个选项"总是使用UTF-8发送URL",当该选项有效时,IE将会对特殊字符进行UTF-8编码,同时进行URL编码。如果改选项无效,则使用默认编码"GBK",并且不进行URL编码。但是,对于URL后面的参数,则总是不进行编码,相当于UTF-8选项无效。比如"中文.html?a=中文",当UTF-8选项有效时,将发送链接"%e4%b8%ad%e6%96%87.html?a=\x4e\x2d\x65\x87";而UTF-8选项无效时,将发送链接"\x4e\x2d\x65\x87.html?a=\x4e\x2d\x65\x87"。注意后者前面的"中文"两个字只有4个字节,而前者却有18个字节,这主要时URL编码的原因。 当web server(tomcat)接收到该链接时,将会进行URL解码,即去掉"%",同时按照ISO8859-1编码(上面已经描述,可以使用URLEncoding来设置成其它编码)识别。上述例子的结果分别是"\ue4\ub8\uad\ue6\u96\u87.html?a=\u4e\u2d\u65\u87"和"\u4e\u2d\u65\u87.html?a=\u4e\u2d\u65\u87",注意前者前面的"中文"两个字恢复成了6个字符。这里用"\u",表示是unicode。 所以,由于客户端设置的不同,相同的链接,在服务器上得到了不同结果。这个问题不少人都遇到,却没有很好的解决办法。所以有的网站会建议用户尝试关闭UTF-8选项。不过,下面会描述一个更好的处理办法。 5.2. rewrite 熟悉的人都知道,apache有一个功能强大的rewrite模块,这里不描述其功能。需要说明的是该模块会自动将URL解码(去除%),即完成上述web server(tomcat)的部分功能。有相关文档介绍说可以使用[NE]参数来关闭该功能,但我试验并未成功,可能是因为版本(我使用的是apache 2.0.54)问题。另外,当参数中含有"?& "等符号的时候,该功能将导致系统得不到正常结果。 rewrite本身似乎完全是采用字节处理的方式,而不考虑字符串的编码,所以不会带来编码问题。 5.3. URLEncode.encode() 这是Java本身提供对的URL编码函数,完成的工作和上述UTF-8选项有效时浏览器所做的工作相似。值得说明的是,java已经不赞成不指定编码来使用该方法(deprecated)。应该在使用的时候增加编码指定。 当不指定编码的时候,该方法使用系统默认编码,这会导致软件运行结果得不确定。比如对于"中文",当系统默认编码为"gb2312"时,结果是"%4e%2d%65%87",而默认编码为"UTF-8",结果却是"%e4%b8%ad%e6%96%87",后续程序将难以处理。另外,这儿说的系统默认编码是由运行tomcat时的环境变量LC_ALL和LANG等决定的,曾经出现过tomcat重启后就出现乱码的问题,最后才郁闷的发现是因为修改修改了这两个环境变量。 建议统一指定为"UTF-8"编码,可能需要修改相应的程序。 5.4. 一个解决方案 上面说起过,因为浏览器设置的不同,对于同一个链接,web server收到的是不同内容,而软件系统有无法知道这中间的区别,所以这一协议目前还存在缺陷。 针对具体问题,不应该侥幸认为所有客户的IE设置都是UTF-8有效的,也不应该粗暴的建议用户修改IE设置,要知道,用户不可能去记住每一个web server的设置。所以,接下来的解决办法就只能是让自己的程序多一点智能:根据内容来分析编码是否UTF-8。 比较幸运的是UTF-8编码相当有规律,所以可以通过分析传输过来的链接内容,来判断是否是正确的UTF-8字符,如果是,则以UTF-8处理之,如果不是,则使用客户默认编码(比如"GBK"),下面是一个判断是否UTF-8的例子,如果你了解相应规律,就容易理解。 public static boolean isValidUtf8(byte[] b,int aMaxCount){ int lLen=b.length,lCharCount=0; for(int i=0;i<lLen && lCharCount<aMaxCount;++lCharCount){ byte lByte=b[i++];//to fast operation, ++ now, ready for the following for(;;) if(lByte>=0) continue;//>=0 is normal ascii if(lByte<(byte)0xc0 || lByte>(byte)0xfd) return false; int lCount=lByte>(byte)0xfc?5:lByte>(byte)0xf8?4 :lByte>(byte)0xf0?3:lByte>(byte)0xe0?2:1; if(i+lCount>lLen) return false; for(int j=0;j<lCount;++j,++i) if(b[i]>=(byte)0xc0) return false; } return true; } 相应地,一个使用上述方法的例子如下: public static String getUrlParam(String aStr,String aDefaultCharset) throws UnsupportedEncodingException{ if(aStr==null) return null; byte[] lBytes=aStr.getBytes("ISO-8859-1"); return new String(lBytes,StringUtil.isValidUtf8(lBytes)?"utf8":aDefaultCharset); } 不过,该方法也存在缺陷,如下两方面: l 没有包括对用户默认编码的识别,这可以根据请求信息的语言来判断,但不一定正确,因为我们有时候也会输入一些韩文,或者其他文字。 l 可能会错误判断UTF-8字符,一个例子是"学习"两个字,其GBK编码是" \xd1\xa7\xcf\xb0",如果使用上述isValidUtf8方法判断,将返回true。可以考虑使用更严格的判断方法,不过估计效果不大。 有一个例子可以证明google也遇到了上述问题,而且也采用了和上述相似的处理方法,比如,如果在地址栏中输入"http://www.google.com/search?hl=zh-CN&newwindow=1&q=学习",google将无法正确识别,而其他汉字一般能够正常识别。 最后,应该补充说明一下,如果不使用rewrite规则,或者通过表单提交数据,其实并不一定会遇到上述问题,因为这时可以在提交数据时指定希望的编码。另外,中文文件名确实会带来问题,应该谨慎使用。 6. 其它 下面描述一些和编码有关的其他问题。 6.1. SecureCRT 除了浏览器和控制台与编码有关外,一些客户端也很有关系。比如在使用SecureCRT连接linux时,应该让SecureCRT的显示编码(不同的session,可以有不同的编码设置)和linux的编码环境变量保持一致。否则看到的一些帮助信息,就可能是乱码。 另外,mysql有自己的编码设置,也应该保持和SecureCRT的显示编码一致。否则通过SecureCRT执行sql语句的时候,可能无法处理中文字符,查询结果也会出现乱码。 对于Utf-8文件,很多编辑器(比如记事本)会在文件开头增加三个不可见的标志字节,如果作为mysql的输入文件,则必须要去掉这三个字符。(用linux的vi保存可以去掉这三个字符)。一个有趣的现象是,在中文windows下,创建一个新txt文件,用记事本打开,输入"连通"两个字,保存,再打开,你会发现两个字没了,只留下一个小黑点。 6.2. 过滤器 如果需要统一设置编码,则通过filter进行设置是个不错的选择。在filter class中,可以统一为需要的请求或者回应设置编码。参加上述setCharacterEncoding()。这个类apache已经给出了可以直接使用的例子SetCharacterEncodingFilter。 6.3. POST和GET 很明显,以POST提交信息时,URL有更好的可读性,而且可以方便的使用setCharacterEncoding()来处理字符集问题。但GET方法形成的URL能够更容易表达网页的实际内容,也能够用于收藏。 从统一的角度考虑问题,建议采用GET方法,这要求在程序中获得参数是进行特殊处理,而无法使用setCharacterEncoding()的便利,如果不考虑rewrite,就不存在IE的UTF-8问题,可以考虑通过设置URIEncoding来方便获取URL中的参数。 6.4. 简繁体编码转换 GBK同时包含简体和繁体编码,也就是说同一个字,由于编码不同,在GBK编码下属于两个字。有时候,为了正确取得完整的结果,应该将繁体和简体进行统一。可以考虑将UTF、GBK中的所有繁体字,转换为相应的简体字,BIG5编码的数据,也应该转化成相应的简体字。当然,仍旧以UTF编码存储。 例如,对于"语言 語言",用UTF表示为"\xE8\xAF\xAD\xE8\xA8\x80 \xE8\xAA\x9E\xE8\xA8\x80",进行简繁体编码转换后应该是两个相同的 "\xE8\xAF\xAD\xE8\xA8\x80>"。 35岁前成功的12条黄金法则第一章:一个目标 一艘没有航行目标的船,任何方向的风都是逆风 1、你为什么是穷人,第一点就是你没有立下成为富人的目标 2、你的人生核心目标是什么? 杰出人士与平庸之辈的根本差别并不是天赋、机遇,而在于有无目标。 3、起跑领先一步,人生领先一大步:成功从选定目标开始 4、贾金斯式的人永远不会成功 为什么大多数人没有成功?真正能完成自己计划的人只有5%,大多数人不是将自己的目标舍弃,就是沦为缺乏行动的空想 5、 如果你想在35岁以前成功,你一定在25至30岁之间确立好你的人生目标 6、 每日、每月、每年都要问自己:我是否达到了自己定下的目标 第二章:两个成功基点 站好位置,调正心态,努力冲刺,35岁以前成功 (一)人生定位 1、 人怕入错行:你的核心竞争力是什么? 2、 成功者找方法,失败者找借口 3、 从三百六十行中选择你的最爱 人人都可以创业,但却不是人人都能创业成功 4、 寻找自己的黄金宝地 (二)永恒的真理:心态决定命运,35岁以前的心态决定你一生的命运 1、 不满现状的人才能成为富翁 2、 敢于梦想,勇于梦想,这个世界永远属于追梦的人 3、 35岁以前不要怕,35岁以后不要悔 4、 出身贫民,并非一辈子是贫民,只要你永远保持那颗进取的心。中国成功人士大多来自小地方 5、 做一个积极的思维者 6、 不要败给悲观的自己 有的人比你富有一千倍,他们也会比你聪明一千倍么?不会,他们只是年轻时心气比你高一千倍。 人生的好多次失败,最后并不是败给别人,而是败给了悲观的自己。 7、 成功者不过是爬起来比倒下去多一次 8、 宁可去碰壁,也不要在家里面壁 克服你的失败、消极的心态 (1) 找个地方喝点酒 (2) 找个迪厅跳跳舞 (3) 找帮朋友侃侃山 (4) 积极行动起来 第三章:三大技巧 1、管理时间:你的时间在哪里,你的成就就在哪里。 把一小时看成60分钟的人,比看作一小时的人多60倍 2、你不理财,财不理你 3、自我管理,游刃有余 (1) 创业不怕本小,脑子一定要好 (2) 可以开家特色店 (3) 做别人不愿做的生意 第四章:四项安身立命的理念 35岁以前一定要形成个人风格 1、做人优于做事 做事失败可以重来,做人失败却不能重来 (1) 做人要讲义气 (2) 永不气馁 2、豁达的男人有财运,豁达的女人有帮夫运 35岁以前搞定婚姻生活 3、忠诚的原则:35岁以前你还没有建立起忠诚美誉,这一缺点将要困扰你的一生 4、把小事做细,但不要耍小聪明 中国人想做大事的人太多,而愿把小事做完美的人太少 第五章:五分运气 比尔·盖茨说:人生是不公平的,习惯去接受它吧 1、人生的确有很多运气的成人:谋事在人,成事在天:中国的古训说明各占一半 2、机会时常意外地降临,但属于那些不应决不放弃的人 3、抓住人生的每一次机会 机会就像一只小鸟,如果你不抓住,它就会飞得无影无踪 4、 者早一步,愚者晚一步 第六章:六项要求 1、智慧 (1)别人可你以拿走你的一切,但拿不走你的智慧 (2)巧妙运用自己的智慧 (3)智者与愚者的区别 2、勇气 (1)勇气的力量有时会让你成为"超人" (2)敢于放弃,敢于"舍得" 3、培养自己的"领导才能、领袖气质" (1) 激情感染别人 (2) "三o七法则"实现领袖气质 (3) 拍板决断能力 (4) 人格魅力 4、创造性:不要做循规蹈矩的人 25-35岁是人生最有创造性的阶段,很多成功人士也都产生在这一阶段 5、明智 (1) 知道自己的长处、短处,定向聚焦 (2) 尽量在自己的熟悉的领域努力 6、持之以恒的行动力:在你选定行业坚持十年,你一定会成为大赢家 第七章:七分学习 1、知识改变命运 2、35岁以前学会你行业中必要的一切知识 a) 每天淘汰你自己 b) 在商言商 3、太相信的书人,只能成为打工仔 4、思考、实践、再思考、再实践 第八章:八分交际 朋友多了路好走 1、智商很重要,情商更重要:35岁以前建立起人际关系网 2、人脉即财脉:如何搞好人际关系 3、交友有原则 4、善于沟通:35岁以前要锻炼出自己的演讲才能 第九章:九分习惯 习惯的力量是惊人的,35岁以前养成的习惯决定着你的成功的大小 1、积极思维的好习惯 2、养成高效工作的好习惯 (1) 办公室 (2) 生活可以不拘小节,但要把工作做细 (3) 学习聆听,不打断别人说话 3、养成锻炼身体的好习惯 4、广泛爱好的好习惯 5、快速行动的好习惯 第十章:十分自信 1、自信是成功的精神支柱 2、自信方能赢得别人的信任 3、把自信建立在创造价值的基础上 4、如何建立自信 (1) 为自己确立目标 (2) 发挥自己的长处 (3) 做事要有计划 (4) 做事不拖拉 (5) 轻易不要放弃 (6) 学会自我激励 (7) 不要让自己成为别人 第十一章:11个需要避开的成功陷阱 1、只有功劳,没有苦劳 2、不要"怀才不遇",而要寻找机遇 3、不要想发横财 4、不要为钱而工作,而让钱为你工作 5、 盲目跟风,人云亦云,人做我也做 6、 小富即安,不思进取,知足常乐 7、 承认错误而非掩饰错误 8、 脚踏实地而非想入非非 9、 野心太大而不是信心十足 10、反复跳槽不可取 11、眼高手低 12、不择手段 第十二章:十二分努力 没有人能随随便便成功 1、小不是成功,大不是成功,由小变大才是成功 2、中国社会进入微利时代:巧干+敢干+实干=成功 3、努力尝试就有成功的可能 4、做任何事情,尽最大努力 5、把事情当成事业来做 6、我看打工者 7、祝你早日掘到第一桶金 养成助你成功的十个习惯一 热诚的态度 我们的态度决定了我们的未来一个人能否成功,取决了他的态度! 成功人士与失败之间的判别是: 成功人士始终有最热诚的态度最积极的思考,最乐观的精神和最辉煌的以经验支配和控制自己的人生,失败者则相反,他们的人生是受过人生的种种失败怀疑虑所引导和支配。 我们的态度决定了我们人生的成功 1,我们怎样对待生活,生活就怎样对待我们。 我们的环境---心里的\感情的\精神的\----完全由我们自己的态度来创造。 二 目标明确\目标管理 1目标明确 没有线路图什么地方也去不了。 2目标管理 把整体目标分解成一个个易记的目标把你的目标象成一金字塔,塔顶就是你的人生目标,你定的目标和为达目标而做的每一件事都必须指向你的人生目标。 三 一勤天下无难事 一心向着自己目标前进的人,整个世界都给他让路。 四 擅于理财、预算时间和金钱 五 喜欢运动 健康的体魄是成就事业的资本。成功人士几乎都有自己喜欢的体育项目。 六 自律 自控能力的强弱对人生的成功也有很大的影响。 1 当你生气时,你能沉默不语吗? 七 谦虚好学 1 你是否把不断的学习更多的知识作为你的职责? 越是成功的人,他们越会抓住一切可以学习的机会。 八 良好的人际关系 成功意味着别人的参与 九 信念 十 立即行动 Hibernate为什么如此成功?Why This Project Is Successful 这篇文章是Gavin King写的,非常有趣,充分体现了Hibernate的设计理念,我粗略的意译如下: 下面是对Hibernate开发工作的个人想法,正是这些工作使得Hibernate如此迅速的得到广泛的欢迎。 1、飞快的版本发布 保持活跃的开发速度,经常进行版本发布,甚至几天之内就从前一个版本开发到下一个版本。这样是保证软件远离Bug的最好的办法,也可以让用户感到很放心,确信Hibernate的开发十分活跃,另外这样做也有一大好处,就是可以发现哪些功能是用户真正需要的。 2、回归测试 我想现在整个Java社区一定都很重视自动回归测试。如果软件的功能和设计有比较大的修改,那么一个综合性的test suite对于软件可维护性和稳定性来说实在是太重要了。我们应该有这样的意识:如果对软件的一个新功能没有进行回归测试,我们根本就不该去做它。 3、把一个功能做到最好 要么不做,要做,就一定做到最好。那些我们做不到最好的功能,我们根本不去做,扔给其他软件去做吧。 4、避免过度设计 浪费大量的时间和精力进行软件功能的抽象和扩充软件的灵活性,还不如多花点时间来解决你的用户面临的实际问题呢!简单一点! 软件能跑起来就OK,不要尝试去解决你的用户根本不关心的问题。就算你的软件设计的不够优雅也没有关系,反正还是initial阶段嘛!以后再 refactor,你应该关注的问题是及时的把有用的功能给做出来。 5、集权 在你需要由民主投票来下决定之前,至少你已经把软件轮廓做好了。软件开发需要由一两个开明的人来领导,这样可以保证软件开发的连贯性而不至于产生太大的分歧,可以保证开发团队集中火力把要实现的功能做到最好。我觉得,OSS软件最大的风险就是意见不统一,摊子铺的太大,结果最后搞的什么都没有做好。 (译者按:非常赞同,凡是成功的OSS软件,都是在某个牛人已经把软件做好了之后,发布出来,然后由大家往里面添加功能的,并且在牛人的领导下不断进步。缺乏牛人的OSS软件都不算很成功,比如Mozilla) 6、文档 没有什么比文档更重要的了。如果你的用户不知道你的软件有这么一个功能,就等于没有这个功能,干脆把它去掉得了,省得给源代码增加复杂度。 7、避免标准化 好的标准可以带来软件的互用性和可移植性,坏的标准能够窒息软件创新!“支持XXX标准”根本就不是真实的用户需求,特别是当这个XXX标准是那些在其位不谋其政“所谓”的专家委员会制订出来的。(译者按:莫非指Sun,IBM等几个big name?)最好的软件是在不断的尝试,不断的出错,不断的经验积累的过程中产生的。 事实上的标准往往更加贴近用户需求。 8、10分钟之内把Hibernate跑起来 潜在的Hibernate的用户在他们下载了Hibernate,第一次使用的时候根本就不可能花半个小时那么多时间来安装、配置和 troubleshooting,他们早就丧失了对Hibernate的兴趣了。我们的口号就是新用户(假设有足够的JDBC知识)5分钟之内把 Hibernate的Demo跑起来,而他们能够在1个小时之内写出“Hello World”式的最简单的Hibernate程序并且正常运行。 9、开发人员的责任感 用户总是不可避免的碰到问题,开发团队有责任有义务提供帮助。用户让我们知道了文档的漏洞,用户让我们知道了测试用例的小bug。此外,没有用户来用我们的Hibernate,我们还开发它做什么,不是浪费时间吗! 有个关于bug的笑话:用户根本不介意发现新功能的bug(译者按:Windows的用户好像都是如此),只要你能迅速的改掉bug。“责任感”意味着 bug修复应该在1周之内。从收到bug报告到bug修复代码提交到CVS上要做到平均在24小时左右,这才是一个理想的目标。 10、易用的、可更新的wiki网页 让FCKeditor在 JSP和JSF 网站项目中跑起来让FCKeditor在 JSP和JSF 网站项目中跑起来
一、准备功夫 说明:
三、修改 <servlet> 这个是对话框的简单上传功能所用到,可以上传文件 <servlet-mapping> 通过观察,发现filemanager请求服务器的路径,就修改如下 3.4 修改 com.fredck.FCKeditor.uploader.SimpleUploaderServlet 找到 org.fckfaces.taglib.html.FCKFaceEditorTag 类,修改下面方法 public Object saveState(FacesContext context) public void restoreState(FacesContext context, Object state)
在调试fck-faces的时候,发现fck-faces的标签非常强大,居然不用再jsp页面引入fckeditor.js, public class Util{ public Util() { } public static final String internalPath(String path) public static final String FCK_FACES_RESOURCE_PREFIX = "/"; //这里原来是/fckfaces 如果你是反编译得到的源代码,则编译成功之后,需要替换掉原来jar包里面的这个class文件。 如果你是获得源码修改,可能需要注意配置fck-faces.taglib.xml,fck-faces.tld和faces-config.xml。这几个文件在压缩包里面都有。 四、使用 4.1 使用javascipt替换textarea方法 <h:form id="newsadd"> <script type="text/javascript"> 4.2 使用jsf标签 五、严重注意事项 JAVA基础:提升JSP应用程序的七大绝招提升JSP应用程序的七大绝招
你时常被客户抱怨JSP页面响应速度很慢吗?你想过当客户访问次数剧增时,你的WEB应用能承受日益增加的访问量吗?本文讲述了调整JSP和servlet的一些非常实用的方法,它可使你的servlet和JSP页面响应更快,扩展性更强。而且在用户数增加的情况下,系统负载会呈现出平滑上长的趋势。在本文中,我将通过一些实际例子和配置方法使得你的应用程序的性能有出人意料的提升。其中,某些调优技术是在你的编程工作中实现的。而另一些技术是与应用服务器的配置相关的。在本文中,我们将详细地描述怎样通过调整servlet和JSP页面,来提高你的应用程序的总体性能。在阅读本文之前,假设你有基本的servlet和JSP的知识。 方法2: 禁止servlet和JSP 自动重载(auto-reloading) 方法4: 将页面输出进行压缩 方法6: 选择正确的页面包含机制 其它一些有用的方法 --------------------------------------------------------------------------- http://community.csdn.net/Expert/topicview.asp?id=5237902 shine333(enihs) 对本文的补充 方法 4: 将页面输出进行压缩 其他的不知道,Tomcat直接修改server.xml: <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 --> <!-- Note : To use gzip compression you could set the following properties : compression="on" 当上面一段被添加到Connector后,只要是大于2048字节的Content Type为text/html,text/xml,且客户端不是gozilla, traviata的,都会被gzip,建议实际应用楼主的Servlet的时候考虑上述设置,即只有对文本类型,且过大的内容进行压缩,才会有益 在Struts+Hibernate+Spring中解决中文乱码的方法软件环境:JDK1.4.2_09+Eclipse3.1+MS SQL SERVER200+SP3+JTDS1.0.2+Struts1.1+Hibernate3.0.5+Spring1.2.4。 由于刚开始学习这个Framework,所以很多东西也不是特别清楚,以前在JB环境下也没怎么遇到乱码问题。这次试了很多方法都不行,于是决定加个Fileter了,web.xml部分内容如下: <filter> 通过在Action中加断点调试,发现使用超连接的跳转是可以使用Filter的;但是如果是以.do为后缀的请求就不行了,抱着试试看的心理,我修改了web.xml: <?xml version="1.0" encoding="UTF-8"?> 主要在这里多加了一个过滤内容!其他的,为防止万一,在页面(jsp)上也加了些东西: <%@ page contentType="text/html; charset=GBK" pageEncoding="GBK"%> <meta http-equiv="content-type" content="text/html; charset=GBK"> 呵呵,可以说是武装到牙齿了,开始调试:这次在Debug的时候,显示出从页面中传来的值终于不是乱码了,保存在数据库中后,也不是乱码。这个问题目前是部分解决了。因为我还没有测试在页面上哪些是不用写的,还有就是页面回现汉字是是否会有问题,不过这里先把自己的所得记录下来,如果有高人就此事谈论过,就算我孤陋寡闻吧,呵呵。 另外给出我的Hibernate.cfg.xml的部分内容: <session-factory> 实战Struts+Spring+Hibernate实战Struts+Spring+Hibernate 经过几天在网上的搜索,查看了关于Struts、Spring、Hibernate的文章,在实际的使用后得到了一些体会。鉴于很多资料不全或较旧,特写下一篇较为完整的实战指南,包括主要程序类和配置文件。 一、准备篇 2、IPlayerDAO.java 持久层的接口类 public interface IPlayerDAO
public class TblPlayerDAO extends HibernateDaoSupport implements IPlayerDAO public TblPlayer findById(String id) 4、IPlayerService 业务层接口类 public interface IPlayerService 5、PlayerServiceImpl 业务层接口实现类 public List findTop(final int count) 6、PlayerAction.java 7、PlayerForm 三、配置篇 <!DOCTYPE web-app <web-app>
<taglib> <taglib> <taglib> <taglib> <taglib> 2、applicationContext-hibernate.xml <!-- | |||||||||||||||||||