• 2006-07-31

    我不喜欢struts-html Tag Lib 的原因

    Views: 16942 | 1 Comment

    我不喜欢 struts-html Tag Lib,因为它违反了一个基本原则----taglib不要生产html标签。在java代码中生产html是一种非常糟糕的作法(不过,有部分人不这样认为),它把程序员和美工溶为一体。

    我们来看看struts是如何用拙劣的方法生成html链接的,假设taglib引用前缀为html:

    <html:link page="stupid.html">
            <bean:message key="some.text" />
    </html:link>

    这是什么代码?如果你只使用简单的html(再加上el)就像下面:

    <a href="stupid.html">${some.text}</a>

    当然,这是两种不同的理念。struts-html tag lib 试图以MS Visual Studio编写应用程序类似的方式编写web application,于是就采用了MS Visual Studio的一些方法(在我看来是陋习)。

    正如前面说的,在java代码中生成html把程序员和美工溶为一体。当你尝试把两者分开时,美工必须从java代码中查找html的id,class属性,以及生成了什么标签。这对美工是一场恶梦!

    我也看到有些标签库把生成分页码(就是previouse 1 2 3 ... next 类似功能)的功能完全包装在java代码中。然后这样使用:

    <page:pagenum page="1" link="browse.jsp" pageSize="8" />(注:当然还有更多的属性可能达10个之多)

    这是什么鬼东西?!美工如何知道你生成了什么?难道你招来一个美工,你还要求他掌握你自己发明的语言(你别因此得意),然后他到了另一家又得学习另一个自以为是的人发明的语言吗?

    因为把程序员和美工溶为一体并不是完全无用的,所以有一部分人会反对我的说法,有一部分人会同意我的说法。

    Posted by ideawu at 2006-07-31 09:37:00
  • 2006-07-17

    知识和能力

    Views: 12263 | 1 Comment

    在论坛里常常看到一些新手(无论是Linux初学者,Java初学者,HTML初学者等)吵着让他们认为的高手教他们“解决一类问题的能力”。每当这时候我就笑了----解决一个具体的问题的能力尚且都难掌握,怎么就想别人教你解决“一类”问题的能力?你想一口吃个胖子吗?

    要讨论问题,首先要定义一些概念。先定义后使用。事实上,解决一个具体问题的方法是一种知识。而解决一类问题的方法是一种能力。所以这样来说,知识也是一种能力,但是,是能力的一种特殊情况。

    然后,我们还要明确一些常识(公理)。知识是可以教授的,而能力是无法教授的。为什么?因为问题是复杂的,而一类问题就更加复杂了。当你掌握了问题的本质之后,你就不会感到复杂了。这时,你几乎已经能够解决问题了。怎么掌握问题的本质?这是一种能力,是无法教授的。

    所以,我们要从知识的学习着手,也就是从特例着手。尝试分析一些特例,并解决它们。然后归纳,假设,求证。不要妄图别人教授你一种“普遍真理”,这是一种能力,是无法教授的。

    最后,我们还要明白一点。不是分析了一定量的特例之后就能掌握能力,也就是说,不是所有的量变都能产生质变。如果你不介意,就把这种现象称为“天命”吧。

    现在还有一个问题,知识是可以教授的,那么知识的是怎么学习的?学习知识是一种能力。这样,我们走进了循环。如果没有知识,就不会有能力;如果没有能力,就没有知识。怎么办?如果你不介意,最初始的学习的能力是天生的,然后在学习的过程中发展。

    每个人都处在知识与能力的矛盾当中。也就是到底是先利用现有的知识来取得能力,再利用取得到的能力获取知识?还是利用现有的能力来获取知识,然后再利用获取到的知识来取得能力?还是同时利用两者?什么叫“同时利用”?能达到“同时”吗?如果无法达到“同时”,如何控制比例?解决这种矛盾是一种能力,不一定能掌握,就算掌握了也无法教授给你。

    这时候,答案应该是“天命”吧。那么什么是“天命”,每一个人的天命是什么?

    也许我们不应该分知识和能力。

    *请回到文章开头往下读*

    Posted by ideawu at 2006-07-17 19:57:05
  • 2006-07-16

    Tomcat网站server.xml设置

    Views: 21952 | No Comments

    Tomcat 5 的最小的server.xml配置文件大概如下:

    01 <Server port="8005" shutdown="SHUTDOWN">
    02	<Service name="Catalina">
    03		<Connector port="8080" />
    04		<Engine name="Catalina" defaultHost="localhost">
    05			<Host name="localhost" appBase="/home/ideawu/webapps">
    06				<Context path="/bbs" docBase="bbs_file" />
    07			</Host>
    08		</Engine>
    09	</Service>
    10 </Server>
    

    第3行设置的Tomcat的监听端口。如果你要访问自己机器上的网站,在浏览器里输入http://localhost:8080。

    Host是虚拟主机,一般是一个网站。Context表示一个Web Application(Web应用程序),如一个网站的留言板,论坛可以分别是一个Web应用程序。

    appBase="/home/ideawu/webapps"表示你的网站的程序放在 /home/ideawu/webapps 目录下,你也可以使用相对Tomcat安装路径的路径,不过一般不这么做。

    path="/bbs" docBase="bbs_file"表示一个目录为bbs_file的Web应用程序,程序的文件放在 /home/ideawu/webapps/bbs_file 目录下,你可以通过 http://localhost:8080/bbs 或者 http://localhost:8080/bbs_file 访问。也就是说,path是docBase的一个映射。

    你的网站的首页默认应该放在 /home/ideawu/webapps/ROOT 目录下,可以设置Context的path为空串来更改。

    Posted by ideawu at 2006-07-16 18:29:26
  • 2006-07-15

    缓存在计算机中的使用

    Views: 9050 | No Comments

    如果计算机中或者计算机间通信的两个实体的速度之比为1:100或者更悬殊时,那么它们之间就有使用缓存(Cache)的必要了。例如CPU/主存与磁盘,CPU/主存与键盘,CPU/主存与打印机等。缓存最先被使用在硬件上,然后在软件中也被应用。最著名的应该是CPU与内存(主存)之间的缓存了。

    缓存的速度比被缓存者的速度快很多。缓存是被缓存者的一个相联的副本,更重要的,它是被缓存者的访问接口。要想访问被缓存者,必须先访问缓存。缓存的改变将导致被缓存者的改变,这就是缓存的一致性问题。主要有两种方法保持缓存的一致性。一种是全写法,另一种是写回法。前一种是在缓存发生改变的同时改变被缓存者;后一种是在缓存改变达到一定程序时才改变被缓存者。前一种安全性更好,但效率受影响;后一种相反。

    缓存的性能指标除了速度之外,最重要的就是合命中率h。命中率等于访问缓存就能找到数据的次数除以总的访问次数,命中率与算法有关。注意,如果数据不在缓存中,那么将浪费一次访问缓存的时间。所以缓存系统的平均访问时间为h*nc+(1-h)*(nc+n)(全概率公式),其中nc为缓存的访问每一次时间,n为被缓存者的每一次访问时间。

    由于缓存容量小,所以需要替换缓存中的某些数据。相关的算法有:FIFO算法,LRU算法,RAND算法。

    • FIFO算法:也就是先进先出算法,最先被缓存的数据将被替换。
    • LRU算法:最近最少使用算法,在软件中一般使用链表来实现。数据每被使用一次,它将被放到表头,被替换的是表尾的数据。这种算法效率很好,所以最常用。
    • RAND算法:随机算法,随机替换数据。

    缓存系统的一个重要问题是缓存与被缓存者的映射(映像)问题,即如何识别缓存中的数据就是被缓存者中的数据。这里使用关键字,如果是内存,使用内存的地址,在软件中使用实体的标识(id)。即缓存中必须有一个字段用来保存数据的标识。相关的算法有

    • 全相联映射:缓存中有一个字段用来保存被缓存者数据的关键字。访问的时候迭代搜索,或者使用HASH法保存和搜索。HASH法浪费缓存的容量,一般在软件中使用,硬件中不使用。
    • 直接映射:如果关键字是有规律的,如内存地址。那么抽取关键字的一部分,另一部分被隐含在缓存和被缓存者的物理空间关系或者其它关系中。一个例子是,如果内存中有0,1,2,3,4,5,6,7号一共8个数据,缓存中有0,1共2个数据。规定0号缓存只能缓存内存中的0,2,4,6号数据中的一个,1号缓存只能缓存1,3,5,7号数据中的一个。当需要访问内存数据n时,先计算出CID = n MOD 2,所以只查找缓存CID,而不需要迭代整个缓存。如果你将上面的关键字转换为二进制,取模运算可以省略,因为模的值已经被隐含在二进制数中,所以关键字的保存也可以只保存二进制数中不包含模值的那部分。
    • 组相联映射:是上面两种的组合。将缓存分成多个组,组的查找使用直接映射,组内数据的查找使用全相联映射。

    因为速度总是与价格联系的,所以缓存永远不会消失。提高缓存性能可以从缓存的硬件性能,替换算法,映射算法3方面着手,还有就是系统的分析,即哪些数据适合被缓存。

    Posted by ideawu at 2006-07-15 07:52:08
  • 2006-07-15

    Resin对中文的支持比Tomcat好多了

    Views: 11223 | No Comments

    我一直以来都在使用Tomcat作为JSP Web服务器。Tomcat对中文的支持没有太大的问题,一些常见的问题都能轻易解决。最近使用Resin,发现Resin对中文的支持更加优秀。下面我对Tomcat5.0.28和Resin3.0.19默认安装不做任何设置后的中文乱码情况做个比较。

    GET提交中文表单

    无论是否设置request.setCharacterEncoding(),Tomcat获取的中文都出现乱码。而Resin直接获取,并不出现乱码。如果想Tomcat不出现乱码,那么你必须设置server.xml中的Connector(定义访问端口的那一个)标签的URIEncoding属性。

    POST提交中文表单

    只有设置了正确的request.setCharacterEncoding(),Tomcat获取的中文才不出现乱码。而Resin直接获取,并不出现乱码。

    include file中文文件

    如果被包含的JSP文件中有中文字符串,那么该字符串必须从系统当前编码转为ISO-8859-1,如String s = new String(("中文").getBytes("ISO-8859-1"),"UTF-8");,否则出现乱码。而Resin直接赋值字符串,String s = "中文";。

    Tomcat有这些古怪的行为,是因为它内部总是使用ISO-8859-1作为编码字符集。它读取JSP文件时,假设JSP文件是以ISO-8859-1编码的。

    所以在Tomcat之外我们有另一个选择Resin。

    相关链接:

    Posted by ideawu at 06:37:35
  • 2006-07-13

    使用ServletContextListener在服务器启动和关闭时创建和关闭缓存

    Views: 15656 | No Comments

    ServletContext 被 Servlet 程序用来与 Web 容器通信。例如写日志,转发请求。每一个 Web 应用程序含有一个Context,被Web应用内的各个程序共享。因为Context可以用来保存资源并且共享,所以我所知道的 ServletContext 的最大应用是Web缓存----把不经常更改的内容读入内存,所以服务器响应请求的时候就不需要进行慢速的磁盘I/O了。

    ServletContextListener 是 ServletContext 的监听者,如果 ServletContext 发生变化,如服务器启动时 ServletContext 被创建,服务器关闭时 ServletContext 将要被销毁。

    在JSP文件中,application 是 ServletContext 的实例,由JSP容器默认创建。Servlet 中调用 getServletContext()方法得到 ServletContext 的实例。

    我们使用缓存的思路大概是:

    1. 服务器启动时,ServletContextListener 的 contextInitialized()方法被调用,所以在里面创建好缓存。可以从文件中或者从数据库中读取取缓存内容生成类,用 ervletContext.setAttribute()方法将缓存类保存在 ServletContext 的实例中。

    2. 程序使用 ServletContext.getAttribute()读取缓存。如果是 JSP,使用a pplication.getAttribute()。如果是 Servlet,使用 getServletContext().getAttribute()。如果缓存发生变化(如访问计数),你可以同时更改缓存和文件/数据库。或者你等变化积累到一定程序再保存,也可以在下一步保存。

    3. 服务器将要关闭时,ServletContextListener 的 contextDestroyed()方法被调用,所以在里面保存缓存的更改。将更改后的缓存保存回文件或者数据库,更新原来的内容。

    import User; //my own class
    import DatabaseManager; // my own class
    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextListener;
    
    public class MyContextListener
    
    	implements ServletContextListener {
    	private ServletContext context = null;
    
    	public void contextInitialized(ServletContextEvent event) {
    		context = event.getServletContext();
    		User user = DatabaseManager.getUserById(1);
    		context.setAttribute("user1", user);
    	}
    
    	public void contextDestroyed(ServletContextEvent event) {
    		User user = (User)context.getAttribute("user1");
    		DatabaseManager.updateUserData(user);
    		this.context = null;
    	}
    }
    

    布署 ServletContextListener

    你实现(implements)了 ServletContextListener 编译后,把它放在正确的WEB-INF/classes目录下,更改WEB-INF目录下的 web.xml文件,在web-app节点里添加

    <listener>
    	<listener-class>MyServletContextListener</listener-class>
    </listener>
    
    Posted by ideawu at 2006-07-13 21:07:08
|<<<114115116117118119120121122>>>| 118/138 Pages, 825 Results.