<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>ahuaxuan</title>
    <description>不懂为啥还要装懂呢，人贵在有自知之明</description>
    <link>http://ahuaxuan.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>如何解决mysql的master-slave模式中ReplicationDriver的使用问题</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/205926" style="color:red;">http://ahuaxuan.javaeye.com/blog/205926</a>&nbsp;
          发表时间: 2008年06月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          /** <br />* 作者：张荣华 <br />* 日期：2008-6-19 <br />**/ <br /><br /><br />前言：<br />之前downpour有一个贴(<a href="http://www.javaeye.com/topic/143714" target="_blank">http://www.javaeye.com/topic/143714</a>)讨论了在java中如何使用mysql的master-slave模式(master-slave模式的介绍见Qieqie的这个贴：<a href="http://www.javaeye.com/topic/162717" target="_blank">http://www.javaeye.com/topic/162717</a>)，其中readonly大大提到我们可以使用ReplicationDriver来从connection层把read或者write操作分开。这确实是一个比较好的方案，在那个帖子讨论后不久，我就在自己的机器上搭了一个mysql的master-slave模式，然后使用ReplicationDriver来控制读写访问不同的机器，测试通过了，事隔几个月之后，我准备把它用于生产环境中，但是问题来了，因为我的应用访问的数据库有多个，主要访问的库是master-slave模式，其他辅助库是就是指定的一台机器，这时候问题来了。<br /><br />	Mysql的文档是这么写的：<span style="color: red">ReplicationDriver does not currently work with java.sql.DriverManager -based connection creation unless it is the only MySQL JDBC driver registered with the DriverManager </span>. DriverManager是一个单例模式，一个DriverManager只能注册一个ReplicationDriver驱动，也就是说ReplicationDriver和Driver两个类不能同时使用，郁闷，及其郁闷，由于我之前没有仔细看这段说明，所以没有预料到这种情况。摆在前面的路有几条<br /><br />一，使用多个datasource解决问题，<br />二，所有得datasource都使用这个驱动，但是这样做有一个缺点，在文章后面我会详细阐述这种做法得缺点。<br />三，扩展再扩展，hack再hack。<br />四，这种方案是第二种方案的补充，详见后文。<br /><br />	首先，我们来看一下ReplicationDriver的官方使用教程：<br /><pre name="code" class="java">public static void main(String[] args) throws Exception {
    ReplicationDriver driver = new ReplicationDriver();

    Properties props = new Properties();

    // We want this for failover on the slaves
    props.put("autoReconnect", "true");

    // We want to load balance between the slaves
    props.put("roundRobinLoadBalance", "true");

    props.put("user", "foo");
    props.put("password", "bar");

    //
    // Looks like a normal MySQL JDBC url, with a
    // comma-separated list of hosts, the first 
    // being the 'master', the rest being any number
    // of slaves that the driver will load balance against
    //

    Connection conn =
        driver.connect("jdbc:mysql://master,slave1,slave2,slave3/test",
            props);

    //
    // Perform read/write work on the master
    // by setting the read-only flag to "false"
    //

  //这个节点应该是通过spring的事务管理来设置，同时这个conn对象应该不是一个真正的connection，
	    //而是一个代理类，通过设置readonly，代理类会去使用不同的connection，
	    //那么问题是它该代理类使用的connection是哪里取的，抑或说难道它每次都会新开一个connection？，需要看源代码
	    
conn.setReadOnly(false);

    conn.setAutoCommit(false);
    conn.createStatement().executeUpdate("UPDATE some_table ....");
    conn.commit();

    //
    // Now, do a query from a slave, the driver automatically picks one
    // from the list
    //

    conn.setReadOnly(true);

    ResultSet rs = 
      conn.createStatement().executeQuery("SELECT a,b FROM alt_table");

     .......
  }</pre><br />这个示例看上去非常之简单，我们可以很容易的就通过ReplicationDriver拿到了一个Connection，首先，对于我们来说，conn.setReadOnly对我们来说这个方法应该是通过spring的事务管理来设置，同时这个conn对象应该不是一个真正的connection，而是一个代理类，通过设置readonly，代理类会去使用不同的connection，那么问题是它该代理类使用的connection是哪里取的，抑或说难道它每次都会新开一个connection？，这就需要看源代码<br /><br />	那么现在我们要弄清楚ReplicationDriver是怎么回事，反编译之后我们看到：<br /><pre name="code" class="java">public ReplicationDriver() throws SQLException {
	}

	static {
		try {
			DriverManager.registerDriver(new NonRegisteringReplicationDriver());
		} catch (SQLException E) {
			throw new RuntimeException("Can't register driver!");
		}
	}
</pre><br />看来看去，这个类中没有什么东西，那么再看看NonRegisteringReplicationDriver类吧。如下面的代码所示，这个类中主要就是这个方法connect方法<br /><br /><pre name="code" class="java">public Connection connect(String url, Properties info) throws SQLException {
		Properties parsedProps = parseURL(url, info);
		if (parsedProps == null) {
			return null;
		}
		Properties masterProps = (Properties) parsedProps.clone();
		Properties slavesProps = (Properties) parsedProps.clone();
		slavesProps.setProperty("com.mysql.jdbc.ReplicationConnection.isSlave",
				"true");
		
		String hostValues = parsedProps.getProperty("HOST");
		
		if (hostValues != null) {
			StringTokenizer st = new StringTokenizer(hostValues, ",");
			StringBuffer masterHost = new StringBuffer();
			StringBuffer slaveHosts = new StringBuffer();
			if (st.hasMoreTokens()) {
				String hostPortPair[] = parseHostPortPair(st.nextToken());
				if (hostPortPair[0] != null) {
					masterHost.append(hostPortPair[0]);
				}
				if (hostPortPair[1] != null) {
					masterHost.append(":");
					masterHost.append(hostPortPair[1]);
				}
			}
			boolean firstSlaveHost = true;
			do {
				if (!st.hasMoreTokens()) {
					break;
				}
				String hostPortPair[] = parseHostPortPair(st.nextToken());
				if (!firstSlaveHost) {
					slaveHosts.append(",");
				} else {
					firstSlaveHost = false;
				}
				if (hostPortPair[0] != null) {
					slaveHosts.append(hostPortPair[0]);
				}
				if (hostPortPair[1] != null) {
					slaveHosts.append(":");
					slaveHosts.append(hostPortPair[1]);
				}
			} while (true);
			if (slaveHosts.length() == 0) {
				throw SQLError
						.createSQLException(
								"Must specify at least one slave host to connect to for master/slave replication "
										+ "load-balancing functionality",
								"01S00");
			}
			masterProps.setProperty("HOST", masterHost.toString());
			slavesProps.setProperty("HOST", slaveHosts.toString());
		}
		return new ReplicationConnection(masterProps, slavesProps);
	}
</pre><br />上面这个方法也很简单，就是解析url后，然后访问确定master和slave机器一些properties的配置。越来越接近真相了，继续往下看，让我们掀起ReplicationConnection的头盖来：<br />先看构造方法：<br /><pre name="code" class="java">public ReplicationConnection(Properties masterProperties,
			Properties slaveProperties) throws SQLException {
		Driver driver = new Driver();
		
		StringBuffer masterUrl = new StringBuffer("jdbc:mysql://");
		StringBuffer slaveUrl = new StringBuffer("jdbc:mysql://");
		String masterHost = masterProperties.getProperty("HOST");
		if (masterHost != null) {
			masterUrl.append(masterHost);
		}
		String slaveHost = slaveProperties.getProperty("HOST");
		if (slaveHost != null) {
			slaveUrl.append(slaveHost);
		}
		String masterDb = masterProperties.getProperty("DBNAME");
		masterUrl.append("/");
		if (masterDb != null) {
			masterUrl.append(masterDb);
		}
		String slaveDb = slaveProperties.getProperty("DBNAME");
		slaveUrl.append("/");
		if (slaveDb != null) {
			slaveUrl.append(slaveDb);
		}

//从这里可以看出，笔者前文提出的猜想是正确的，每一个ReplicationDriver其实是两个Connection的代理，这两个
		//Connection才是真正访问DB的connection。		masterConnection = (com.mysql.jdbc.Connection) driver.connect(masterUrl
				.toString(), masterProperties);
		slavesConnection = (com.mysql.jdbc.Connection) driver.connect(slaveUrl
				.toString(), slaveProperties);
		currentConnection = masterConnection;
	}
</pre><br />这个构造方法没有任何的玄机，从这里也可以看出，那么前文提出的猜想是正确的，每一个ReplicationDriver其实是两个Connection的代理，这两个Connection才是真正访问DB的connection。好了，看到这里看客们大概也看出来了，当调用connection.setReadonly的时候，其实就是把需要的masterConnection或者slavesConnection赋值给当前的currentConnection，ReplicationDriver就是这么个实现，原理也非常简单，那么怎么解决文章中开头提出的那个问题呢。<br /><br />第一种方案：<br />	改成多个datasource，这种方式是最简单，最粗鲁的，然后我们就可以看到一堆有一堆，一坨又一坨的datasource，然后你还有一堆堆一坨坨的JdbcTemplate，HibernateTemplate，SqlMapClientTemplate，等等。<br /><br />第二种方案：<br />	第二种方案是所有的驱动都是用ReplicationDriver，有同学问：那怎么行呢，因为我又的datasource不是master-slave模式的。还好，没有什么关系，即使是这样配置jdbc:mysql://192.168.1.1:3306,192.168.1.1:3306/xx也是没有关系的，带来的结果就是一个ReplicationDriver其实hold了两个connection，而这两个connection其实是连着同一个数据库。那么也就是说如果连接池里配置了50个connection，那么实际上却有100个connection连着数据库，这种事情还是比较让人郁闷的。<br /><br /><br />第三种方案：<br />看来看去，问题都出现在DriverManager上，如果我新建一个DriverManager，行否，于是新建一个类，名约ReplicationDriverManager。这样系统中就有两个DriverManager了，普通的DriverManager注册的驱动为Driver.java，ReplicationDriverManager注册的驱动为ReplicationDriver。大家互不干扰，貌似可行。粗略的看了一下代码，也是可以实现的，关键在于需要扩展连接池（至少c3p0是这样的，需要重写c3p0的两个类），然后还需要重写一个ReplicationDriver，将静态块中的DriverManager换成我们自己的DriverManager。然后还需要重写ReplicationConnection，Driver类等等，也是非常麻烦的。<br /><br />想来想去，想破了头了，终于，还是有点头绪，就是在第二种方案的基础上，再次修改ReplicationConnection，也就是说，如果我的配置为jdbc:mysql://192.168.1.1:3306/xx，那么我强行把currentConnection设置为masterConnection，这样ReplicationConnection中的slavesConnection就一直是空着的，或者masterConnection和slavesConnection还有currentConnection这3个引用都指向同一个对象，那么连接池中配置50个连接，那么就是50个连接，不会变成100个连接了，而其他的master-slave模式的配置依旧，这个方式貌似看上去还是不错的。我们看看代码怎么写：<br />首先，来一个EasyReplicationDriver，代码如下：<br /><pre name="code" class="java">public class EasyReplicationDriver extends EasyNonRegisteringReplicationDriver
		implements Driver {

	public EasyReplicationDriver() throws SQLException {
	}

	static {
		try {
			DriverManager.registerDriver(new EasyNonRegisteringReplicationDriver());
		} catch (SQLException E) {
			throw new RuntimeException("Can't register driver!");
		}
	}
}</pre><br /><br />接着再来一个EasyNonRegisteringReplicationDriver，如下：<br /><pre name="code" class="java">/**
 * @author ahuaxuan(aaron zhang)
 * @since 2008-6-18
 * @version $Id$

 */

public class EasyNonRegisteringReplicationDriver extends NonRegisteringDriver {

	public EasyNonRegisteringReplicationDriver() throws SQLException {
	}

	public Connection connect(String url, Properties info) throws SQLException {
		Properties parsedProps = parseURL(url, info);
		if (parsedProps == null) {
			return null;
		}
		Properties masterProps = (Properties) parsedProps.clone();
		Properties slavesProps = (Properties) parsedProps.clone();
		slavesProps.setProperty("com.mysql.jdbc.ReplicationConnection.isSlave",
				"true");
		
		String hostValues = parsedProps.getProperty("HOST");
		
		if (hostValues != null) {
			StringTokenizer st = new StringTokenizer(hostValues, ",");
			StringBuffer masterHost = new StringBuffer();
			StringBuffer slaveHosts = new StringBuffer();
			if (st.hasMoreTokens()) {
				String hostPortPair[] = parseHostPortPair(st.nextToken());
				if (hostPortPair[0] != null) {
					masterHost.append(hostPortPair[0]);
				}
				if (hostPortPair[1] != null) {
					masterHost.append(":");
					masterHost.append(hostPortPair[1]);
				}
			}
			boolean firstSlaveHost = true;
			do {
				if (!st.hasMoreTokens()) {
					break;
				}
				String hostPortPair[] = parseHostPortPair(st.nextToken());
				if (!firstSlaveHost) {
					slaveHosts.append(",");
				} else {
					firstSlaveHost = false;
				}
				if (hostPortPair[0] != null) {
					slaveHosts.append(hostPortPair[0]);
				}
				if (hostPortPair[1] != null) {
					slaveHosts.append(":");
					slaveHosts.append(hostPortPair[1]);
				}
			} while (true);
			/*if (slaveHosts.length() == 0) {
				throw SQLError
						.createSQLException(
								"Must specify at least one slave host to connect to for master/slave replication "
										+ "load-balancing functionality",
								"01S00");
			}*/
			masterProps.setProperty("HOST", masterHost.toString());
			slavesProps.setProperty("HOST", slaveHosts.toString());
		}
		return new EasyReplicationConnection(masterProps, slavesProps);
	}</pre><br />注意上面我注释掉的这段代码，如果我们想要ReplicationDriver支持jdbc:mysql://192.168.1.1:3306/xxx,那么就必须把上面那段代码注释掉。<br />第3步，让我们看看EasyReplicationConnection这个类：<br /><pre name="code" class="java">public EasyReplicationConnection(Properties masterProperties,
			Properties slaveProperties) throws SQLException {
		Driver driver = new Driver();
		
		StringBuffer masterUrl = new StringBuffer("jdbc:mysql://");
		StringBuffer slaveUrl = new StringBuffer("jdbc:mysql://");
		String masterHost = masterProperties.getProperty("HOST");
		if (masterHost != null) {
			masterUrl.append(masterHost);
		}
		String slaveHost = slaveProperties.getProperty("HOST");
		if (slaveHost != null) {
			slaveUrl.append(slaveHost);
		}
		String masterDb = masterProperties.getProperty("DBNAME");
		masterUrl.append("/");
		if (masterDb != null) {
			masterUrl.append(masterDb);
		}
		String slaveDb = slaveProperties.getProperty("DBNAME");
		slaveUrl.append("/");
		if (slaveDb != null) {
			slaveUrl.append(slaveDb);
		}
		
		//从这里可以看出，笔者前文提出的猜想是正确的，每一个ReplicationDriver其实是两个Connection的代理，这两个
		//Connection才是真正访问DB的connection。
		masterConnection = (com.mysql.jdbc.Connection) driver.connect(masterUrl
				.toString(), masterProperties);
		
		if (slaveUrl.toString().contains("///")) {
			if (logger.isDebugEnabled()) {
				logger.debug(" ----- the salveUrl contains the '///', " +
						"that means there is no slaver, make slavesConnection = masterConnection --");
			}
			slavesConnection = masterConnection;
		} else {
			slavesConnection = (com.mysql.jdbc.Connection) driver.connect(slaveUrl
					.toString(), slaveProperties);
		}</pre><br />主要就是加了一个判断，一旦路径中出现///，那么就证明没有slave机器，那么就可以把masterConnection赋值给slavesConnection了。这样一来就ok了。<br /><br />经过3个类的改写之后，终于，我们可以使用ReplicationDriver的功能了，看来看去还是这种方式最美好。<br /><br />不过由于ahuaxuan的水平所限，可能在以上的方案中有其没有发现的问题，抑或有更好的方案，希望大家不吝赐教。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/205926#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 19 Jun 2008 18:23:29 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/205926</link>
        <guid>http://ahuaxuan.javaeye.com/blog/205926</guid>
      </item>
      <item>
        <title>喝茶对程序员的意义</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/205048" style="color:red;">http://ahuaxuan.javaeye.com/blog/205048</a>&nbsp;
          发表时间: 2008年06月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1、正常人一天宜饮多少茶？ <br />饮茶量的多少决定于饮茶习惯、年龄、健康状况、生活环境、风俗等因素。一般健康的成年人，平时又有 <br /><br />饮茶习惯的，一日饮茶12克左右，分3-4次冲泡是适宜的。对于体力劳动量大、消耗多、进食量也大的人，尤其 <br /><br />是高温环境、接触毒害物质较多的人，一日饮茶20克左右也是适宜的。油腻食物较多、烟酒量大的人也可适当 <br /><br />增加茶叶用量。孕妇和儿童、神经衰弱者、心动过速者，饮茶量应适当减少。 <br /><br />2、茶叶化学成分及其对人体的保健作用 <br />茶叶的化学成分是由3.5-7.0% 的无机物和93～96.5%的有机物组成。茶叶中的无机矿质元素约有27种，包 <br /><br />括磷、钾、硫、镁、锰、氟、铝、钙、钠、铁、铜、锌、硒等多种。茶叶中的有机化合物主要有蛋白质、脂质 <br /><br />、碳水化合物、氨基酸、生物碱、茶多酚、有机酸、色素、香气成分、维生素、皂苷、甾醇等。 <br />茶叶中含有20-30% 的叶蛋白，但能溶于茶汤的只有3.5%左右。茶叶中含有1.5-4% 的游离氨基酸，种类达 <br /><br />20多种，大多是人体必需的氨基酸。茶叶中含有25-30% 的碳水化合物，但能溶于茶汤的只有3-4%。茶叶中含有 <br /><br />4-5%的脂质，也是人体必需的。 <br /><br />3、家中有多种茶叶，如何安排饮用? <br />有些人在一天之中，不同时间饮用不同的茶叶，清晨喝一杯淡淡的高级绿茶，醒脑清心；上午喝一杯茉莉 <br /><br />花茶，芬芳怡人，可提高工作效率；午后喝一杯红茶，解困提神；下午工间休息时喝一杯牛奶红茶或喝一杯高 <br /><br />级绿茶加点点心、果品，补充营养；晚上可以找几位朋友或家人团聚一起，泡上一壶乌龙茶，边谈心边喝茶， <br /><br />别有一番情趣。这种一日饮茶巧安排，你如果有兴趣，不妨也可试一试。 <br /><br />4、如何调制牛奶红茶？ <br />很多年轻人喜欢饮用美味可口的牛奶红茶，冲泡配制方法是，先将适量红茶放入茶壶中，茶叶用量比清饮 <br /><br />稍多些，然后冲入热开水,约5分钟后，从壶咀倒出茶汤放在咖啡杯中；如果是红茶袋泡茶，可将一袋茶连袋放 <br /><br />在咖啡杯中，用热开水冲泡5分钟，弃去茶袋。 然后往茶杯中加入适量牛奶和方糖，牛奶用量以调制成的奶茶 <br /><br />呈桔红、黄红色为度。奶量过多，汤色灰白，茶香味淡薄，奶量过少，失去奶茶风味，糖的用量因人而易，以 <br /><br />适口为度。 <br /><br />5、为什么有人喜欢用枸杞子、西洋参、白菊花、桔皮、薄荷等加在茶叶中泡茶喝? <br />用枸杞子泡茶，有滋补抗衰的作用。《本草经疏》对枸杞子之功效作过较全面的论述：“枸杞子，润而滋 <br /><br />补，兼能退热，而专于补肾、润肺、生津、益气，为肝肾真阴不足、劳乏内热补益之要药。老人阴虚十之七八 <br /><br />，故服食多为益精明目之上品”。枸杞子泡茶喝，不但对肝肾阴虚所致的头晕目眩、视力减退、腰膝酸软、遗 <br /><br />精等久服甚效，而且对高血脂、高血压、动脉硬化、糖尿病等也有一定的疗效。 <br />用西洋参片泡茶喝，可以利用西洋参味甘辛凉的性质，调整茶味，而且西洋参补阴虚效果甚佳。这种西洋 <br /><br />参茶常有良好的益肺养胃、滋阴津、清虚火、去低热的功效。 <br />用白菊花泡茶喝，可发挥白菊花平肝潜阳、疏风清热、凉血明目的功效，而且白菊花清香味甘，泡茶喝可增进 <br /><br />茶汤香味，适口性好。 <br />用桔皮泡茶喝，可以利用桔皮宽中理气、消痰止咳的功效，桔皮泡绿茶，可去热解痰、抗菌消炎，故咳嗽 <br /><br />多痰者饮之有益。 <br />用薄荷泡茶喝，可以利用薄荷中薄荷醇、薄荷酮的疏风清热作用，而且泡茶喝之有清凉感，是清热利尿的 <br /><br />良药。 <br /><br />6、孕妇、儿童能否喝茶? <br />孕妇、儿童一般都不宜喝浓茶，因过浓的茶水中过量的咖啡因会使孕妇心动过速，对胎儿也会带来过分的 <br /><br />刺激，儿童也是如此。因此一般主张孕妇、儿童宜饮淡茶，通过饮些淡茶，可以补充一些维生素和钾、锌等矿 <br /><br />物质营养成分。儿童适量饮茶，可加强胃肠蠕动，帮助消化；饮茶有清热降火之功效，避免儿童大便干结造成 <br /><br />肛裂。另外，儿童饮茶或用茶水漱口还可以预防龋齿。 <br /><br />7、如何用茶止痢? <br />茶叶中的茶多酚类物质对多种病菌有杀灭或抑制的功效，因此可以用茶叶来治疗细菌性痢疾。做法是先将 <br /><br />绿茶研成粉末， 将3克茶叶末用温茶水送服后，继续饮用些茶水，一日三次，以获得较好的疗效。 <br /><br />8、用茶水洗脸、洗脚、洗头、洗澡、漱口有什么好处? <br />用茶水洗脸、洗澡，可减少皮肤病的发生，而且可以使皮肤光泽、滑润、柔软。用纱布蘸茶水敷在眼部黑 <br /><br />圈处，每日1-2次，每次20-30分钟，可以消除黑眼圈。用茶水连渣洗手洗脚，可以防治皲裂，并能防治湿疹、 <br /><br />止痒，减轻汗脚的脚臭。用茶水洗头，可以使头发乌黑柔软、光泽美观；用茶水刷眉，可使眉毛变得浓密光亮 <br /><br />，用茶漱口，可以消除口臭，有利于保护牙齿，防治口腔疾病。 <br /><br />9、吃过盐渍蔬菜和腌腊肉制品为什么要多喝茶? <br />盐渍蔬菜如泡菜、腌咸菜和腌腊肉制品如腌肉、腊肉、火腿、腊肠等，常含有较多的硝酸盐，食物中在有 <br /><br />二级胺同时存在的情况下，硝酸盐和二级胺可以发生化学反应而产生亚硝胺，亚硝胺是一种危险的致癌物质， <br /><br />极易引起细胞突变而致癌。茶叶中的儿茶素类物质，具有阻断亚硝胺合成的作用，因此食用了盐渍蔬菜和腌腊 <br /><br />肉制品以后，应多饮些儿茶素含量较高的高级绿茶，可以抑制致癌物的形成，而且能增强免疫功能，有益于健 <br /><br />康。 <br /><br />10、为什么糖尿病患者宜多饮茶? <br />糖尿病患者的病征是血糖高，口渴，乏力。实验表明，饮茶可以有效地降低血糖，且有止渴、增强体力的 <br /><br />功效。糖尿病患者一般宜饮绿茶，饮茶量可稍增多一些，一日内可数次泡饮，使茶叶的有效成分在体内保持足 <br /><br />够的浓度。饮茶的同时，可以吃些番瓜食品，这样有增效作用。一个月为一疗程，通常可以取得很好的疗效。 <br /><br />11、喝什么茶更能减肥? <br />就纯茶而言，一般经验认为，喝乌龙茶、沱茶、普洱茶和砖茶等紧压茶更有利于降脂减肥。至于保健茶， <br /><br />目前市场上供应的多种减肥茶，如宁红减肥茶、健美减肥茶、七珠健美茶、上海健茶等，都是以茶叶为基础配 <br /><br />以决明子、山楂、荷叶等多种中草药，包装成袋泡茶。饮用方便，疗效因人而异，各有一定的适应症。 <br /><br />12、喝红茶 好处多 <br />红茶是经过发酵烘制而成的，发酵时茶叶在氧化酶的作用下变成红色氧化物，成为红茶。红茶品性温和， <br /><br />味道醇厚，除含多种水溶性维生素外，还富含微量元素钾，当冲泡后70%的钾可溶于茶水内。钾有增强心脏血液 <br /><br />循环的作用，并能减少钙在体内的消耗。因红茶中所含的锰是骨结构不可缺少的元素之一，因而常喝红茶对骨 <br /><br />骼强健也有益处。 国外有资料报道，经常饮红茶还有防治流感、中风及皮肤癌的效果。 <br />研究表明，由于红茶中含有一种类黄酮化合物，其作用类似于抗氧化剂，能防止中风和心脏病。美国一项 <br /><br />最新研究显示：每天喝一杯红茶的人与不喝茶的相比，前者罹患心脏病的风险要比后者低40%以上。 <br /><br />13、饭后不宜饮茶 <br />饭后立即饮茶，势必冲淡胃液，影响食物消化，同时茶中的单宁酸能使食物中的凝固物质，给胃增加负担 <br /><br />，并影响蛋白质的吸收。饮茶最好是在饭后一小时。 <br /><br />14、饮茶可减轻吸烟的危害 <br />据世界卫生组织、英国帝国癌症研究基金会和美国癌症协会共同完成的一份关于吸烟的调查报告说，目前 <br /><br />全世界每10秒钟就有1人死于吸烟所引起的疾病，每年全世界至少有315万人因吸烟而丧生，而且这个数字还在 <br /><br />增加；而中国吸烟人数已高达3亿多，每年消耗香烟1万5干多亿支，占全世界消耗总量的30％以上，中国的中年 <br /><br />男人70％吸烟，每分钟就有1人死于吸烟。若中国烟民人数还在增加的话，预计每分钟将有5人死于吸烟。这对 <br /><br />吸烟者来说，无疑是一个值得警惕的信号。从健康角度考虑，戒烟势在必行。而对那些一时还难以戒掉烟痛的 <br /><br />吸烟者来说，饮茶则是减轻吸烟危害的最好方法。因为茶叶中的茶多酚、维生素C等成分对香烟中所含有的各 <br /><br />种有害物质有降解作用，边饮茶边吸烟，毒素可随饮茶不断解除，通过粪便排出体外。吸烟者常饮茶，主要有 <br /><br />四大好处： <br />一是可以减轻吸烟诱发癌症的可能性。香烟的烟雾里含有4千多种化学物质，其中50种以上化学物质属于致 <br /><br />癌物质，而且经过呼吸道吸收又最有利于这些香烟中致癌物质在全身扩散。长期吸烟不仅可以导致肺癌，还可 <br /><br />能得食道癌、喉癌、胰腺癌、肾癌。膀胱癌等各种癌症，尤其是肺癌患者中吸烟者要占80％至99.5％。美国休 <br /><br />斯敦安德森癌症中心的科研人员从分子角度阐明了吸烟与肺癌的关系，指出吸烟引起的基团变异是导致肺瘤的 <br /><br />直接原因。饮茶有防癌抗癌作用。茶叶中的茶多酚能抑制自由基的释放，控制癌细胞的增殖。自由基是人体在 <br /><br />呼吸代谢过程中，在消耗氧的同时产生的一组有害“垃圾”。它几乎存在于人体的每一个细胞之中，是人体的 <br /><br />一大隐患和“定时炸弹”。研究表明，自由基也是造成基因变异、致癌的重要原因。一般情况下人的机体是处 <br /><br />于自由基不断产生和不断消除的动态平衡之中。值得指出的是，香烟是自由基发生剂，据测定，人们每吸一日 <br /><br />烟就可产生10的17次方个自由基，吸烟会破坏这种动态平衡。自由基产生过多，人体致癌的可能性也加大了。 <br /><br />茶叶中茶多酚的主体儿茶素类物质是一种抗氧化剂，也是一种自由基强抑制剂．它可以抑制由于吸烟引起的肿 <br /><br />瘤发生。绿茶中的茶多酚清除自由基的能力较强，它们对超氧阴离子自由基具有很强的清除效应。中国预防医 <br /><br />学科学院营养与食品卫生研究所在研究了145种茶叶后证实，茶叶确有阻断人体内亚硝胺合成的能力。南京中山 <br /><br />肿瘤研究所阎玉森经试验发现，茶多酚进入人体后能与致癌物结合，使其分解，降低致癌活性，从而抑制致癌 <br /><br />细胞的生长。美国泊杜大学从事食品与营养研究的多罗西•莫尔说：“我们的研究表明，绿茶的叶富含抗癌物 <br /><br />质，其浓度相当高，足以在体内产生抗癌作用。”在癌症研究方面积有四十年丰富经验的美国健康基金会名誉 <br /><br />会长约翰•韦斯柏格博士说：“我的研究结果表明，如果你每天喝6杯茶，就可以不得癌症”。美国哥伦比亚大 <br /><br />学哈菜姆医学中心的研究人员研究结果也显示，绿茶具有抗癌功效。因此吸烟者饮茶有助于减少癌症的发生。 <br />二是可以有助于减轻由于吸烟所引起的辐射污染。据美国马萨诸塞大学医疗中心的约瑟夫•迪法兰赞博士 <br /><br />估计，每天吸30支烟的人，他的肺部在一年内得到香烟中放射性物质的辐射量相当于他的皮肤在胸腔X光机上 <br /><br />透视了大约300次。而饮茶能有效地阻止放射性物质侵入骨髓并可使锶90和钻60迅速排出体外，茶叶中的儿茶素 <br /><br />类物质和脂多糖物质可减轻辐射对人体的危害，对造血功能有显著的保护作用。用茶叶片剂治疗由于放射引起 <br /><br />的轻度辐射病的临床试验表明，其总有效率可达90％。 <br />三是可以防治由于吸烟而促发的白内障。科学研究发现，吸烟正成为危害眼睛健康的大敌，会促发白内障 <br /><br />。美国哈佛大学医学院研究人员发现，与那些从不吸烟的人相比，每天吸20支以上香烟的人，患白内障的可能 <br /><br />性是不吸烟人的2倍，吸烟量越大，患白内障的可能性也就越大。在我国不明原因的失明者中就有4%的人是因为 <br /><br />吸烟引起的。加拿大科学家却发现，多饮茶可以防止白内障。他们认为，白内障是由于人体内氧化反应产生的 <br /><br />自由基作用于眼球的晶状体所致，而茶叶中的茶多酚分解产生的具有抗氧化作用的代谢物可以阻止体内产生自 <br /><br />由基的氧化反应的发生。另外，美国农业部营养与衰老研究中心的科学家们最近发现，白内障的发病率与人体 <br /><br />血浆中胡萝卜素含量高低及浓度大小关系密切。凡是白内障患者，其血浆中胡萝卜素浓度往往很低，且发病率 <br /><br />比正常人高3～4倍。茶叶中含有比一般蔬菜和水果都高得多的胡萝卜素。胡萝卜素不仅有防止白内障、保护眼 <br /><br />睛的作用，同时还有防癌抗癌、抗尼古丁、解烟毒的作用。吸烟者饮茶对保护视力是有好处的。 <br />四是可以补充由于吸烟所消耗掉的维生素C。因为吸烟可促使人体血清中的维生素C与烟雾中的一氧化碳 <br /><br />、亚硝胺、尼古丁、甲醛等氧化致癌物结合，进而转变为无毒化合物或非突变物质排出体外，使得维生素C含量 <br /><br />大大减少，导致人体内的垃圾——自由基的大量堆积，给人体留下了隐患，加剧了自由基对各种正常细胞的损 <br /><br />伤作用。比如吸入尼古丁等有害物质，使细胞中氧自由基浓度增加。氧自由基对人体细胞有侵害作用，极易引 <br /><br />起癌变反应，美国的研究人员发现，经常补充一定剂量的维生素C则可避免吸烟所带来的这种危害。因为维生 <br /><br />素C具有抗氧化作用，可抑制氧自由基的生成，使人体细胞免受侵害。茶叶中维生素C的含量较丰富，尤其是 <br /><br />绿茶，在正常情况下，茶叶中维生素C的浸出率可以达到80％左右，茶汤中的维生素C在摄氏90的温度下也很少 <br /><br />被破坏。吸烟者饮茶可以摄取到适量的维生素C，特别是坚持饮绿茶，完全可以补充由于吸烟造成的维生素C的 <br /><br />不足，以保持人体内产生和清除自由基的动态平衡，增强人体的抵抗能力。 <br />总而言之，虽然饮茶对吸烟者有一定的好处，但本文的目的绝非是鼓励人们去吸烟，更不是因为饮茶可缓 <br /><br />解吸烟的危害而可以肆无忌惮地去吸烟。恰恰相反，由于吸烟所带来的危害无论是对个人还是对社会都是巨大 <br /><br />而惨痛的，因此戒烟是大势所趋，是明智之举。饮茶只能作为戒烟过程中的一项补救措施而已，以尽可能减少 <br /><br />吸烟的危害。为了您的健康，彻底戒烟才是我们的最终目的。何况不吸烟者饮茶更是好处多多呢！ <br /><br />15、常饮绿茶对胎儿有利 <br />现代医学研究已经证明，锌对人体有着极为重要的作用。儿童缺锌会影响生长发育；孕妇缺锌，则会影响 <br /><br />胎儿发育，甚至有可能发先天性畸形儿。所以，孕妇应多食用含锌食品。 <br />日常生活中的含锌饮食品有绿茶、可可粉、芸麻及紫菜等，其中尤以绿茶的含锌量为最多，每天饮用3—5克 <br /><br />普通绿茶所冲泡的2—3杯茶水，即可满足人体对锌的要求。一项医学研究调查表明，饮茶孕妇所生婴儿的血 <br /><br />中含锌量也较高。 <br />值得注意的是，饮茶必须适量，过量饮茶会影响铁的吸收。孕妇饮茶最好以80℃—85℃左右的温开水随泡 <br /><br />随饮，不要冲泡过度或放置过久，且每次不易过浓，分次多饮为宜。 <br /><br />16、茶的药用实例 <br />糖茶：绿茶、白糖适量，开水冲泡，片刻饮之。有和胃补中益气之功… … <br />菊花茶：绿茶、白菊花（干）适量，开水冲泡，待凉饮之。有清肝明目之功。主治肝经风热头痛、目赤肿 <br /><br />痛和高血压等症。 <br />山楂茶：山楂适量，捣碎，加水煎煮至一一杯，再加入茶叶适量，长期饮用，有降脂、减肥的功效，对高 <br /><br />血压、冠心病及肥胖症也有一定疗效。 <br />松萝茶：是我国著名的药用茶。『本经逢源』记述：徽州松萝，专于化食”。有消积滞，祛油腻、清火、 <br /><br />下气、降痰之功效，久饮还可治顽疮及坏血症。 <br />醋茶：将茶泡好后，去掉茶叶，按茶水和醋5：2的比例配制。每日饮用2～3次，可治暑天腹泻、痢疾，并 <br /><br />有解酒和疗酒醉的作用。 <br />盐茶：茶叶里放点食盐，用开水冲泡后饮之。有明目消炎、降火化痰之功效。同时可治牙痛 、感冒咳嗽、 <br /><br />目赤肿痛等症。夏天常饮，还可防中暑。 <br />姜茶：茶叶少许，生姜几片去皮水煎，饭后饮服。可发汗解表，温肺止咳，对流感、伤寒、咳嗽等疗效显 <br /><br />柿茶：柿饼适量煮烂，加入冰糖，茶叶适量，再煮沸，配成茶水饮之，有理气化痰、益肠健胃的功效，它最适 <br /><br />于肺结核患者饮用。 <br />奶茶：先用牛奶和白糖煮沸，然后按1份牛奶、2份茶汁配好，再用开水冲服。有减肥健脾、提神明目之功 <br /><br />效。 <br />蜂蜜茶：茶叶适量放人小布袋内。放人茶杯冲人开水，再加入适量蜂蜜。饮此茶有止渴养血、润肺益肾之 <br /><br />功能，并能治便秘、脾胃不和、咽炎等症。 <br />莲茶：湘莲30克，先用温水浸泡5小时后沥于，加红糖30克，水适量，同煮至烂，饮用时加入茶汁。有健脾 <br /><br />益肾之功。肾炎、水肿患者宜天天服用。 <br />枣茶：茶叶5克，开水冲泡3分钟后，加l0粒红枣捣烂的枣泥。有健脾补虚作用，尤其适用于小儿夜尿，不 <br /><br />思饮食。 <br />银茶：茶叶2克，金银花1克，开水冲泡后饮服，可清热解毒，防暑止渴，对暑天发热、疖痛、肠炎有效。 <br /><br />17、红茶亦可抗癌 <br />绿茶可诱导癌细胞“自杀”已被多家实验所确认。那么，经过加工制作的红茶，其中的茶多酚大多被氧化 <br /><br />了，是否还有抗癌活性呢？湖南医科大学茶与健康研究室的一项专题研究证实，红茶同样具有很强的抗癌功能 <br /><br />。 <br />绿茶是用高温破坏鲜茶叶中的酶、制止发酵而制成的，沏出来的茶保持鲜茶叶原有的绿色，甘香可口，亚 <br /><br />洲人喜爱喝。而红茶是经全发酵加工制作的，色泽乌黑油润，沏出的茶色红艳，具有特别的香气和滋味，为英 <br /><br />、美、加拿大等国人喜好。传统的观点认为，绿茶中的多酚类化合物是抗癌的主要活性成份，但茶多酚经过加 <br /><br />工后大多被氧化而形成了多酚类氧化产物茶色素（国外称红茶多酚）。红茶的抗癌活性是否因此就降低了呢？ <br /><br />1995年，世界粮农组织发起了红茶对人体健康作用的研究，由红茶消费国英、美、加拿大及红茶主要生产国肯 <br /><br />尼亚、印尼、斯里兰卡组成的茶叶贸易健康研究协会，委托美国康奈尔大学医学院、波士顿大学医学院、纽约 <br /><br />州立大学医学院等研究机构，重点开展了红茶对癌症影响的研究。1996年他们在茶色素对某些癌症的化学预防 <br /><br />作用的研究中取得了肯定结果。此后各国争相开展了对红茶及其主要成份茶色素的药理研究。 <br /><br />1997年10月以来，湖南医科大学茶与健康研究室曹进、赵燕等，从分子生物学角度入手，观察了被茶色素 <br /><br />作用后的人急性早幼粒白血病细胞（HL—60）的生物化学和形态学变化，观察到人急性早幼粒白血病细胞均出 <br /><br />现了凋亡的典型改变：DNA出现梯形条带，细胞出现凋亡小体；流式细胞仪测定结果进一步表明，茶色素对癌细 <br /><br />胞的分化阻断效果主要发生在细胞增殖分化早期，即DNA合成前期。 <br /><br />18、荧屏面前常饮茶 <br />电视的诞生，给人们的生活带来了新的视听享受，世界上有大量的人每天都要面对电视屏幕度过许多时光 <br /><br />。但是在播放电视时，荧光屏会发出一些射线，这些射线对人体有害，尤其是长时间地看电视，能引起人们的 <br /><br />视觉疲劳和视力衰退，有试验表明，连续看电视四五个小时，人的视力会暂时减退30%。而对电脑工作者、监视 <br /><br />器工作人员等群体来说更是长时间地、近距离地面对银屏用眼，对人眼的损害不可小视。为了减轻这些射线对 <br /><br />眼睛和身体的影响，看电视时常喝杯茶，不失是个好办法。 <br />茶叶中含有多种维生素和一些微量元素，甚至比许多水果中的含量还多，对人体健康有许多好处。茶中的 <br /><br />维生素A，有利于恢复和防止视力衰退；维生素B2对眼睑、眼睛的结膜和角膜有保护作用，缺了它，常会引起流 <br /><br />泪、视力模糊；维生素C是眼睛晶状体中的重要营养成分，不足时会使晶状体受损，变得混浊；维生素D直接参 <br /><br />与眼视网膜的杆状细胞内视紫质的合成，以维持视觉的正常。微量元素锌则是维生素A在人体内运转的必需物质 <br /><br />。如果维生素D或者锌不足，会减弱眼睛的暗适应力和辨色能力。 <br />另外，茶中还含有β-胡萝卜素、钼、钙、脂多糖、茶多酚类物质，它们也有减轻视觉疲劳和防辐射的效用 <br /><br />。 <br />其实，屏幕射线对人体的损害还不仅仅是视力，而会对神经、免疫力、心血系统等都有不利影响，只是表 <br /><br />现等不似视力那么直接罢了。饮茶对减轻屏幕射线的危害很有益，最直接的一个作用就是饮茶能够增加排尿， <br /><br />将毒素排出，“净化”了身体环境。 <br />所以，当看电视时，坐在电脑面前，一杯清茶入口，既是享受，又能防病，何乐而不为? 小小茶叶，浑身 <br /><br />是宝，当是常备常用之物。 <br /><br />19、借茶解酒酒更浓 <br />酒逢知己千杯少，醉了方知乾坤大。人们兴致未尽时往往开怀畅饮，一醉方休。醉酒后许多人会喝上几杯 <br /><br />浓茶以解酒。其实，喝浓茶非但不能解酒，还如同火上浇油，正所谓：借茶解酒酒更浓。这是为什么呢? 酒， <br /><br />首先会直接损伤胃粘膜，导致胃炎、胃十二指肠球部溃疡，甚至发生胃出血。而浓茶对胃粘膜也会产生一定的 <br /><br />刺激性，诱发胃醛分泌，所以喝浓茶对酒后损伤胃粘膜起着推波助澜的作用。 <br />酒精能使血液流动加快，血管扩张，而且对心脏有很大的兴奋作用，使心跳加速。茶中的茶碱同样具有兴 <br /><br />奋心脏的作用，双管齐下，更加重了心脏的负担。 医学研究揭示，酒精在体内的代谢过程是这样的：当酒精被 <br /><br />消化道吸收之后，90%以上被肝脏的乙醇脱氢酶转化为乙醛，再被乙醛脱氢酶转化为乙酸，然后分解为二氧化碳 <br /><br />和水排出体外。但酒后饮茶时，茶中的茶碱会迅速通过肾脏，产生利尿作用。这时，酒精被转化为乙醛而尚未 <br /><br />被转化为乙酸，更未被转化为二氧化碳和水，就从肾脏排出。由于乙醛对肾脏有较大的刺激作用。因而会危害 <br /><br />健康。由此可见，酒后是不宜饮茶的。可喝点醋、果汁或糖水，吃些水果。 <br /><br />20、能用茶水服药吗? <br />能否用茶水服药，不能一概而论。在多数情况下不主张用茶水服药,尤其是某些含铁剂(硫酸亚铁、碳酸亚 <br /><br />铁、枸橼酸铁胺等)、含铝剂(如氢氧化铝等)、制剂(如蛋白质) 等西药遇到茶汤中多酚类物质会产生结合沉淀 <br /><br />，影响药效。有些中草药如麻黄、黄连等一般也不宜与茶水混饮。另外，茶叶中含有咖啡碱，具有兴奋作用， <br /><br />因此，服用镇静、催眠、镇咳类药物时，也不宜用茶水送服，避免药性冲突，降低药效。一般认为，服药后2小 <br /><br />时内不宜饮茶。 <br />然而，服用维生素类药物、兴奋剂、利尿剂、降血脂、降血糖药物时，一般可以用茶水送服。例如服用维 <br /><br />生素C后饮茶， 茶叶中的儿茶素可以有助于维生素C在人体内的吸收和积累；茶叶本身具有兴奋、利尿、降血脂 <br /><br />、降血糖等功效，服用这类药物时，茶水有增效作用。 <br /><br />21、家庭如何保管好茶叶? <br />买回的小包装茶，无论是复合薄膜袋装茶或是听罐包装茶，都必须放在能保持干燥的地方。如果是散装茶 <br /><br />，可用干净白纸包好，置于有干燥剂(如块状未潮解石灰)的罐、坛中，坛口盖密。如茶叶数量少而且很干燥， <br /><br />也可用二层防潮性能好的薄膜袋包装密封好，放在冰箱中，至少可保存半年基本不变质。总而言之，保存茶叶 <br /><br />的条件：一是要干燥，二是最好低温(5℃左右)。 <br /><br />22、神经衰弱者如何饮茶? <br />神经衰弱者的主要症状是夜晚不能入睡，白天无精打采没有精神。神经衰弱患者往往害怕饮茶，认饮茶后 <br /><br />，刺激神经，可能更加睡不着觉。实际上，从辨证施治的观点来看，要使夜晚能睡得香，必须在白天设法使其 <br /><br />达到精神振奋。因此，神经衰弱者在白天上、下午各饮一次茶，可以上午饮花茶，下午饮绿茶，达到振作精神 <br /><br />的目的，到了夜晚不再喝茶，稍看点书报就能安稳入睡。如能坚持数日至一周，必定会收到较好的效果。 <br /><br />23、心脏病、高血压病患者如何饮茶? <br />对于心动过速的病患者以及心、肾功能减退的病人，一般不宜喝浓茶，只能饮用些淡茶，一次饮用的茶水 <br /><br />量也不宜过多，以免加重心脏和肾脏的负担。对于心动过缓的心脏病患者和动脉粥样硬化和高血压初起的病人 <br /><br />，可以经常饮用些高档绿茶，这对促进血液循环、降低胆固醇、增加毛细血管弹性，增强血液抗凝性都有一定 <br /><br />好处的。 <br /><br />24、胃病患者如何饮茶? <br />胃病的种类很多，最常见的有浅表性胃炎、萎缩性胃炎、胃溃疡、胃出血等。胃病患者服药时一般不宜饮 <br /><br />茶，服药2小时后， 饮用些淡茶、糖红茶、牛乳红茶，有助于消炎和胃粘膜的保护，对溃疡也有一定疗效。饮 <br /><br />茶还可以阻断体内亚硝基化合物的合成，防治癌前病变。 <br /><br />25、喝茶会不会影响牙齿的洁白? <br />喝茶尤其是长期喝浓茶，茶叶中的多酚类氧化物附着于牙齿表面，如果不刷牙，确实会使牙齿逐步变黄， <br /><br />就像茶壶、茶杯长期不清洗结有一层“茶座”一样，如果喝浓茶加上有吸烟习惯的人，常会加剧牙齿的黄化， <br /><br />这是值得重视的问题。然而，一般饮茶者，只要不抽烟，注意早晚两次刷牙，而且经常适当吃些水果等食物， <br /><br />牙齿绝不会变黄。 <br /><br />26、喝什么茶对健康更有利? <br />喝什么茶对健康更有利，不能用一句话来作简单的回答，也就是说，各种茶叶的营养、药效成分有一定差 <br /><br />异，可适合不同身体条件的人们饮用。比如身体比较虚弱的人，喝点红茶，在茶中添加点糖和奶，既可增加能 <br /><br />量又能补充营养。青年人正处发育旺盛期，以喝绿茶为好。妇女经期前后以及更年期，性情烦躁，饮用花茶有 <br /><br />疏肝解郁、理气调经的功效。身体肥胖、希望减肥的人，可以多喝些乌龙茶、沱茶等。常年食牛羊肉较多的人 <br /><br />，可以多喝些砖茶、饼茶等经过后发酵的紧压茶，有助于脂肪食物的消化。经常接触有毒害物质的工作人员， <br /><br />可以选择绿茶作为劳动保护饮料。脑力劳动者、军人、驾驶员、运动员、歌唱家、广播员、演员等，为了提高 <br /><br />脑子的敏捷程度，保持头脑清醒、精神饱满，增强思维能力，判断能力和记忆力，可以饮用高档绿茶，诸如各 <br /><br />种名优绿茶之类。 <br />如果单从各种茶叶营养成分和药效成分的含量比较而言，高级绿茶优于其它茶类。如维生素C、B1和B2，磷 <br /><br />、钾、锌等矿物质， 茶多酚等物质，其含量通常都是高级绿茶高于其它茶。因此，从营养保健的角度而言，喝 <br /><br />高级绿茶更有利于健康。 <br /><br />27、喝浓茶好不好? <br />所谓浓茶是指泡茶用量超过常量
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/205048#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 18 Jun 2008 11:15:52 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/205048</link>
        <guid>http://ahuaxuan.javaeye.com/blog/205048</guid>
      </item>
      <item>
        <title>端午游淹城</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/201760" style="color:red;">http://ahuaxuan.javaeye.com/blog/201760</a>&nbsp;
          发表时间: 2008年06月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我是一个喜欢历史的人，尤其是中国的历史，每次看到电视里有考古或者讲历史的节目，我总是会那么投入，端午节闲来无事，于是去了趟淹城。<br /><br /><br />淹城位于常州市南面，距市区约七公里是我国目前西周到春秋时期保存下来的最古老，最完整的地面古城池。据说，这也是世界上公有的三城三河形制的古城，面积约0.6平方公里,迄今已有将近2700年的历史，在孟子•非功篇里也有淹城的记载，说淹城是“三里之城，七里之郭”。<br /><br />关于淹城的来历和淹城的主人究竟是谁,史学界和考古界众说纷纭,至今仍无定论.一说淹城曾是商末周初奄国的国都 , 奄君就是当时在山东曲阜之东的奄国君主.相传奄君就是周成王时与商代后人武庚勾结发动叛乱的奄国君王,被周成王所灭后,带领残部从山东辗转逃到江南,在这 里凿河为堑,堆土为城,仍称"奄".因为古代三点水的"淹"字与没有三点水的"奄"字通用,一直流传至今,遂有"淹城"之名.另一说是春秋晚期吴国公子季 扎不满阖闾刺杀王僚夺取王位,决心与阖闾的强暴政治决裂,"终身不入吴国",便在封地延陵筑城挖河,以示淹留之决心,帮名"淹城".自然,淹城的主人是季扎了.淹城,这座历史古城,沿有许多不解之谜,有待考古工作者、历史学家去揭开谜底。据史书记载，淹城古城墙最高达20米，墙基宽25-30米，全部由泥 土夯筑而成。<br /><br />2700多年前的城池，地表上除了护城河和城墙什么也没有留下，实在说要有什么只有几座树丛茂密的土敦孤零零的立在城内，关于这个土敦也有一个传说，传说淹君有一个女儿叫做百灵公主，能歌善舞，淹君给她找了一个驸马，但是这个驸马居心叵测，乘淹君外出偷了淹君的宝物，淹君归来，大怒，把自己的女儿斩成了3段，分布埋在3各土敦里，看图片<br />这个三个土敦分布是头敦，肚敦和足敦。后来考古学家初步认定这3个敦确实是墓葬，这也应验了一句话，历史变成了传说，传说变成了神话。如同庞贝城传说，特洛伊传说一样，这些传说最终被证明确实是真实历史。<br /><br />	说到这里，我就有一个疑问了，我觉得淹城城主是季扎的可能性不大，季扎是当时的名士，怎么可能把自己的女儿跺为3段呢，我倒觉得城主可能是犯上作乱的东夷淹君。<br /><br />	漫步在城内，看到的是一簇簇树木和花草，还有那半高不高的城墙，还有熙熙攘攘的游客，我们不知道2700多年前这里发生过什么样的故事，也不知道2700多年前这里有过什么样的人，古人留给我们的只有护城河，城墙，和墓葬，还有无尽的遐想。<br /><br /><br /><br /><br />季扎生平：<br />季扎<br />春秋时期吴国皇族.<br /><br />吴季扎为辞让王位曾两度出走，季扎是受中原文化熏陶较深的吴人，曾代表吴国出使文化发达的中原各国，和当时著名的政治家叔向、子产、晏婴都有过交往。尤其是季扎曾出使保存周代礼乐文化最完备的鲁国，他对周礼的精通和独到理解，赢得了鲁人的敬重。 <br />在齐国，他与齐国政治家晏婴有过一次推心置腹的深谈。当时齐国已政出私门，齐重臣陈完控制了齐国政局，他削弱公室，扩张自己的势力，先后灭掉了栾氏、高氏 等齐国公族。在此形势下，季扎劝告晏婴明哲保身，交出自己的权和封邑，以免除灾祸。晏子政治家，采纳了季扎的建议。《史记》说晏婴“是以免栾高之难”。 <br />在郑国，季扎和郑国著名政治家子产相谈投机。他向子产预言了郑国政局的走向，并忠告子产说：“现在郑国政治腐败，国家将会出现困难，到时您会掌握大权。希 望您当政时要谨慎，以礼治国，不然，郑国将会出现一场大混乱。”后来子产果然当政，他进行改革，铸刑鼎，倡导以法治国，引发了一场大争议，连北方晋国大臣 叔向也写信批评子产铸刑鼎将会导致上下秩序混乱。 <br />后来，他又到了晋国时值晋国国君正渐失权威，韩、赵、魏、范、中行、智氏六家异姓卿大夫掌握着国家大权，其中以韩、赵、魏三家发展势头最盛。季扎当着韩宣 子、赵文字、魏献子的面说：“晋国的归宿将在你们三家身上。”临行前叮嘱叔向说：“现在晋君越来越弱，大夫们的势力都发展起来了，以后政权将归于韩、赵、 魏三家，先生一向坚持道义，不肯屈从于时势。我奉劝先生早想存身之道，以远离灾难。” <br />叔向是晋国一位保守的政治家，其宗族也是晋公室中仅存的一支，其他各支已为六家异姓卿大夫所灭。在政治上，叔向倾向维护公室，反对卿大夫进行的改革，因而成为韩、赵、魏等异姓卿大夫仇视的目标。叔向以后，其宗族果然为三家所灭，晋国也分裂成韩、赵、魏三国。 <br />《史记》记载，季扎受阖闾委派出使中原诸国，途经徐国时，遇到徐君，徐君非常喜欢季扎的佩剑，但不好意思开口索要。季扎看出了徐君的心思，因为还要出使他 国，不便立即解剑相送，想等回来路经徐国时再相送。不料回来时徐君已死。季扎凭吊了徐君之墓后，解下佩剑挂在徐君墓前的树上。随从的人问季扎：“徐君已 死，为何还要送给他佩剑?”季扎回答说：“当时，我心里已把佩剑默许给他了，只是不便相送，现在岂能背信弃义，违背自己心中许下的诺言。” <br />季扎在吴姓血缘史上也的确是承前启后的人物，他来自吴仲雍嫡裔，当初为让国逃到延陵(今江苏省常州市附近地区)乡下，长兄诸樊只得将延陵封给季扎，季扎因而号延陵季子。此后，季扎后代世居于此，延陵成为吴姓的著名郡望。 <br />季扎死后葬在上湖(今江苏江阴申港)中的小岛上，相传大圣人孔子因推崇季扎之德，曾手书十字碑文：“呜呼有吴延陵君子之墓。”吴人为纪念季扎，在他墓旁建 庙，世代祭祀。今天除江阴申港外，江苏还有四处季扎墓，丹阳延陵镇一处，常州三处，并且都有上述碑文。自古史书对季扎葬处的记载多有不同，但不管季扎葬于 何处，这些墓碑都表现出后人对季扎的怀念之情。吴姓子孙以太伯、仲雍、季扎三位先祖而莨豪，后世吴姓宗族的祠堂往往取名“至德堂”、“三让堂”、“三德 堂”，以彰显其祖先谦让之德风。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/201760#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 10 Jun 2008 13:51:25 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/201760</link>
        <guid>http://ahuaxuan.javaeye.com/blog/201760</guid>
      </item>
      <item>
        <title>看看祸国殃民得老师是什么样子的</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/197751" style="color:red;">http://ahuaxuan.javaeye.com/blog/197751</a>&nbsp;
          发表时间: 2008年05月28日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          问题：为什么中国的软件业迟迟不能在世界版图中占有一席之地呢，作为一个软件工匠，我亦常思考此类问题，不得其解，踏入软件界三年来，略有所获，见到的事情越多，心里也就越明白。<br /><br />万事皆有因，有因必有果，想要知道中国软件现状的原因，还是须从教育说起，从目前看来中国计算机教育领域最大的两个问题一是重理论，轻实践，二是浮夸风盛行，三是学术作弊。<br /><br />以上3点，处处都可以得见，大家都知道的我就不说了，说说大家都不知道的。<br /><br />从苏州大学说起，苏州大学地处苏州，人云上有天堂，下有苏杭，本来地方不错，更何况是大学里面呢，但是如果就这样判断一个学校可能不妥。<br /><br />苏州大学是一个很无视实践的大学，在研究生里更是如此，很少有人有不错的动手能力，据我所知这不能怪学生，学生也是被迫的，因为你会做东西是毕不了业的，你一定要会忽悠老师，这样你才能顺利毕业，比如说计算机硕士答辩，东西有没有做出来，无关经要，只要论文忽悠的好就没有问题，大多数都是东抄西抄，没有真才实学，只有少数人还过得去，但是如果你认真得做了一个东西，比如说你要是自己弄一个工作流引擎作为毕业论文得题目，那么很遗憾，即使你能成功完成，你过不了，因为这个没有技术含量，可是据我观察，作其他课题得也没有什么技术含量啊，你要说数据挖掘吧，也就那么回事，因为工作流引擎听上去没有数据挖掘高深，所以工作流引擎就没有什么技术含量了。苏州大学有一个老师，叫作xxx，大言不惭，居然说他一个大学本科生得学生的毕业论文就是工作流引擎，那个学生只用了14天就做出了一个工作流引擎，我简直就是叹为观止，自叹不如啊，我不是自叹不如那个本科生，我是自叹不如xxx不要脸的程度。人可以无耻，但是不能无耻到他这个程度吧。<br /><br />说到学术作弊，有些老师，喜欢把学生的论文的第一作者改成自己的名字，明明是学生写的，跟自个屁关系也没有，我都不知道这种人怎么做的出来的，这种人在苏州大学并不少见，脸皮都不知道该往哪里放，一个高等学府中尽是这些垃圾之人，有什么资格当老师呢，只不过误人子弟而已，这些人枉为人师，亦枉为人，劝大家还是不要报考苏州大学。<br /><br />虽然我并不认识xxx，于他无仇，也只是从他的不要脸的言词上才知道一点此人无耻的程度，也是从他不要脸的言词上才知道一点他误人子弟行径。我能说什么呢，我只能说中国软件业只是因为这些垃圾才步履缓慢，也正是因为这些垃圾使软件人才的素质不得提高。<br /><br />我还是奉劝xxx之流为了中国软件业的未来，你们这些人还是早点收手吧，别在祸国殃民了行不？求你们了！
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/197751#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 28 May 2008 10:04:41 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/197751</link>
        <guid>http://ahuaxuan.javaeye.com/blog/197751</guid>
      </item>
      <item>
        <title>预测型挖掘</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/184884" style="color:red;">http://ahuaxuan.javaeye.com/blog/184884</a>&nbsp;
          发表时间: 2008年04月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          5月份，我又有一个新的挖掘任务，就是根据历史销售记录来分析将来的销售情况。所以需要预先研究一下这个方面的挖掘，以下是我的学习笔记。<br /><br />预测型知识挖掘：<br /> 	顾名思义就是由历史数据和当前数据来推测出未来数据的一种挖掘方式。<br />从上面一句话中的历史，当前，未来三个关键字，我们可以看出时间概念在这次挖掘中将会起到非常重要的作用。 从预测的主要功能上来看，主要是对未来数据的分类和趋势的输出。<br /><br />	统计学中的回归方法等可以通过历史数据直接产生对未来数据的预测的连续值（这个应该就是我要的功能）<br /><br />预测型知识的挖掘可以借助于经典的统计方法，神经网络和机器学习技术。<br /><br />一般来说常见的预测应用模型由如下4种：<br /><br />1，	趋势预测模型<br />针对那些具有时序性的数据，比如销售记录，股票价格，发现长期的趋势变化。有许多来自于统计学的方法经过改造可以用于数据挖掘中，如基于n阶移动平均值，n阶加权（加权，又是加权，加权无处不在）移动平均值。还有一些研究较早的，比如分类（分类的方法太多了，之前我在文本分类中使用的是vsm，在这些纯数据的挖掘中，象bayes，decision等方法应该能比较有效果），关联规则等技术也被应用到趋势预测中。<br /><br />2，	周期分析模式<br />主要是针对那些数据分布和时间依赖性很强的数据进行周期模式的挖掘。例如，服装在某个季节或所有季节的销售周期。近年来这方面的研究备受注目，有比如快速傅立叶变换等统计方法及其改造算法。看来我要着重研究这个周期分析模式。<br /><br />3，	序列模式<br />主要针对历史事情发生的次序的分析形成预测模式来对未来行为进行预测。例如，预测“3年前购买计算机的客户有很大的概率会购买数字相机”。我怎么就觉得这个和推荐算法有千丝万缕的联系呢。<br /><br />4，	神经网络<br />在预测型知识挖掘中，神经网络也是很有用的一个模式结构。我一直觉得这个是最玄的一个东东，目前我也没有对它进行什么深入的研究。<br /><br />通过以上的一些权衡，我觉得周期分析模式应该是我这次任务的主攻方向，希望我能圆满完成任务。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/184884#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 21 Apr 2008 13:11:18 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/184884</link>
        <guid>http://ahuaxuan.javaeye.com/blog/184884</guid>
      </item>
      <item>
        <title>别装了，难道你们不想把properties直接注入到object中去（spring-plugin）？</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/180941" style="color:red;">http://ahuaxuan.javaeye.com/blog/180941</a>&nbsp;
          发表时间: 2008年04月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: small">/** <br />*作者：张荣华（ahuaxuan） <br />*日期：2008-4-9<br />**/<br /><br /><br /><br />1背景<br />Spring2.5支持使用annotation来配置我们的service，比如如下代码：<br /><pre name="code" class="java">@Service("userService")
public class UserServiceImpl extends BaseServiceSupport implements UserService {

	public void xxx() {

	}
}</pre><br /><br />这样就表示这个service需要被spring管理，不过只是这样做是不够的，我们还需要在applicationcontext***.xml中加入这么一段：<br /><pre name="code" class="java">&lt;context:component-scan base-package="xxxxxxx"/></pre><br />这么一来这个xxxxxxx包下所有的使用@Service这个注释的对象都会自动的被spring管理。<br /><br />虽然这样看上去很美好，但是却是不满足我们的需求的，因为我们的service中，或者其他被管理的bean中有时候需要一些配置，比如说String，Integer等等，而且这些配置的值一般都来自Properties文件，一般情况下我们会使用如下这段代码：<br /><pre name="code" class="java">&lt;bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		&lt;property name="locations">
			&lt;list>
				&lt;value>classpath:jdbc.properties&lt;/value>
			&lt;/list>
		&lt;/property>
	&lt;/bean>	</pre><br />这样我们就可以通过${}来引用到properties文件中的值。<br /><br />不过使用了@service之后，我们就无法通过${}来得到properties中的值了。downpour是spring2.5使用的先行者，他很早就意识到这个问题，通过我们的讨论，确定了解决问题的方向。下面我把这个方案拿出来和大家共享。<br /><br />2目标：<br />我们的目标是实现一个Annotation，代码如下：<br /><pre name="code" class="java">@Service
public class ImageFileUpload implements Serializable {

    @Properties(name="pic.address" )
    private String picAddress;

    @Properties(name="pic.url" )
    private String picUrl;

    private String picServerUrl;
}</pre><br /><br />pic.address和pic.url是properties文件中的两个属性<br /><br />以上代码中的@Properties就是我们要实现的Annotation，通过name的值作为key去对应的properties中寻找对应的value，并且主动赋值给ImageFileUpload的对应属性。<br /><br />3步骤：<br />我们知道，spring在初始化完bean之后我们可以对这些bean进行一定的操作，这里就是一个扩展点，我决定使用BeanPostProcessor这个接口，这个接口中有一个postProcessAfterInitialization方法就是用来做bean的后处理的，一旦一个bean被初始化完成之后，我们就可以对这个bean进行赋值了。<br /><br />但是考虑到我们项目中不是所有的bean都使用Annotation来注册到spring中的，这些普通的，配置在xml文件中的bean也有用到${}的需求，所以我考虑扩展PropertyPlaceholderConfigurer这个类。我们来分析一下具体的代码。<br /><br />	首先建立一个Annotation，如下：<br /><pre name="code" class="java">/**
 * @author ahuaxuan(aaron zhang)
 * @since 2008-4-7
 * @version $Id: Properties.java 261 2008-04-07 07:03:41Z aaron $
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) 
public @interface Properties {

//	String bundle();
	
	String name();
}</pre><br /><br />接着我们实现我们的扩展主类：<br /><pre name="code" class="java">/**
 * @author ahuaxuan(aaron zhang)
 * @since 2008-4-7
 * @version $Id: AnnotationBeanPostProcessor.java 260 2008-04-07 07:03:35Z aaron $
 */
public class AnnotationBeanPostProcessor extends PropertyPlaceholderConfigurer implements BeanPostProcessor, InitializingBean {

	private static transient Log logger = LogFactory.getLog(AnnotationBeanPostProcessor.class);
	
	private java.util.Properties pros;
	
	@SuppressWarnings("unchecked")
	private Class[] enableClassList = {String.class};
	
	@SuppressWarnings("unchecked")
	public void setEnableClassList(Class[] enableClassList) {
		this.enableClassList = enableClassList;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		
		Field [] fields = bean.getClass().getDeclaredFields();
		
		for (Field field : fields) {
			if (logger.isDebugEnabled()) {
				StringBuilder sb = new StringBuilder();
				sb.append(" ========= ")
					.append(field.getType())
					.append(" ============ ")
					.append(field.getName())
					.append(" ============ ")
					.append(field.isAnnotationPresent(Properties.class));
				
				logger.debug(sb.toString());
			}
			
			if (field.isAnnotationPresent(Properties.class)) {
				if (filterType(field.getType().toString())) {
					Properties p = field.getAnnotation(Properties.class);
					try {
//						StringBuilder sb = new StringBuilder();
//						sb.append("set").append(StringUtils.upperCase(field.getName().substring(0, 1)))
//										.append(field.getName().substring(1, field.getName().length()));
//						
//						Method method = bean.getClass().getMethod(sb.toString(), String.class);
//						method.invoke(bean, pros.getProperty(p.name()));
本来我是通过set方法来把properties文件中的值注入到对应的属性上去的，后来downpour提供了更好的方案，就是下面这两行代码，虽然这样做破坏了private的功能，同时破坏了封装，但是确实节省了很多代码，建议大家在业务代码中不要这样做，如果做框架代码可以考虑一下。

						ReflectionUtils.makeAccessible(field);
						field.set(bean, pros.getProperty(p.name()));
					} catch (Exception e) {
						logger.error(" --- ", e);
					} 
				}
			}
		}
		
		
		return bean;
	}
	
	@SuppressWarnings("unchecked")
	private boolean filterType(String type) {
		
		if (type != null) {
			for (Class c : enableClassList) {
				if (c.toString().equals(type)) {
					return true;
				}
			}
			
			return false;
		} else {
			return true;
		}
	}

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public void afterPropertiesSet() throws Exception {
		pros = mergeProperties();
	}
}</pre><br /><br />最后我们需要在xml文件中配置一下：<br /><pre name="code" class="java">&lt;bean id="propertyConfigurer"
		class="xx.service.AnnotationBeanPostProcessor">
		&lt;property name="locations">
			&lt;list>
				&lt;value>classpath:jdbc.properties&lt;/value>
			&lt;/list>
		&lt;/property>
	&lt;/bean></pre><br /><br />这样任何一个bean，不管它是使用annotation注册的，还是直接配置在xml文件中的都可以使用这种方式来注入properties中的值。<br /><br />	下面看一下我在项目中的一个真实的例子，这个类是一个value object，它代表一组配置：<br /><pre name="code" class="java">@Component
public class Config implements Serializable{

	/**  */
	private static final long serialVersionUID = 8737228049639915113L;

	@Properties(name = " online.pay.accounts")
	private String accounts;
	
	@Properties(name = " online.pay.user")
	private String user;
	
	@Properties(name = " online.pay.password")
	private String password;
	
	@Properties(name = " online.transurl")
	private String transUrl;
	
	@Properties(name = " online.refundurl")
	private String refundUrl;
	
	@Properties(name = " online.query")
	private String queryUrl;

	```setter and getter method
}
</pre><br />那么在需要用到该vo的地方比如：<br /><pre name="code" class="java">@Service(“userService”)
public class UserServiceImpl implements UserService {
	@autowired
	private Config config;

	public void setConfig(Config config) {
		This.config = config;
	}
}</pre><br />就这么多内容就ok了，如果按照原来的办法，我们就需要在xml配置以上两个bean，然后在里面写一堆又一堆的${}，肯定能让你看了之后崩溃，至少我差点崩溃，因为它看上去实在是太丑陋了。而现在，我的心情好多了，因为我用这个@Properties(name = "")用的很爽，呵呵，而且即使有些bean是配置在xml文件中的，比如datasource等等，我们还是可以通过${}来进行设值，也就是说这个方案既支持annotation，也支持${}，很好，很强大。<br /><br />结语：<br />很显然，在spring2.5的时代，以上这个需求是非常平常的，我相信在spring3.0中一定会提供这样的功能，而且我觉得spring2.5应该是一个过渡版本，虽然上面的方案中代码行数并不多，但是我觉得很有价值，应该很有市场才对，也许我们可以把这个东东叫做spring-properties2object-plugin。<br /><br />题外话：<br />	说点题外话吧，目前在我的项目里，我使用了struts2.0+spring2.5+hibernate3.2，使用struts2.0的时候我使用struts2.0的zero configuration和codebehind，基本上实现了真正意义零配置，剩下的都是一些common的配置，而且很少，不超过150行。在使用spring的时候，我也基本上是使用annotation来注册我的bean，同时使用上面的方案来作为补充，所以applicationContext-xxx.xml中的也是一些common的配置，也是非常少，应该只有200行左右。而hibernate我是使用annotation来配置我的PO，基本没有配置文件。所以整个项目的xml文件中配置的总行数大大下降。<br /><br /><br /></span>
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/180941#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 09 Apr 2008 18:01:16 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/180941</link>
        <guid>http://ahuaxuan.javaeye.com/blog/180941</guid>
      </item>
      <item>
        <title>xml和annotation的是是非非</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/178725" style="color:red;">http://ahuaxuan.javaeye.com/blog/178725</a>&nbsp;
          发表时间: 2008年04月02日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          
  /**
<br />
<br />*作者：张荣华
<br />
<br />*日期：2008-4-2<br />
<br />**/
<br /><span style="font-size: small"><br />前言<br />Xml和annotation都是我们在项目中常用到的技术，尤其是在配置文件这一块。很久很久以前，当jdk5.0还没有出来的时候，或者我们还没有大规模换到jdk5.0的时代，xml作为配置文件是大行其道，但是当annotation诞生之后，形式有所转变，曾经发挥巨大功能的xml开始被人们所批斗了，现下人们对annotation开始了疯狂的崇拜。<br /><br />那么就先说说xml的功与过，他的功我们都看在心里，就拿以前最常见的技术来说吧,struts+spring+hibernate，哪一个不用配置文件，使用配置文件谁不用xml，虽然hibernate还支持property文件配置，但是很显然，那玩意儿更不易读（可能还有人说，我用xdoclet了， 可是这个东东用的人是非常之少的）。于是我们看到我的项目里到处都是xml配置文件，从struts，到hibernate，而且尤其是struts和hibernate，一个中型项目，struts的xml配置文件有时候达到数万行，再大一点的，可以数十万行，虽然很多时候都是拷贝，粘贴，但是看这个这个数十个几千行的struts配置文件能不让人倒胃口吗。再说那个hbm.xml，大家再熟悉不过了，你想想吧，一个几域模型的项目里，你需要有多少配置文件，我眼睛都看花了。这些都是xml的缺点。但是xml也有优点，比如我要做一个通用的配置，象spring里的声明式事务管理之类，那么所有的需要事务管理的bean，我只要一段配置文件或者很少几段配置文件就可以解决，这个配置不会超过50行。<br /><br />那么再看看配置文件界的后起之秀吧，话说自打annotation出来之后，众人犹如众星捧月一般，把这玩意举上神坛，每日供奉，于是呼出来一批基于annotation的mvc框架，我们看看最常用的一个吧，struts2.0里也可以使用annotation来实现result的配置，我们看看它是怎么做的：<br />首先：指定action的package目录。<br />其次：根据上面指定的目录下的action的名字来声明url，这样struts2.0可以根据url自动找到需要执行的action的方法。<br />第三：根据方法上声明的annotation来返回对应的页面。<br /><br />Struts2.0的这个零配置就是使用coc和annotation来实现的，很简单吧，但是虽然简单，功效却非常之大，这样struts.xml中的action的配置全部都可以拿掉了，配置文件就会变的非常之简单。然后我们在维护或者开发的时候，第一时间就可以知道这个方法会返回哪些页面（曾经我在webwork2上也实现了同样的功能），而在从前，我们不得不去xml配置文件中ctrl+f来寻找，就这一点就节省了很多的时间，同时xml配置文件中的配置也所剩无几，剩下的都是一些公用的配置。我们再看hibernate呢，hibernate3.2支持annotation，而且在新项目中我一直使用annotation来配置我们的po，我们得到的好处就是我们再也看不到hbm文件了，我直接就可以看到某个po属性的配置，可以这么说在这个地方用得是恰到好处，给我们带了不少方便。问题是annotation是万能的吗，我需要把struts.xml文件全部去掉吗，我要把事务的配置都写道类里面去吗，如果我们这样做了，我相信实现annotation的人一定会哭了。<br /><br />举个例子吧，spring2.0支持使用annoation来配置事务，我看了一下，发现如果我们用annotation来配置事务，事务的传播途径，是否只读之类的配置我们需要写上很多遍才行，而事实上，大多数方法的事务需求是一样的，即使有一些不一样，我们也可以通过多配两个transactionAttribute就可以实现，我们为了使用annotation而让配置反而变复杂了，值得吗。难道只是为了和别人吹嘘&ldquo;hi，知道吗，我们现在连事务也使用annotation来实现了&rdquo;。我们最想要的不是把这些公用的东西整到annotation里去，而是要把一些独用的东西放进去，比如说struts中的action定义，hibernate中po的配置，还有一个目前最想要的是spring的bean定义，这些东西可以放到annotation里去。<br /><br />Annotation不是万能钥匙，不能解开所有的锁，大家也不要对它过度崇拜，一个优秀的软件工程师不只是不断的学习，更需要的是正确的判断在何种场景下需要选择何种技术，一般说来一种技术越强大，那么它的局限性就越高，所以通常我们会选择其他的技术来和它互补，在配置文件界，我们可以说annotation很好很强大，但是我们不要忘了xml也是很好的，也是很强大的，而且他们是互补的。<br /><br /></span><p><span style="font-size: small">&nbsp;</span></p>
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/178725#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 02 Apr 2008 10:57:43 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/178725</link>
        <guid>http://ahuaxuan.javaeye.com/blog/178725</guid>
      </item>
      <item>
        <title>lighty的lb问题</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/174948" style="color:red;">http://ahuaxuan.javaeye.com/blog/174948</a>&nbsp;
          发表时间: 2008年03月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          看了galaxystar的帖子之后对lighty有了初步的了解，而且从google的trends上也可以看出lighty确实是一个web server中迅速崛起的新星，势头非常之强劲，所以趁这个周末我也来学习一把<br /><br />1，下载安装，我使用的是ubuntu7.10，所以下载lighttpd非常简单，只要执行<br />sudo apt-get install lighttpd 命令就可以下载并安装lighttpd了，我下载的好像是1.4的版本。<br />安装结束之后，它就自动启动了，这时候在流览器里输入http://localhost就可以看到lighttpd的页面了。<br /><br />2，下载tomcat6.10，拷贝一份出来，修改conf目录下的server.xml，将connector中的8080改成18080。启动两个tomcat实例。这时候在浏览器里输入http://localhost:8080/index.jsp和http://localhost:18080/index.jsp，分别出现两个tomcat的页面，接下来在这两个index.jsp(webapps/root()目录下)分别加入以下代码&lt;%out.println("-aaaaaa-");%>和&lt;%out.println("-bbbbbb-");%><br /><br />3,接下来修改lighttpd.conf文件，在server.modules 数组中加入一个新的元素，如下：<br />server.modules              = (<br /><br />            "mod_access",<br /><br />            "mod_alias",<br /><br />            "mod_accesslog",<br /><br />            "mod_compress",<br /><br />#           "mod_rewrite",<br /><br />#           "mod_redirect",<br /><br />#           "mod_status",<br /><br />#           "mod_evhost",<br /><br />#           "mod_usertrack",<br /><br />#           "mod_rrdtool",<br /><br />#           "mod_webdav",<br /><br />#           "mod_expire",<br /><br />#           "mod_flv_streaming",<br /><br />            "mod_proxy", #主要是增加这个节点，代表lighty将会使用proxy模块<br /><br />#           "mod_evasive"<br /><br /> )<br /><br />接着在下面加上如下配置：<br />$HTTP["host"] == "localhost" {<br /><br /><br /><br />$HTTP["url"] =~ ".jsp|.go|.do|action" {<br /><br />proxy.balance = "fair"<br /><br />proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => 8080 ),("host" => "127.0.0.1", "port" => 18080) ))<br /><br />}<br /><br />}<br /><br />上面这段配置浅显易懂，只有一个proxy.balance节点的熟悉比较奇怪。<br />lighttpd的配置要比apache简单的多了，而且更容易理解。这也是apache需要学习的地方。<br /><br />4,重启lighttpd,看看有什么效果：./lighttpd restart<br /> <br />在浏览器中输入http://localhost/index.jsp<br />出现的页面上有-aaaaaa-这个字符串，这代表请求被转发到1号tomcat了，再多次请求，发现返回的页面上一直有a的字符串，而没有出现b的字符串，貌似lb没有产生效果。<br /><br /><br />ok,让我们来看看proxy.loadblance的几个值：<br />hash:表示相同的request uri会被发送到同一个proxyhandler，比如说现在我们有两个node，那么a.jsp的hashcode为1，b.jsp的hashcode为2，那么a.jsp的请求只会被发到node1（2），而b.jsp的请求只会被发到node2（1）。<br /><br />fair:表示使用普通的基于负载的消极的均衡，什么意思呢，我的理解就是，当node1负载过高时，才把请求发给node2，所以就出现了上面的那种情况，不管你怎么刷，都是出现-aaaaaa-,而不会出现-bbbbbb-了。只有当node1压力过高时才会出现b。该策略是lighttpd默认的lb策略。<br /><br />round-robin：表示不管怎么样都是轮发。也就是说通过这种方式我们不能通过设置factor来控制node的处理数量，这个是一个个小小的缺憾。更大的问题是据说lighttpd的round-robin不是很稳定，这个是最麻烦的（最大的麻烦是我没有试出来，一使用round-robin，lighttpd就跟我说：no proxy-handler found for: /，文档上写道：Check if you have used an IP address for the proxy address. Hostnames are not allowed there!   但是我没有用hostname也不行，有点郁闷了。<br />）。所以综合看来hash策略应该是最佳选择了。<br /><br />题外话，lighttpd好像也不支持stickly-session吧，所以我觉得使用lighttpd来做lb不是很合适。在这个功能上它和apache还有较大差距，今天试用的是lighttpd1.4的版本，不知道1.5版本的lighttpd在这方面会不会好一点。有空我来试用一下lighttpd1.5的round-robin，看能否让它正常跑起来。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/174948#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 23 Mar 2008 00:00:51 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/174948</link>
        <guid>http://ahuaxuan.javaeye.com/blog/174948</guid>
      </item>
      <item>
        <title>通过对web日志的挖掘来实现内容推荐系统</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/169512" style="color:red;">http://ahuaxuan.javaeye.com/blog/169512</a>&nbsp;
          发表时间: 2008年03月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          /**<br /><br />*作者：张荣华<br /><br />*日期：2008-3-9<br /><br />**/<br /><br />先说一说问题，不知道大家有没有这样的经验，反正我是经常碰到。<br /><br />        举例1，某些网站每隔几天就发邮件给我，每次发的邮件内容都是一些我根本不感兴趣的东西，我不甚其扰，对其深恶痛绝。<br />        举例2，添加具有某功能的一个msn机器人，每天都有几次突然蹦出一个窗口，推荐一堆我根本不想知道的内容，烦不烦啊， 我只好将你阻止掉。<br /><br />        每一个观众只想看他感兴趣的东西，而不是一下与之无关的事物，那么如何才能知道观众的兴趣所在呢，还是数据挖掘，经过一番思考，终于有点思路，即根据用户以往的浏览历史来预测用户将来的行为，也就是基于内容的推荐。<br />基于内容的推荐（Content-based Recommendation）是信息过滤技术的延续与发展，它是建立在项目的内容信息上作出推荐的，而不需要依据用户对项目的评价意见，更多地需要用机器学习的方法从关于内容的特征描述的事例中得到用户的兴趣资料。在基于内容的推荐系统中，项目或对象是通过相关的特征的属性来定义，系统基于用户评价对象的特征，学习用户的兴趣，考察用户资料与待预测项目的相匹配程度。用户的资料模型取决于所用学习方法，常用的有决策树、神经网络和基于向量的表示方法等。基于内容的用户资料是需要有用户的历史数据，用户资料模型可能随着用户的偏好改变而发生变化。<br /><br /><br />基于内容推荐方法的优点是：<br />1）不需要其它用户的数据，没有冷开始问题和稀疏问题。<br />2）能为具有特殊兴趣爱好的用户进行推荐。<br />3）能推荐新的或不是很流行的项目，没有新项目问题。<br />4）通过列出推荐项目的内容特征，可以解释为什么推荐那些项目。<br />5）已有比较好的技术，如关于分类学习方面的技术已相当成熟。<br /><br /><br />    缺点是要求内容能容易抽取成有意义的特征，要求特征内容有良好的结构性，并且用户的口味必须能够用内容特征形式来表达，不能显式地得到其它用户的判断情况。<br /><br />要实现内容推荐系统总体来说要经过4个大的步骤：<br />1 搜集数据，即搜集用户的行为资料，其中也包括很多方法，根据我找到的资料与以往的经验来看，web日志可以作为我们的切入点，即我们的数据来源。<br /><br />2 过滤数据，web日志中有很多无用的信息，我们要把这些无用的信息排除掉，而且要区分出用户和日志数据之间的联系。<br /><br />3 分析数据，利用分类聚类技术分析出这些日志数据之间的关联性，以及这些日志数据和用户之间的关联性，这也是最重要的一步。<br /><br />4 输出结果。<br /><br />    有了这个思路之后，我们可以着手做第一步，即日志数据的收集<br />我们知道，大多数的web服务器都是有自己的日志记录的，比如说apache安装之后有一个logs目录，其中就有它的日志文件，一般说来它有自己的一个格式，比如说：<br />1浏览器所在主机的 IP 地址(ip)； 2访问日期和时间(date-time)；3客户机与服务器通信所用的方法(methed，get or post)； 4客户机请求访问页面的 URL； 5服务器返回的状态(status)； 6客户端浏览器的类型；<br /><br />   <br />        但是这个日志文件有一些不能克服的问题，或者我不知道如何克服，那么我先说说我的疑问，首先，这个日志文件中记录的是ip地址，据了解，网络中有很多计算机的ip地址是相同的，因为他们在一个统一的路由后面，这个比例可能达到25%。那么我们就无法根据ip地址来唯一确定一个用户。其次，一般的web服务器中都会用多个应用，那么其他应用的访问信息对我们来说有可能是多余的。再者，web服务器的日志形式比较单一，灵活性不大，可定制的余地很小，在日志数据中有效数据所占的比例较小。还有，一些静态文件的请求也会被web服务器记录下来，比如说js文件，css文件，还有图片文件，等等这些东西对内容推荐来说都是无用的资源。<br /><br />        基于上面3点原因，我认为可以自定义日志数据。为了解决用户唯一性，我们让应用为每一个浏览器生成一个clientId保存在对应的浏览器上，这样该浏览器只要访问网站，我们就可以确定这个浏览器的唯一性，当然我们仍然不能确定浏览器使用者的唯一性，但是我们可以更进一步，如果浏览器的使用者登陆网站的话，我们就可以使用用户id来确定用户的唯一性，不过大多数网站用户可能在使用网站的时候并不会登陆，我也是这样，没有关系，即使使用clientId问题也不会太大，随着社会的发展，计算机的拥有量逐渐增加，一般来说一个人只会使用一台固定的电脑，在公司里尤其是这样。所以我认为clientId的方案是可行的，也许有人要问，别人的浏览器禁止了cookie怎么办，那么我只能说没有办法，不过还好事实是绝大多数人都没有这样做。<br /><br />接下来我们可以定义一下我们所需要的日志数据的格式，比如这样，<br />ip，clientId，userId，url，datetime，get or post等等。<br />这样数据有效性会大大提高。<br /><br />在得到较为有效的数据之后，我们还需要对这些数据进行再次过滤：<br />    1 去掉一些非内容的url，这些数据也是无效数据，这些非内容的url需要我们自己手工的统计出来，然后和日志数据中的数据进行比对，将这些非内容数据从日志数据中清除出去。<br />    2 同时我们也需要把post请求从日志数据中清除出去，或者我们在记录日志的时候根本不应该把post请求记录下来。<br /><br /><br />        经过以上步骤之后我们就可以开始第3个阶段了，统计每个用户的访问的url，对这些url进行访问，得到对应的html中所包含的数据，这些数据都是文本，将有用的文本提取出来，然后对这些有用的文本进行聚类。这样就可以得到每个用户喜欢的几个类别。<br /><br />        聚类完成之后我们就可以开始分类了，即把最新的文章或者内容和对应的类别进行匹配，匹配成功之后，我们可以认为这个新文章或者内容可以推荐给对应的用户。<br /><br />        问题：以上的流程只适用于没有使用缓存的系统，但是一般大型的网站都会使用varnish，squid等等，使用它们之后我们就无法得到用户访问的日志数据了，所以如果使用了varnish或者squid，我们不得不再次面对web服务器的日志数据。<br /><br />        在不考虑varnish或者squid的情况下，使用lucene+jamon+htmlparse基本就可以实现以上推荐系统。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/169512#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 09 Mar 2008 12:29:44 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/169512</link>
        <guid>http://ahuaxuan.javaeye.com/blog/169512</guid>
      </item>
      <item>
        <title>用jamon来监控你的sql执行效率</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/164758" style="color:red;">http://ahuaxuan.javaeye.com/blog/164758</a>&nbsp;
          发表时间: 2008年02月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          /** <br /><br />*作者：张荣华 <br /><br />*日期：2008-2-25<br /><br />**/<br /><br />之前有一篇文章讲到如何使用jamon来监控请求以及方法得调用(原文地址见：<a href="http://www.javaeye.com/post/354575 " target="_blank">http://www.javaeye.com/post/354575 </a>)，本文属于其姊妹篇，使用jamon监控系统的sql调用及其调用效率。<br /><br />需求：<br />1我们知道在使用hibernate得时候，我们可以打开show sql选项，可以直接查看sql语句调用的情况，那么当我们使用其他持久技术的时候我们也需要这个功能怎么办呢，没有关系，jamon能够帮我们做到。<br /><br />2 很多时候，不同的程序员会写出不同的性能的sql，有时候可能会不小心或者因为不知道而写出性能很差的sql，我自己曾经就发生过这种事情，在500w条数据的表里使用了一个limit来分页，到后面，执行一条sql都需要几分钟，诸如此类的时候可能大家都有碰到过，如果能有监控sql性能的工具嵌在应用里该多好，当然有jamon就可以帮我们做到。<br /><br />对于jamon来说，每一个query的执行之后的统计结果都会被保存下来，这些概要统计都以MonProxy-SQL开头。这些统计中包括查询执行的时间，有比如平均时间，执行总时间，最小执行时间，最大执行时间，这些东西难道不是我们正想要的吗。<br /><br />那么让我们开始吧，我们知道，这些query执行的统计应该是在connection中被统计的，也就是说我们要代理一般的connection，而connection又是由datasource产生的，所以我们可以代理datasource，说干就干。<br /><br />一个datasource接口中关于connection的方法只有两个：<br /><pre name="code" class="java">
/**
   * &lt;p>Attempts to establish a connection with the data source that
   * this &lt;code>DataSource&lt;/code> object represents.
   *
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   */
  Connection getConnection() throws SQLException;
      
  /**
   * &lt;p>Attempts to establish a connection with the data source that
   * this &lt;code>DataSource&lt;/code> object represents.
   *
   * @param username the database user on whose behalf the connection is 
   *  being made
   * @param password the user's password
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   * @since 1.4
   */
  Connection getConnection(String username, String password) 
    throws SQLException;
</pre><br />也就是说我们只要override这两个方法即可。<br />根据这个思路我写了以下代码：<br /><pre name="code" class="java">
/**
 * @author ahuaxuan(aaron zhang)
 * @since 2008-2-25
 * @version $Id$
 */
public class MonitorDataSource implements DataSource {
	public DataSource realDataSource;

	public void setRealDataSource(DataSource realDataSource) {
		this.realDataSource = realDataSource;
	}

	public DataSource getRealDataSource() {
		return realDataSource;
	}
	public Connection getConnection() throws SQLException {
//表示由jamon来代理realDataSource返回的Connection
		return MonProxyFactory.monitor(realDataSource.getConnection());
	}

	public Connection getConnection(String username, String password)
			throws SQLException {
//表示由jamon来代理realDataSource返回的Connection

		return MonProxyFactory.monitor(realDataSource.getConnection(username,
				password));
	}
}
</pre><br />显然这个一个代理模式。接下来就是生成这个代理类,我是在spring中注册了这么一个类：<br /><pre name="code" class="java">
&lt;bean id="writeMonitorDataSource" class="org.ahuaxuan.MonitorDataSource" destroy-method="close">
		&lt;property name="realDataSource" ref="writeDataSource"/>
	&lt;/bean></pre><br /><br />writeMonitorDataSource 所依赖的writeDataSource就是我们真正配置的datasource，比如：<br /><pre name="code" class="java">&lt;bean id="writeDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		&lt;property name="driverClassName">
			&lt;value>${jdbc.driverClassName}&lt;/value>
		&lt;/property>
		&lt;property name="url">
			&lt;value>${jdbc.url}&lt;/value>
		&lt;/property>
		&lt;property name="username">
			&lt;value>${jdbc.username}&lt;/value>
		&lt;/property>
		&lt;property name="password">
			&lt;value>${jdbc.password}&lt;/value>
		&lt;/property>
        &lt;property name="maxActive">
            &lt;value>${jdbc.maxActive}&lt;/value>
        &lt;/property>
        &lt;property name="maxIdle">
        	&lt;value>${jdbc.maxIdle}&lt;/value>
        &lt;/property>
        &lt;property name="maxWait">
        	&lt;value>${jdbc.maxWait}&lt;/value>
        &lt;/property>
&lt;/bean>
</pre><br />好了，那么在使用datasource的时候，我们应该用哪个呢，当然是writeMonitorDataSource这个里，我们可以把它注入给jdbcTemplate，或者sessionfactory，或者其他需要用到datasource的地方。<br /><br />到这里，就一切准备完毕了，我们可以看看我们sql语句的执行效率了(这个页面的地址为sql.jsp)： <br /><span style="color: darkred">见图1</span><br />当然要我们的应用能够显示这个页面，我们需要把jamon的一组页面拷到我们的应用中，这一组页面包含在我提供下载的包中，最新的jamon版本是2.7。<br /><br />我们可以看到id为153的那条sql语句执行了78ms，我要去看看这条sql语句是不是有点什么问题或者是否有优化的可能性。<br /><br />当然，刚才说到每一条sql语句都是有统计平均时间，最大最小执行时间等等，没错，在另外一个页面jamonadmin.jsp上就包含这些内容<br /><span style="color: darkred">见图2</span><br />       <br /> <br />上面的图片代表hits表示执行次数，avg表示sql执行的平均时间，后面的min和max表示sql执行的最小耗时和最大耗时。从这里我们能够更直观的看到我们每条sql语句执行的情况。很有用的一个功能。<br /><br />而且在上面那两个页面上，我们还可以选择把sql执行的结果导出来，可以导成xml或excel格式。<br /><br />总结：使用jamon来监控我们的sql语句我觉得很有使用意义，而且使用jamon对我们的应用来说完全是松耦合的，根本不需要更改我们的业务逻辑代码，完全是可插拔的，我们也可以开发时使用jamon，部署时拔掉jamon。有了它能够使一些程序员能够更多一点的关注自己所写的sql的效率，当然如果之前开发的时候没有使用jamon也没有关系，即使上线后也可以查看一下sql语句是否有问题，比如哪些sql语句执行得比较频繁，是否存在给其做缓存得可能性等等。总之使用jamon在应用程序中来监控我们得sql语句具有很强得实用意义，<br /><br />再次总结：jamon，很好，很强大。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/164758#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 25 Feb 2008 15:48:25 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/164758</link>
        <guid>http://ahuaxuan.javaeye.com/blog/164758</guid>
      </item>
      <item>
        <title>数据挖掘之分类（kNN算法的描述及使用）</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/164435" style="color:red;">http://ahuaxuan.javaeye.com/blog/164435</a>&nbsp;
          发表时间: 2008年02月23日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          /** <br /><br />*作者：张荣华 <br /><br />*日期：2008-2-23 <br /><br />**/<br /><br /><br />数据挖掘之分类系列文章<br /><br />之前说到分类的基本概念以及一个文本分类的实例，原文地址见：<a href="http://www.javaeye.com/topic/163285" target="_blank">http://www.javaeye.com/topic/163285</a> 现在我们就来改造之前的分类算法，本文主要介绍KNN算法在文本分类器中的使用。<br /><br />kNN算法简介：<br /> kNN(k Nearest Neighbors)算法又叫k最临近方法， 总体来说kNN算法是相对比较容易理解的算法之一，假设每一个类包含多个样本数据，而且每个数据都有一个唯一的类标记表示这些样本是属于哪一个分类， kNN就是计算每个样本数据到待分类数据的距离，取和待分类数据最近的k各样本数据，那么这个k个样本数据中哪个类别的样本数据占多数，则待分类数据就属于该类别。<br />	<br />	基于kNN算法的思想，我们必须找出使用该算法的突破点，本文的目的是使用kNN算法对文本进行分类，那么和之前的文章一样，关键还是项向量的比较问题，之前的文章中的分类代码仅使用的反余弦来匹配项向量，找到关键的“距离”，那么我们可以试想反余弦之后使用kNN的结果如何。<br /><br />	补充上一篇文章中没有详细讲解的反余弦匹配问题：<br />Lucene中有一个term vectors这个东东，它表示该词汇单元在文档中出现的次数，比如说这里有两篇文章，这两篇文章中都有hibernate和spring这两个单词，在第一篇文章中hibernate出现了10次，spring出现了20次，第二篇文章中hibernate出现15次，spring出现10次，那么对第一篇文章来说有两个项向量，分别是hibernate:10,spring:20，第二篇文章类似，hibernate:15,spring:10。然后我们就可以在二维空间的x，y组上表示出来，如图：<br /> <br />这样看来我们其实是要得到两者之间的夹角，计算两个向量之间夹角的公式为A*B/||A||*||B||。按照这个原理我们就可以得到新文章和样本文章之间的距离，代码如下，这个份代码在第一篇文章提供的代码下载中。<br /><pre name="code" class="java">public double caculateVectorSpace(Map&lt;String, Integer> articleVectorMap, Map&lt;String, Integer> classVectorMap) {
		if (articleVectorMap == null || classVectorMap == null) {
			if (logger.isDebugEnabled()) {
				logger.debug("itemVectorMap or classVectorMap is null");
			}
			
			return 20;
		}
		
		int dotItem = 0;
		double denominatorOne = 0;
		double denominatorTwo = 0;
		
		
		for (Entry&lt;String, Integer> entry : articleVectorMap.entrySet()) {
			String word = entry.getKey();
			double categoryWordFreq = 0;
			double articleWordFreq = 0;
			
			if (classVectorMap.containsKey(word)) {
				categoryWordFreq = classVectorMap.get(word).intValue() / classVectorMap.size();
				articleWordFreq = entry.getValue().intValue() / articleVectorMap.size();
			}
			
			dotItem += categoryWordFreq * articleWordFreq;
			denominatorOne += categoryWordFreq * categoryWordFreq;
			denominatorTwo += articleWordFreq * articleWordFreq;
		}
		
		double denominator = Math.sqrt(denominatorOne) * Math.sqrt(denominatorTwo);
		
		double ratio =  dotItem / denominator;
		
		return Math.acos(ratio);
	}
</pre><br />	接着根据kNN的原理，我们记录下待分类数据和样本数据的距离，对每一个待分类数据都找出k个距离最小的样本，最后判断这些样本所在的分类， 这些样本所在的分类就是该新数据应该所在的分类。<br /><br />	那么根据以上的描述，我把结合使用反余弦匹配和kNN结合的过程分成以下几个步骤：<br />1，	计算出样本数据和待分类数据的距离<br />2，	为待分类数据选择k个与其距离最小的样本<br />3，	统计出k个样本中大多数样本所属的分类<br />4，	这个分类就是待分类数据所属的分类<br /><br />根据上面的步骤，我写出了以下代码，这些代码都包含在提供下载的代码里，我将不断的扩充这些代码，可以说一下代码是使用kNN比较核心的代码。<br /><br />MatchCondition这个类包括，待分类数据，样本数据，样本类别，和距离。<br /><pre name="code" class="java">
protected Map&lt;String, List&lt;MatchCondition>> analyse(Map&lt;String, Map&lt;String, Integer>> articleVectorMap, Map&lt;String, Map&lt;String, Integer>> categoryVectorMap) {
		
		Map&lt;String, List&lt;MatchCondition>> result = new HashMap&lt;String, List&lt;MatchCondition>>();
		
		for (Entry&lt;String, Map&lt;String, Integer>> categoryEntry : categoryVectorMap.entrySet()) {
			
			for (Entry&lt;String, Map&lt;String, Integer>> itemEntry : articleVectorMap.entrySet()) {
				double acos = caculateVector(itemEntry.getValue(), filterVectorMap(categoryEntry.getValue()));
				if (acos &lt; vectorGene) {
					if (result.get(itemEntry.getKey()) != null) {
						List&lt;MatchCondition> list = result.get(itemEntry.getKey());
						
						if (list.size() &lt; kNum) {
							list.add(new MatchCondition(itemEntry.getKey(), categoryEntry.getKey(), acos));
						} else {
							if (list.size() == kNum) {
								Collections.sort(list, new MatchConditionComparator());
							}
							
							int n = 0;
							for (MatchCondition condition : list) {
								if (acos &lt; condition.getAcos()) {
									list.set(n, new MatchCondition(itemEntry.getKey(), categoryEntry.getKey(), acos));
								}
								n++;
							}
						}
					} else {
						List&lt;MatchCondition> list = new LinkedList&lt;MatchCondition>();
						list.add(new MatchCondition(itemEntry.getKey(), categoryEntry.getKey(), acos));
						result.put(itemEntry.getKey(), list);
					}
					
				}
			}
			
		}
		
		return result;
	}</pre><br /><br />所有的代码在本文提供的下载代码中，以第一篇文章中的测试数据运行测试，所得的结果为：<br />2008-02-23 14:04:15,646 DEBUG ArticleKNNClassifierImpl:81 - ---------------- The article id is 3<br />2008-02-23 14:04:15,646 DEBUG ArticleKNNClassifierImpl:83 - categoryId : a | count : 1<br />2008-02-23 14:04:15,646 DEBUG ArticleKNNClassifierImpl:81 - ---------------- The article id is 2<br />2008-02-23 14:04:15,646 DEBUG ArticleKNNClassifierImpl:83 - categoryId : b | count : 2<br />2008-02-23 14:04:15,646 DEBUG ArticleKNNClassifierImpl:83 - categoryId : a | count : 3<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:81 - ---------------- The article id is 1<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:83 - categoryId : b | count : 1<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:83 - categoryId : a | count : 4<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:81 - ---------------- The article id is 5<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:83 - categoryId : b | count : 1<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:83 - categoryId : a | count : 4<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:81 - ---------------- The article id is 4<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:83 - categoryId : b | count : 3<br />2008-02-23 14:04:15,656 DEBUG ArticleKNNClassifierImpl:83 - categoryId : a | count : 2<br />从这里我们可以看出articleId为2的应该属于a分类，articleId为1的也属于a分类，articleId为5的也属于a分类，articleId为4的属于b分类。当然其实我们的样本数量太少了，并不能说明acos＋knn结合的的效果。<br /><br />也有人提出了一种结合kNN分类器的加权特征提取问题，该分类通过每次的分类结果不断的调整权值，具有较好的分类效果。所以说虽然kNN算法比较简单，但是事实上如果使用正确，应该也可以收到不错的效果。<br /><br />下一篇关于数据挖掘的文章还是分类问题，不过会使用另外一个算法，就是朴素贝叶斯分类。当然也希望有大家能够在这里交流一下自己的经验。
          <br/>
          <span style="color:red;">
            <a href="http://ahuaxuan.javaeye.com/blog/164435#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 23 Feb 2008 15:18:28 +0800</pubDate>
        <link>http://ahuaxuan.javaeye.com/blog/164435</link>
        <guid>http://ahuaxuan.javaeye.com/blog/164435</guid>
      </item>
      <item>
        <title>数据挖掘之分类</title>
        <author>ahuaxuan</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://ahuaxuan.javaeye.com">ahuaxuan</a>&nbsp;
          链接：<a href="http://ahuaxuan.javaeye.com/blog/163285" style="color:red;">http://ahuaxuan.javaeye.com/blog/163285</a>&nbsp;
          发表时间: 2008年02月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          /**<br /><br />*作者：张荣华<br /><br />*日期：2008-2-19<br /><br />**/<br />随着当代计算机硬件的发展，硬件功能越来越强大，价格越来越低，企业可以记录的数据也越来越多，这些因素就为数据挖掘的普及做了比较好的前提准备，树挖掘是未来信息处理的重要技术，而且就目前而言已经取得了决定性成功而且得到了比较广泛的应用。<br /><br />数据挖掘中有很多领域，分类就是其中之一，什么是分类，<br />分类就是把一些新得数据项映射到给定类别的中的某一个类别，比如说当我们发表一篇文章的时候，就可以自动的把这篇文章划分到某一个文章类别，一般的过程是根据样本数据利用一定的分类算法得到分类规则，新的数据过来就依据该规则进行类别的划分。<br /><br />	分类在数据挖掘中是一项非常重要的任务，有很多用途，比如说预测，即从历史的样本数据推算出未来数据的趋向，有一个比较著名的预测的例子就是大豆学习。再比如说分析用户行为，我们常称之为受众分析，通过这种分类，我们可以得知某一商品的用户群，对销售来说有很大的帮助。<br /><br />分类器的构造方法有统计方法，机器学习方法，神经网络方法等等。常见的统计方法有knn算法，基于事例的学习方法。机器学习方法包括决策树法和归纳法，上面讲到的受众分析可以使用决策树方法来实现。神经网络方法主要是bp算法，这个俺也不太了解。<br /><br /><br />文本分类， 所谓的文本分类就是把文本进行归类，不同的文章根据文章的内容应该属于不同的类别，文本分类离不开分词，要将一个文本进行分类，首先需要对该文本进行分词，利用分词之后的的项向量作为计算因子，再使用一定的算法和样本中的词汇进行计算，从而可以得出正确的分类结果。在这个例子中，我将使用庖丁分词器对文本进行分词。<br /><br />下面这个例子将使用反余弦进行词汇单元进行匹配，<br />第一步，训练样本：<br /><pre name="code" class="java">protected Map&lt;String, Map&lt;String, Integer>> getClassVector(List&lt;Category> categoryList) throws Exception {
		
		if (categoryList == null || categoryList.size() == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug("The list of new categoryList which should be classified is null or size = 0");
			}
			return Collections.emptyMap();
		}
		
		Map&lt;String, Map&lt;String, Integer>> categoryMap = new HashMap&lt;String, Map&lt;String, Integer>>();
		
		Directory ramDir = new RAMDirectory();
		IndexWriter writer = new IndexWriter(ramDir, new PaodingAnalyzer(), true);
		
		for (Category cRc : categoryList) {
			for (Article item : cRc.getArticleList()) {
				
				Document doc = new Document();
				doc.add(new Field("description", item.getContent(), Field.Store.NO,
						Field.Index.TOKENIZED, TermVector.YES));
				doc.add(new Field("category", cRc.getId().toString(), Field.Store.YES, Field.Index.NO));
				writer.addDocument(doc);
			}
		}
		
		if (logger.isDebugEnabled()) {
			logger.debug("Generate the index in the memory, the size of categoryList list is " + categoryList.size());
		}
		
		writer.close();
		
		buildContentVectors(ramDir, categoryMap, "category", "description");
		return categoryMap;
		
	}
</pre><br />第二步：对待分类的文章进行分词（原理和样本训练类似）：<br /><pre name="code" class="java">
protected Map&lt;String, Map&lt;String, Integer>> getArticleVector(List&lt;Article> articleList) throws Exception {
		if (articleList == null || articleList.size() == 0) {
			if (logger.isDebugEnabled()) {
				logger.debug("The list of articles which should be classified is null or size = 0");
			}
		}
		
		Map&lt;String, Map&lt;String, Integer>> articleMap = new HashMap&lt;String, Map&lt;String, Integer>>();
		
		Directory articleRamDir = new RAMDirectory();
//		IndexWriter writer = new IndexWriter(articleRamDir, new ChineseAnalyzer(), true);
		IndexWriter writer = new IndexWriter(articleRamDir, new PaodingAnalyzer(), true);
		
		for (Article article : articleList) {
			Document doc = new Document();
			doc.add(new Field("articleId", article.getId(),
					Field.Store.YES, Field.Index.NO));
			doc.add(new Field("description", article.getContent(), Field.Store.NO, Field.Index.TOKENIZED, TermVector.YES));
			writer.addDocument(doc);
		}
		
		writer.flush();
		writer.close();
		
		buildContentVectors(articleRamDir, articleMap, "articleId", "description"