licheng700

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            26 Posts :: 5 Stories :: 5 Comments :: 1 Trackbacks

          #

          Jakarta-Common-BeanUtils研究心得(1)
          SonyMusic
          2003.05.13

          一、概述
          第一次看到BeanUtils包,是在Struts項目中,作為Struts一個工具來使用的,
          估計功能越弄越強,就移到Common項目中了吧。

          BeanUtils一共有四個package:
          org.apache.commons.beanutils
          org.apache.commons.beanutils.converters
          org.apache.commons.beanutils.locale
          org.apache.commons.beanutils.locale.converters
          后三個包主要是用于數據的轉換,圍繞著一個Converter接口,該接口只有一個方法:
          java.lang.Object convert(java.lang.Class type, java.lang.Object value) ,
          用于將一個value轉換成另一個類型為type的Object。在一些自動化的應用中應該會有用。
          這里不作評論,以后有興趣了,或者覺得有用了,再行研究。
          這里只講第一個包。

          二、測試用的Bean
          在開始所有的測試之前,我寫了一個簡單的Bean,以便于測試,代碼如下:
          package test.jakarta.commons.beanutils;

          /**
           * @author SonyMusic
           *
           */
          public class Month {
           private int value;
           private String name;
           private int[] days={11,22,33,44,55};

           public Month(int v, String n){
             value=v;
             name=n;
           }
           
           /**
            * Returns the name.
            * @return String
            */
           public String getName() {
             return name;
           }

           /**
            * Returns the value.
            * @return int
            */
           public int getValue() {
             return value;
           }

           /**
            * Sets the name.
            * @param name The name to set
            */
           public void setName(String name) {
             this.name = name;
           }

           /**
            * Sets the value.
            * @param value The value to set
            */
           public void setValue(int value) {
             this.value = value;
           }

           /**
            * @see java.lang.Object#toString()
            */
           public String toString() {
             return value+"("+name+")";
           }

           public int[] getDays() {
             return days;
           }

           public void setDays(int[] is) {
             days = is;
           }

          }

          三、BeanUtils
          這是一個主要應用于Bean的Util(呵呵,這個解釋很絕吧),以下是其中幾個方法的例子

          //static java.util.Map describe(java.lang.Object bean)
          //這個方法返回一個Object中所有的可讀屬性,并將屬性名/屬性值放入一個Map中,另外還有
          //一個名為class的屬性,屬性值是Object的類名,事實上class是java.lang.Object的一個屬性
           Month month=new Month(1, "Jan");
           
           try {
             Map map=BeanUtils.describe(month);
             Set keySet=map.keySet();
             for (Iterator iter = keySet.iterator(); iter.hasNext();) {
               Object element = (Object) iter.next();
               System.out.println("KeyClass:"+element.getClass().getName());
               System.out.println("ValueClass:"+map.get(element).getClass().getName());
               System.out.print(element+"\t");
               System.out.print(map.get(element));
               System.out.println();
             }
           } catch (IllegalAccessException e) {
             e.printStackTrace();
           } catch (InvocationTargetException e) {
             e.printStackTrace();
           } catch (NoSuchMethodException e) {
             e.printStackTrace();
           }
          輸出為:
          KeyClass:java.lang.String
          ValueClass:java.lang.String
          value  1
          KeyClass:java.lang.String
          ValueClass:java.lang.String
          class  class test.jakarta.commons.beanutils.Month
          KeyClass:java.lang.String
          ValueClass:java.lang.String
          name  Jan
          注意到所有Map中的key/value都是String,而不管object中實際的值是多少。
          與此對應的還有static void populate(java.lang.Object bean, java.util.Map properties)
          用于將剛才describe出的Map再裝配成一個對象。


          再看這樣一段代碼
          曹曉鋼也許還記得,為了取一個不確定對象的property,著實花了不少時間,
          難度不大,但要做到100%的正確,仍然需要付出很大的精力。
          //static java.lang.String getProperty(java.lang.Object bean, java.lang.String name)
           Month month=new Month(1, "Jan");
           
           try {
             System.out.println(BeanUtils.getProperty(month,"value"));
           } catch (Exception e) {
             e.printStackTrace();
           }
          //輸出是:1

          與getProperty類似的還有getIndexedProperty, getMappedProperty,
          以getIndexedProperty為例:
           Month month=new Month(1, "Jan");
           
           try {
             System.out.println(BeanUtils.getIndexedProperty(month,"days",1));
             System.out.println(BeanUtils.getIndexedProperty(month,"days[1]"));
           } catch (Exception e) {
             e.printStackTrace();
           }
          這兩個調用是相同的。


          BeanUtils中還有一個方法:
          static void copyProperties(java.lang.Object dest, java.lang.Object orig)
          它真是太有用,還記得struts中滿天飛的都是copyProperties,我甚至懷疑整個BeanUtils最初
          是不是因為這個方法的需求才寫出來的。
          它將對象orig中的屬性復制到dest中去。


          四、PropertyUtils
          這個類和BeanUtils類很多的方法在參數上都是相同的,但返回值不同。
          BeanUtils著重于"Bean",返回值通常是String,而PropertyUtils著重于屬性,
          它的返回值通常是Object。


          五、ConstructorUtils
          這個類中的方法主要分成兩種,一種是得到構造方法,一種是創建對象。
          事實上多數時候得到構造方法的目的就是創建對象,這里只介紹一下創建對象。
          //static java.lang.Object ConstructorUtils.invokeConstructor
          //(java.lang.Class klass, java.lang.Object[] args)
          //根據一個java.lang.Class以及相應的構造方法的參數,創建一個對象。
           Object obj=ConstructorUtils.invokeConstructor(Month.class, {new Integer(1), "Jan"});
           Month month=(Month)obj;
           try {
             System.out.println(BeanUtils.getProperty(month,"value"));
           } catch (Exception e) {
             e.printStackTrace();
           }
          輸出證明,構造方法的調用是成功的。
          如果需要強制指定構造方法的參數類型,可以這樣調用:
             Object[] args={new Integer(1), "Jan"};
             Class[] argsType={int.class, String.class};
             Object obj;
             obj = ConstructorUtils.invokeExactConstructor(Month.class, args, argsType);
             Month month=(Month)obj;
             System.out.println(BeanUtils.getProperty(month,"value"));
          argsType指定了參數的類型。
          posted @ 2005-09-16 15:33 小海船 閱讀(1726) | 評論 (0)編輯 收藏

          1.在tomcat圖形界面里創建jndi數據源.(Tomcat Administration菜單里)
          2.在對應的web應用工程下綁定該連接源.例如假設該Web應用項目名為SpringMVC.則在Tomcat5.X系列下的對應配置文件名(包含指定的路徑:C:\Tomcat 5.0\conf\Catalina\localhost\SpringMVC.xml)
          3.SpringMVC.xml內容為:
            <?xml version='1.0' encoding='utf-8'?>
          <Context docBase="E:/workspace/SpringMVC" path="/SpringMVC" reloadable="true" workDir="E:\workspace\SpringMVC\work">
            <!--Resource auth="Container" name="jdbc/spring" type="javax.sql.DataSource"/-->
           <!--應用工程,指定綁定的jndi-->
            <ResourceLink name="jdbc/spring" global="jdbc/spring" type="javax.sql.DataSource" />
          </Context>
          4.Tomcat5.x下的配置數據源在,server.xml文件中.
          5.Spring利用該連接源,進行數據庫操作.(如何配置)
             1.在該Web應用工程下的web.xml中配置Spring控制臺
             2.web.xml文件的內容
                <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'>
             <web-app>    
              <servlet>
              <servlet-name>HelloWorld</servlet-name>
              <servlet-class>HelloWorld</servlet-class>
              </servlet>
              <servlet>
              <servlet-name>springapp</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/springappservlet.xml</param-value>
              </init-param>        
              </servlet>    
              <servlet-mapping>
                <servlet-name>HelloWorld</servlet-name>
                <!-- 字母區分大小寫-->
                <url-pattern>/servlet/HelloWorld</url-pattern>
                 </servlet-mapping>
                <servlet-mapping>
                <servlet-name>springapp</servlet-name>
                <url-pattern>*.do</url-pattern>
             </servlet-mapping>
             <!--(過濾器) 對提交的數據進行統一的編碼-->
             <filter>
             <filter-name>encoding</filter-name>
             <filter-class>EncodingFilter</filter-class>
             <init-param>
             <param-name>encoding</param-name>
             <param-value>gb2312</param-value>
             </init-param>
             </filter> 
             <filter-mapping>
             <filter-name>encoding</filter-name>
             <url-pattern>/*</url-pattern> 
             </filter-mapping>    
          </web-app>

            3.創建對應的spring配置即Web.xml中指定的目錄下的配置文件>/WEB-INF/springappservlet.xml
               <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "

          <!-- SelectMethod=cursor :打開游標-->
          <!-- 用SQL Server驅動一次select很多數據最好在
          connection string中加上SelectMethod=Cursor,
          以利用服務器端游標加快速度,其實不只sqlserver,
          oracle的jdbc,只要使用PreparedStatement,
          驅動默認就使用游標,sqlserver則不然,
          必須使用SelectMethod=Cursor才打開游標-->
          <beans>
              <!-- bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">
               <property name="driverClassName">
               <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
               </property>
               <property name="url">
               <value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=Northwind;SelectMethod=cursor</value>
               </property>
               <property name="username">
               <value>sa</value>
               </property>
               <property name="password">
               <value>628418</value>
               </property>
              </bean-->
              <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
              <property name="jndiName">
                      <value>java:comp/env/jdbc/spring</value>
                  </property>
              </bean>    
              <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                  <property name="dataSource">
                      <ref bean="dataSource"/>
                  </property>
              </bean>
                                                                                         
              <bean id="userDAO" class="UserDAO">
                  <property name="jdbcTemplate">
                      <ref bean="jdbcTemplate"/>
                  </property>       
              </bean>    
              <bean id="userAction" class="UserAction">
               <property name="jdbcTemplate">
                      <ref bean="jdbcTemplate"/>
               </property>
               <property name="commandClass">
                      <value>UserInfo</value>
               </property>
                <property name="fail_view">
                      <value>/WEB-INF/jsp/fail.jsp</value>
               </property>
                <property name="success_view">
                      <value>/WEB-INF/jsp/success.jsp</value>
               </property>    
              </bean>
              <bean id="springappController" class="SpringappController"/>
              <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                  <property name="viewClass">
                      <value>org.springframework.web.servlet.view.InternalResourceView</value>
                  </property>
              </bean>
               <bean id="helloUserAction" class="HelloUserAction">
                  <property name="helloWord">
                      <value>Hello!</value>
                  </property>
                  <property name="viewPage">
                      <value>/WEB-INF/jsp/hellouser.jsp</value>
                  </property>
              </bean>
              <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
                  <!-- 控制器模式-->
                  <property name="mappings">
                      <props>
                          <prop key="/hello.do">springappController</prop>
                          <prop key="/hellouser.do">helloUserAction</prop>
                          <prop key="/insert.do">userAction</prop>
                      </props>
                  </property>
                  <!-- 攔截器模式>
                 <property name="interceptors">
                 <list>
                 <ref local="localeChangeInterceptor"/>
                <ref local="themeChangeInterceptor"/>
                <ref local="copyInterceptor"/>
                </list>
               </property-->       
              </bean>
          </beans>

            4.對應的應用UserAction
            import java.io.UnsupportedEncodingException;

          import org.springframework.jdbc.core.JdbcTemplate;
          import org.springframework.validation.BindException;
          import org.springframework.web.servlet.ModelAndView;
          import org.springframework.web.servlet.mvc.SimpleFormController;

          public class UserAction extends SimpleFormController {

           private String fail_view;

           private String success_view;

           private JdbcTemplate jdbcTemplate;

           protected ModelAndView onSubmit(Object cmd, BindException ex)
             throws Exception {
            System.out.println("--------go into UserAction-------");
            UserInfo userInfo = (UserInfo) cmd;
            System.out.println("name=" + userInfo.getName() + ",sex="
              + userInfo.getSex() + ",age=" + userInfo.getAge());
            if (login(userInfo) == 1) {   
             insertUser(userInfo);
             return new ModelAndView(this.success_view);
            }
            return new ModelAndView(this.fail_view);
           }

           public void insertUser(UserInfo user) throws UnsupportedEncodingException {
            System.out.println("-----------------");
            System.out.println("name=" + user.getName() + ",sex=" + user.getSex()
              + ",age=" + user.getAge());
            System.out.println("-----------------");
            //if(user.getName()!=null && !user.getName().equals(""))
            //user.setName(new String(user.getName().getBytes("ISO8859_1"),"GBK"));
            System.out.println("name=" + user.getName() + ",sex=" + user.getSex()
              + ",age=" + user.getAge());
            jdbcTemplate.update("INSERT INTO USERS VALUES('" + user.getSex()
              + "', '" + user.getName() + "', '" + user.getAge() + "')");
           }

           private int login(UserInfo userInfo) {
            if (userInfo.getName().equals("李沖")) {
             System.out.println("11");
             return 0;
            } else
             return 1;
           }

           public String getFail_view() {
            return fail_view;
           }

           public void setFail_view(String fail_view) {
            this.fail_view = fail_view;
           }

           public String getSuccess_view() {
            return success_view;
           }

           public void setSuccess_view(String success_view) {
            this.success_view = success_view;
           }

           public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
           }
          }


               

          posted @ 2005-09-08 08:46 小海船 閱讀(1294) | 評論 (0)編輯 收藏

          1.在執行spring MVC代碼時,報出servlet init()錯誤,根據報錯信息,一般可以判斷是服務器不支持spring.花了我一天.
          2.tomcat4.1不支持spring MVC,換tomcat5.0即可.
          3,看準錯誤提示,如編譯不通過錯誤,和服務器本身不支持錯誤.
          4,由于spring MVC可以代替structs ,所以以后建立的框架結構,以spring+hibernate為主 .
          花了一天總結出的.
          posted @ 2005-09-06 20:17 小海船 閱讀(179) | 評論 (0)編輯 收藏

          package net.spring.test;

          import java.lang.reflect.Method;

          import net.spring.util.LogInitiate;
          import net.spring.util.Logger;

          import org.springframework.context.ApplicationContext;
          import org.springframework.context.support.FileSystemXmlApplicationContext;

          /**
           * @author chengli 無侵入式的可擴展框架:無需代碼中涉及Spring類,即可納入Spring容器進行管理
           *         核心類BeanFactory作為依賴注入機制的實現基礎
           */
          public class QuickStart {

           public static void main(String[] args) {

            // BasicConfigurator.configure();
            LogInitiate.initialize();
            Logger logger = new Logger(QuickStart.class);
            try {
             ApplicationContext ctx = new FileSystemXmlApplicationContext(
               "bean.xml");
             // 從ctx中讀取的配置信息區分大小寫
             // Spring通過依賴注入機制,將依賴關系從編碼中脫離,從而大大降低組件之間的耦合
             // 通過接口來將調用者和實現者分離
             Action action = (Action) ctx.getBean("UpperAction");
             logger.info(action.execute(" Mis JohSon"));
             action = (Action) ctx.getBean("LowerAction");
             logger.info(action.execute(" Mis JohSon"));
            } catch (Exception e) {
             // e.printStackTrace();
             logger.error(e.getMessage());
            }
           }

           public Object reflection(String className,String methodName ,String nameValue) {
            Class cls;
            Object obj = null;
            try {
             cls = Class.forName(className);
             Method mtd = cls.getMethod(methodName, new Class[] { String.class });
             obj = (Object) cls.newInstance();
             mtd.invoke(obj, new Object[] { nameValue });
             return obj;
            } catch (Exception e) {
             e.printStackTrace();
            }
            return obj;
           }
          }

          posted @ 2005-09-06 08:39 小海船 閱讀(282) | 評論 (0)編輯 收藏

          select  n_accountid,accountNO,accountName ,
          sum(decode(n_direction,1,n_amount, 0)) amountOfDebit,
          sum(decode(n_direction,2,n_amount, 0)) amountOfCredit,
          sum(n_amount) amount,
          sum(decode(n_direction,1,1, 0)) numOfDebitTrans,
          sum(decode(n_direction,2,1, 0)) numOfCreditTrans,
          COUNT(n_direction) nums
          from

          (select a.n_accountid n_accountid ,
          a.n_amount n_amount,a.n_direction n_direction,
          b.s_accountno accountNO,b.s_accountname  accountName from

          (select n_id ,n_accountid,n_currencytype,n_amount,n_direction,dt_modifytime from bs_accthistransinfo where 1=1
           and to_char(dt_modifytime,'yyyy-mm-dd')>='2005-01-01'
           and to_char(dt_modifytime,'yyyy-mm-dd')<='2006-01-01'
           union all
          select n_id ,n_accountid,n_currencytype,n_amount,n_direction,dt_modifytime from bs_acctcurtransinfo where 1=1
           and to_char(dt_modifytime,'yyyy-mm-dd')>='2005-01-01'
           and to_char(dt_modifytime,'yyyy-mm-dd')<='2006-01-01'
          )a,  

          (select n_id,s_accountno,s_accountname,n_currencytype from bs_bankaccountinfo where
              n_ischeck=1
           and n_accountstatus= 1)
           b
           where a.n_currencytype=b.n_currencytype  and a.n_accountid=b.n_id
          )
          c
          group by n_accountid,accountNO,accountName


           

          posted @ 2005-08-25 16:31 小海船 閱讀(181) | 評論 (1)編輯 收藏

          在java對oracle的操作中,日期字段是很頭疼的事情,其實仔細研究一下也并不難掌握。

          舉個例子來說明:

          表 book  中有name varchar2(20)//書籍名稱,buydate Date //購買日期 兩個字段。

          已經創建了數據庫連接Connection conn;

          方法一、使用java.sql.Date實現比較簡單的yyyy-mm-dd格式日期。

          java.sql.Date不支持時間格式。切記不要使用new java.sql.Date(int year,int month,int date),因為還要處理時間差問題。

          PreparedStatement pstmt = conn.prepareStatement("insert into book (name,buydate) values (?,?)");

          java.sql.Date buydate=java.sql.Date.valueOf("2005-06-08");
          pstmt.setString(1, "Java編程思想");
          pstmt.setDate(2,buydate );
          pstmt.execute();

          方法二、使用java.sql.Timestamp,同上不使用new Timestamp(....)

          PreparedStatement pstmt = conn.prepareStatement("insert into book (name,buydate) values (?,?)");

          java.sql.Timestamp buydate=java.sql.Timestamp.valueOf("2004-06-08 05:33:99");
          pstmt.setString(1, "Java編程思想");
          pstmt.setTimestamp(2,buydate );
          pstmt.execute();

          方法三、使用oracle 的to_date內置函數

          PreparedStatement pstmt = conn.prepareStatement("insert into book (name,buydate) values (?,to_date(?, 'yyyy-mm-dd hh24:mi:ss')");

          String buydate="2004-06-08 05:33:99";
          pstmt.setString(1, "Java編程思想");
          pstmt.setString(2,buydate );
          pstmt.execute();

          附:oracle日期格式參數 含義說明  
          d: 一周中的星期幾  
          day: 天的名字,使用空格填充到9個字符  
          dd: 月中的第幾天  
          ddd: 年中的第幾天  
          dy: 天的簡寫名  
          iw: ISO標準的年中的第幾周  
          iyyy: ISO標準的四位年份  
          yyyy: 四位年份  
          yyy,yy,y: 年份的最后三位,兩位,一位  
          hh: 小時,按12小時計  
          hh24: 小時,按24小時計  
          mi: 分  
          ss: 秒  
          mm: 月  
          mon: 月份的簡寫  
          month: 月份的全名  
          w: 該月的第幾個星期  
          ww: 年中的第幾個星期

          posted @ 2005-08-17 20:37 小海船 閱讀(321) | 評論 (0)編輯 收藏

          很多朋友在深入的接觸JAVA語言后就會發現這樣兩個詞:反射(Reflection)和內省(Introspector),經常搞不清楚這到底是怎么回事,在什么場合下應用以及如何使用?今天把這二者放在一起介紹,因為它們二者是相輔相成的。

          反射

          相對而言,反射比內省更容易理解一點。用一句比較白的話來概括,反射就是讓你可以通過名稱來得到對象(類,屬性,方法)的技術。例如我們可以通過類名來生成一個類的實例;知道了方法名,就可以調用這個方法;知道了屬性名就可以訪問這個屬性的值。

          還是寫兩個例子讓大家更直觀的了解反射的使用方法:

          //通過類名來構造一個類的實例
          Class cls_str = Class.forName("java.lang.String");
          //上面這句很眼熟,因為使用過JDBC訪問數據庫的人都用過J
          Object str = cls_str.newInstance();
          //相當于 String str = new String();

          //通過方法名來調用一個方法
          String methodName = "length";
          Method m = cls_str.getMethod(methodName,null);
          System.out.println("length is " + m.invoke(str,null));
          //相當于System.out.println(str.length());

          上面的兩個例子是比較常用方法??吹缴厦娴睦泳陀腥艘l問了:為什么要這么麻煩呢?本來一條語句就完成的事情干嗎要整這么復雜?沒錯,在上面的例子中確實沒有必要這么麻煩。不過你想像這樣一個應用程序,它支持動態的功能擴展,也就是說程序不重新啟動但是可以自動加載新的功能,這個功能使用一個具體類來表示。首先我們必須為這些功能定義一個接口類,然后我們要求所有擴展的功能類必須實現我指定的接口,這個規定了應用程序和可擴展功能之間的接口規則,但是怎么動態加載呢?我們必須讓應用程序知道要擴展的功能類的類名,比如是test.Func1,當我們把這個類名(字符串)告訴應用程序后,它就可以使用我們第一個例子的方法來加載并啟用新的功能。這就是類的反射,請問你有別的選擇嗎?

                 關于方法的反射建議大家看我的另外一篇文章《利用Turbine的事件映射來擴展Struts的功能》,地址是:http://www.javayou.com/article/CSDN/extend_struts.html。這篇文章詳細介紹了如果通過反射來擴展Struts框架的功能。

          內省

          內省是Java語言對Bean類屬性、事件的一種缺省處理方法。例如類A中有屬性name,那我們可以通過getName,setName來得到其值或者設置新的值。通過getName/setName來訪問name屬性,這就是默認的規則。Java中提供了一套API用來訪問某個屬性的getter/setter方法,通過這些API可以使你不需要了解這個規則(但你最好還是要搞清楚),這些API存放于包java.beans中。

          一般的做法是通過類Introspector來獲取某個對象的BeanInfo信息,然后通過BeanInfo來獲取屬性的描述器(PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的getter/setter方法,然后我們就可以通過反射機制來調用這些方法。下面我們來看一個例子,這個例子把某個對象的所有屬性名稱和值都打印出來:

          /* 
           * Created on 2004-6-29
           */

          package demo;

          import java.beans.BeanInfo;
          import java.beans.Introspector;
          import java.beans.PropertyDescriptor;

          /**
           * 內省演示例子
           * @author liudong
           */

          public class IntrospectorDemo {
              String name;
              public static void main(String[] args) throws Exception{
                  IntrospectorDemo demo = new IntrospectorDemo();
                  demo.setName("Winter Lau");       

                  //如果不想把父類的屬性也列出來的話,
                  //getBeanInfo的第二個參數填寫父類的信息
                  BeanInfo bi = Introspector.getBeanInfo(demo.getClass(),Object.class);
                  PropertyDescriptor[] props = bi.getPropertyDescriptors();
                  for(int i=0;i<props.length;i++){
                      System.out.println(props[i].getName()+"="+
                              props[i].getReadMethod().invoke(demo,null));
                  }

              }   

              public String getName() {
                  return name;
              }

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

          Web開發框架Struts中的FormBean就是通過內省機制來將表單中的數據映射到類的屬性上,因此要求FormBean的每個屬性要有getter/setter方法。但也并不總是這樣,什么意思呢?就是說對一個Bean類來講,我可以沒有屬性,但是只要有getter/setter方法中的其中一個,那么Java的內省機制就會認為存在一個屬性,比如類中有方法setMobile,那么就認為存在一個mobile的屬性,這樣可以方便我們把Bean類通過一個接口來定義而不用去關系具體實現,不用去關系Bean中數據的存儲。比如我們可以把所有的getter/setter方法放到接口里定義,但是真正數據的存取則是在具體類中去實現,這樣可提高系統的擴展性。

          總結

          Java的反射以及內省應用到程序設計中去可以大大的提供程序的智能化和可擴展性。有很多項目都是采取這兩種技術來實現其核心功能,例如我們前面提到的Struts,還有用于處理XML文件的Digester項目,其實應該說幾乎所有的項目都或多或少的采用這兩種技術。在實際應用過程中二者要相互結合方能發揮真正的智能化以及高度可擴展性。

          posted @ 2005-08-15 08:39 小海船 閱讀(232) | 評論 (0)編輯 收藏

          給出如下結論:

            在Class輸出字符串前,會將Unicode的字符串按照某一種內碼重新生成字節流,然后把字節流輸入,相當于進行了一步“String.getBytes(???)”操作。???代表某一種字符集。

            如果是Servlet,那么,這種內碼就是在HttpServletResponse.setContentType()方法中指定的內碼,也就是上文定義的<Servlet-charset>。

            如果是JSP,那么,這種內碼就是在<%@ page contentType=""%>中指定的內碼,也就是上文定義的<Jsp-charset>。

            如果是Java程序,那么,這種內碼就是file.encoding中指定的內碼,默認為ISO8859-1。

            當輸出對象是瀏覽器時

            以流行的瀏覽器IE為例。IE支持多種內碼。假如IE接收到了一個字節流“D6 D0 CE C4”,你可以嘗試用各種內碼去查看。你會發現用“簡體中文”時能得到正確的結果。因為“D6 D0 CE C4”本來就是簡體中文中“中文”兩個字的編碼。

            OK,完整地看一遍。

            JSP:源文件為GB2312格式的文本文件,且JSP源文件中有“中文”這兩個漢字

            如果指定了<Jsp-charset>為GB2312,轉化過程如下表。

            表4 Jsp-charset = GB2312時的變化過程

          序號 步驟說明 結果
          1 編寫JSP源文件,且存為GB2312格式 D6 D0 CE C4
          (D6D0=中 CEC4=文)
          2 jspc把JSP源文件轉化為臨時JAVA文件,并把字符串按照GB2312映射到Unicode,并用UTF格式寫入JAVA文件中 E4 B8 AD E6 96 87
          3 把臨時JAVA文件編譯成CLASS文件 E4 B8 AD E6 96 87
          4 運行時,先從CLASS文件中用readUTF讀出字符串,在內存中的是Unicode編碼 4E 2D 65 87(在Unicode中4E2D=中 6587=文)
          5 根據Jsp-charset=GB2312把Unicode轉化為字節流 D6 D0 CE C4
          6 把字節流輸出到IE中,并設置IE的編碼為GB2312(作者按:這個信息隱藏在HTTP頭中) D6 D0 CE C4
          7 IE用“簡體中文”查看結果 “中文”(正確顯示)

            如果指定了<Jsp-charset>為ISO8859-1,轉化過程如下表。

            表5 Jsp-charset = ISO8859-1時的變化過程

          序號 步驟說明 結果
          1 編寫JSP源文件,且存為GB2312格式 D6 D0 CE C4
          (D6D0=中 CEC4=文)
          2 jspc把JSP源文件轉化為臨時JAVA文件,并把字符串按照ISO8859-1映射到Unicode,并用UTF格式寫入JAVA文件中 C3 96 C3 90 C3 8E C3 84
          3 把臨時JAVA文件編譯成CLASS文件 C3 96 C3 90 C3 8E C3 84
          4 運行時,先從CLASS文件中用readUTF讀出字符串,在內存中的是Unicode編碼 00 D6 00 D0 00 CE 00 C4
          (啥都不是!?。。?/TD>
          5 根據Jsp-charset=ISO8859-1把Unicode轉化為字節流 D6 D0 CE C4
          6 把字節流輸出到IE中,并設置IE的編碼為ISO8859-1(作者按:這個信息隱藏在HTTP頭中) D6 D0 CE C4
          7 IE用“西歐字符”查看結果 亂碼,其實是四個ASCII字符,但由于大于128,所以顯示出來的怪模怪樣
          8 改變IE的頁面編碼為“簡體中文” “中文”(正確顯示)

            奇怪了!為什么把<Jsp-charset>設成GB2312和ISO8859-1是一個樣的,都能正確顯示?因為表4表5中的第2步和第5步互逆,是相互“抵消”的。只不過當指定為ISO8859-1時,要增加第8步操作,殊為不便。

            再看看不指定<Jsp-charset> 時的情況。

            表6 未指定Jsp-charset 時的變化過程

          序號 步驟說明 結果
          1 編寫JSP源文件,且存為GB2312格式 D6 D0 CE C4
          (D6D0=中 CEC4=文)
          2 jspc把JSP源文件轉化為臨時JAVA文件,并把字符串按照ISO8859-1映射到Unicode,并用UTF格式寫入JAVA文件中 C3 96 C3 90 C3 8E C3 84
          3 把臨時JAVA文件編譯成CLASS文件 C3 96 C3 90 C3 8E C3 84
          4 運行時,先從CLASS文件中用readUTF讀出字符串,在內存中的是Unicode編碼 00 D6 00 D0 00 CE 00 C4
          5 根據Jsp-charset=ISO8859-1把Unicode轉化為字節流 D6 D0 CE C4
          6 把字節流輸出到IE中 D6 D0 CE C4
          7 IE用發出請求時的頁面的編碼查看結果 視情況而定。如果是簡體中文,則能正確顯示,否則,需執行表5中的第8步

            Servlet:源文件為JAVA文件,格式是GB2312,源文件中含有“中文”這兩個漢字

            如果<Compile-charset>=GB2312,<Servlet-charset>=GB2312

            表7 Compile-charset=Servlet-charset=GB2312 時的變化過程

          序號 步驟說明 結果
          1 編寫Servlet源文件,且存為GB2312格式 D6 D0 CE C4
          (D6D0=中 CEC4=文)
          2 用javac –encoding GB2312把JAVA源文件編譯成CLASS文件 E4 B8 AD E6 96 87?。║TF)
          3 運行時,先從CLASS文件中用readUTF讀出字符串,在內存中的是Unicode編碼 4E 2D 65 87 (Unicode)
          4 根據Servlet-charset=GB2312把Unicode轉化為字節流 D6 D0 CE C4 (GB2312)
          5 把字節流輸出到IE中并設置IE的編碼屬性為Servlet-charset=GB2312 D6 D0 CE C4 (GB2312)
          6 IE用“簡體中文”查看結果 “中文”(正確顯示)

            如果<Compile-charset>=ISO8859-1,<Servlet-charset>=ISO8859-1

            表8 Compile-charset=Servlet-charset=ISO8859-1時的變化過程

          序號 步驟說明 結果
          1 編寫Servlet源文件,且存為GB2312格式 D6 D0 CE C4
          (D6D0=中 CEC4=文)
          2 用javac –encoding ISO8859-1把JAVA源文件編譯成CLASS文件 C3 96 C3 90 C3 8E C3 84?。║TF)
          3 運行時,先從CLASS文件中用readUTF讀出字符串,在內存中的是Unicode編碼 00 D6 00 D0 00 CE 00 C4
          4 根據Servlet-charset=ISO8859-1把Unicode轉化為字節流 D6 D0 CE C4
          5 把字節流輸出到IE中并設置IE的編碼屬性為Servlet-charset=ISO8859-1 D6 D0 CE C4 (GB2312)
          6 IE用“西歐字符”查看結果 亂碼(原因同表5)
          7 改變IE的頁面編碼為“簡體中文” “中文”(正確顯示)

            如果不指定Compile-charset或Servlet-charset,其默認值均為ISO8859-1。

            當Compile-charset=Servlet-charset時,第2步和第4步能互逆,“抵消”,顯示結果均能正確。讀者可試著寫一下Compile-charset<>Servlet-charset時的情況,肯定是不正確的。

            當輸出對象是數據庫時

            輸出到數據庫時,原理與輸出到瀏覽器也是一樣的。本節只是Servlet為例,JSP的情況請讀者自行推導。

            假設有一個Servlet,它能接收來自客戶端(IE,簡體中文)的漢字字符串,然后把它寫入到內碼為ISO8859-1的數據庫中,然后再從數據庫中取出這個字符串,顯示到客戶端。

            表9 輸出對象是數據庫時的變化過程(1)

          序號 步驟說明 結果
          1 在IE中輸入“中文” D6 D0 CE C4 IE
          2 IE把字符串轉變成UTF,并送入傳輸流中 E4 B8 AD E6 96 87
          3 Servlet接收到輸入流,用readUTF讀取 4E 2D 65 87(unicode) Servlet
          4 編程者在Servlet中必須把字符串根據GB2312還原為字節流 D6 D0 CE C4
          5 編程者根據數據庫內碼ISO8859-1生成新的字符串 00 D6 00 D0 00 CE 00 C4
          6 把新生成的字符串提交給JDBC 00 D6 00 D0 00 CE 00 C4
          7 JDBC檢測到數據庫內碼為ISO8859-1 00 D6 00 D0 00 CE 00 C4 JDBC
          8 JDBC把接收到的字符串按照ISO8859-1生成字節流 D6 D0 CE C4
          9 JDBC把字節流寫入數據庫中 D6 D0 CE C4
          10 完成數據存儲工作 D6 D0 CE C4 數據庫
          以下是從數據庫中取出數的過程
          11 JDBC從數據庫中取出字節流 D6 D0 CE C4 JDBC
          12 JDBC按照數據庫的字符集ISO8859-1生成字符串,并提交給Servlet 00 D6 00 D0 00 CE 00 C4 (Unicode)  
          13 Servlet獲得字符串 00 D6 00 D0 00 CE 00 C4 (Unicode) Servlet
          14 編程者必須根據數據庫的內碼ISO8859-1還原成原始字節流 D6 D0 CE C4  
          15 編程者必須根據客戶端字符集GB2312生成新的字符串 4E 2D 65 87
          (Unicode)
           
          Servlet準備把字符串輸出到客戶端
          16 Servlet根據<Servlet-charset>生成字節流 D6D0 CE C4 Servlet
          17 Servlet把字節流輸出到IE中,如果已指定<Servlet-charset>,還會設置IE的編碼為<Servlet-charset> D6 D0 CE C4
          18 IE根據指定的編碼或默認編碼查看結果 “中文”(正確顯示) IE

            解釋一下,表中第4第5步和第15第16步是用紅色標記的,表示要由編碼者來作轉換。第4、5兩步其實就是一句話:“new String(source.getBytes("GB2312"), "ISO8859-1")”。第15、16兩步也是一句話:“new String(source.getBytes("ISO8859-1"), "GB2312")”。親愛的讀者,你在這樣編寫代碼時是否意識到了其中的每一個細節呢?

            至于客戶端內碼和數據庫內碼為其它值時的流程,和輸出對象是系統控制臺時的流程,請讀者自己想吧。明白了上述流程的原理,相信你可以輕松地寫出來。

            行文至此,已可告一段落了。終點又回到了起點,對于編程者而言,幾乎是什么影響都沒有。

            因為我們早就被告之要這么做了。

            以下給出一個結論,作為結尾。

            1、 在Jsp文件中,要指定contentType,其中,charset的值要與客戶端瀏覽器所用的字符集一樣;對于其中的字符串常量,不需做任何內碼轉換;對于字符串變量,要求能根據ContentType中指定的字符集還原成客戶端能識別的字節流,簡單地說,就是“字符串變量是基于<Jsp-charset>字符集的”;

            2、 在Servlet中,必須用HttpServletResponse.setContentType()設置charset,且設置成與客戶端內碼一致;對于其中的字符串常量,需要在Javac編譯時指定encoding,這個encoding必須與編寫源文件的平臺的字符集一樣,一般說來都是GB2312或GBK;對于字符串變量,與JSP一樣,必須“是基于<Servlet-charset>字符集的”。

            出處: CSDN
            責任編輯: 方舟

          posted @ 2005-08-13 15:23 小海船 閱讀(105) | 評論 (0)編輯 收藏

          Servlet:從源文件到Class的過程

            Servlet源文件是以“.java”結尾的文本文件。本節將討論Servlet的編譯過程并跟蹤其中的中文變化。

            用“javac”編譯Servlet源文件。javac可以帶“-encoding <Compile-charset>”參數,意思是“用< Compile-charset >中指定的編碼來解釋Serlvet源文件”。

            源文件在編譯時,用<Compile-charset>來解釋所有字符,包括中文字符和ASCII字符。然后把字符常量轉變成Unicode字符,最后,把Unicode轉變成UTF。

            在Servlet中,還有一個地方設置輸出流的CharSet。通常在輸出結果前,調用HttpServletResponse的setContentType方法來達到與在JSP中設置<Jsp-charset>一樣的效果,稱之為<Servlet-charset>。

            注意,文中一共提到了三個變量:<Jsp-charset>、<Compile-charset>和<Servlet-charset>。其中,JSP文件只與<Jsp-charset>有關,而<Compile-charset>和<Servlet-charset>只與Servlet有關。

            看下例:

          import javax.servlet.*;

          import javax.servlet.http.*;

          class testServlet extends HttpServlet
          {
           public void doGet(HttpServletRequest req,HttpServletResponse resp)
           throws ServletException,java.io.IOException
           {
            resp.setContentType("text/html; charset=GB2312");
            java.io.PrintWriter out=resp.getWriter();
            out.println("<html>");
            out.println("#中文#");
            out.println("</html>");
           }
          }

            該文件也是用UltraEdit for Windows編寫的,其中的“中文”兩個字保存為“D6 D0 CE C4”(GB2312編碼)。

            開始編譯。下表是<Compile-charset>不同時,CLASS文件中“中文”兩字的十六進制碼。在編譯過程中,<Servlet-charset>不起任何作用。<Servlet-charset>只對CLASS文件的輸出產生影響,實際上是<Servlet-charset>和<Compile-charset>一起,達到與JSP文件中的<Jsp-charset>相同的效果,因為<Jsp-charset>對編譯和CLASS文件的輸出都會產生影響。

            表3 “中文”從Servlet源文件到Class的轉變過程

          Compile-charset Servlet源文件中 Class文件中 等效的Unicode碼
          GB2312 D6 D0 CE C4
          (GB2312)
          E4 B8 AD E6 96 87 (UTF) \u4E2D\u6587 (在Unicode中=“中文”)
          ISO-8859-1 D6 D0 CE C4
          (GB2312)
          C3 96 C3 90 C3 8E C3 84 (UTF) \u00D6 \u00D0 \u00CE \u00C4 (在D6 D0 CE C4前面各加了一個00)
          無(默認) D6 D0 CE C4 (GB2312) 同ISO-8859-1 同ISO-8859-1

            普通Java程序的編譯過程與Servlet完全一樣。

            CLASS文件中的中文表示法是不是昭然若揭了?OK,接下來看看CLASS又是怎樣輸出中文的呢?

            Class:輸出字符串

            上文說過,字符串在內存中表現為Unicode編碼。至于這種Unicode編碼表示了什么,那要看它是從哪種字符集映射過來的,也就是說要看它的祖先。這好比在托運行李時,外觀都是紙箱子,里面裝了什么就要看寄郵件的人實際郵了什么東西。

            看看上面的例子,如果給一串Unicode編碼“00D6 00D0 00CE 00C4”,如果不作轉換,直接用Unicode碼表來對照它時,是四個字符(而且是特殊字符);假如把它與“ISO8859-1”進行映射,則直接去掉前面的“00”即可得到“D6 D0 CE C4”,這是ASCII碼表中的四個字符;而假如把它當作GB2312來進行映射,得到的結果很可能是一大堆亂碼,因為在GB2312中有可能沒有(也有可能有)字符與00D6等字符對應(如果對應不上,將得到0x3f,也就是問號,如果對應上了,由于00D6等字符太靠前,估計也是一些特殊符號,真正的漢字在Unicode中的編碼從4E00開始)。

            各位看到了,同樣的Unicode字符,可以解釋成不同的樣子。當然,這其中有一種是我們期望的結果。以上例而論,“D6 D0 CE C4”應該是我們所想要的,當把“D6 D0 CE C4”輸出到IE中時,用“簡體中文”方式查看,就能看到清楚的“中文”兩個字了。(當然了,如果你一定要用“西歐字符”來看,那也沒辦法,你將得不到任何有何時何地的東西)為什么呢?因為“00D6 00D0 00CE 00C4”本來就是由ISO8859-1轉化過去的。
          posted @ 2005-08-13 15:22 小海船 閱讀(88) | 評論 (0)編輯 收藏

          什么是UTF

            UTF,是Unicode Text Format的縮寫,意為Unicode文本格式。對于UTF,是這樣定義的:

           ?。?)如果Unicode的16位字符的頭9位是0,則用一個字節表示,這個字節的首位是“0”,剩下的7位與原字符中的后7位相同,如“\u0034”(0000 0000 0011 0100),用“34” (0011 0100)表示;(與源Unicode字符是相同的);

           ?。?)如果Unicode的16位字符的頭5位是0,則用2個字節表示,首字節是“110”開頭,后面的5位與源字符中除去頭5個零后的最高5位相同;第二個字節以“10”開頭,后面的6位與源字符中的低6位相同。如“\u025d”(0000 0010 0101 1101),轉化后為“c99d”(1100 1001 1001 1101);

           ?。?)如果不符合上述兩個規則,則用三個字節表示。第一個字節以“1110”開頭,后四位為源字符的高四位;第二個字節以“10”開頭,后六位為源字符中間的六位;第三個字節以“10”開頭,后六位為源字符的低六位;如“\u9da7”(1001 1101 1010 0111),轉化為“e9b6a7”(1110 1001 1011 0110 1010 0111);

            可以這么描述JAVA程序中Unicode與UTF的關系,雖然不絕對:字符串在內存中運行時,表現為Unicode代碼,而當要保存到文件或其它介質中去時,用的是UTF。這個轉化過程是由writeUTF和readUTF來完成的。

            好了,基礎性的論述差不多了,下面進入正題。

            先把這個問題想成是一個黑匣子。先看黑匣子的一級表示:

          input(charsetA)->process(Unicode)->output(charsetB)

            簡單,這就是一個IPO模型,即輸入、處理和輸出。同樣的內容要經過“從charsetA到unicode再到charsetB”的轉化。

            再看二級表示:

          SourceFile(jsp,java)->class->output

            在這個圖中,可以看出,輸入的是jsp和java源文件,在處理過程中,以Class文件為載體,然后輸出。再細化到三級表示:

          jsp->temp file->class->browser,os console,db

          app,servlet->class->browser,os console,db

            這個圖就更明白了。Jsp文件先生成中間的Java文件,再生成Class。而Servlet和普通App則直接編譯生成Class。然后,從Class再輸出到瀏覽器、控制臺或數據庫等。

            JSP:從源文件到Class的過程

            Jsp的源文件是以“.jsp”結尾的文本文件。在本節中,將闡述JSP文件的解釋和編譯過程,并跟蹤其中的中文變化。

            1、JSP/Servlet引擎提供的JSP轉換工具(jspc)搜索JSP文件中用<%@ page contentType ="text/html; charset=<Jsp-charset>"%>中指定的charset。如果在JSP文件中未指定<Jsp-charset>,則取JVM中的默認設置file.encoding,一般情況下,這個值是ISO8859-1;

            2、jspc用相當于“javac –encoding <Jsp-charset>”的命令解釋JSP文件中出現的所有字符,包括中文字符和ASCII字符,然后把這些字符轉換成Unicode字符,再轉化成UTF格式,存為JAVA文件。ASCII碼字符轉化為Unicode字符時只是簡單地在前面加“00”,如“A”,轉化為“\u0041”(不需要理由,Unicode的碼表就是這么編的)。然后,經過到UTF的轉換,又變回“41”了!這也就是可以使用普通文本編輯器查看由JSP生成的JAVA文件的原因;

            3、引擎用相當于“javac –encoding UNICODE”的命令,把JAVA文件編譯成CLASS文件;

            先看一下這些過程中中文字符的轉換情況。有如下源代碼:

          <%@ page contentType="text/html; charset=gb2312"%>
          <html><body>
          <%
           String a="中文";
           out.println(a);
          %>
          </body></html>

            這段代碼是在UltraEdit for Windows上編寫的。保存后,“中文”兩個字的16進制編碼為“D6 D0 CE C4”(GB2312編碼)。經查表,“中文”兩字的Unicode編碼為“\u4E2D\u6587”,用 UTF表示就是“E4 B8 AD E6 96 87”。打開引擎生成的由JSP文件轉變而成的JAVA文件,發現其中的“中文”兩個字確實被“E4 B8 AD E6 96 87”替代了,再查看由JAVA文件編譯生成的CLASS文件,發現結果與JAVA文件中的完全一樣。

            再看JSP中指定的CharSet為ISO-8859-1的情況。

          <%@ page contentType="text/html; charset=ISO-8859-1"%>
          <html><body>
          <%
           String a="中文";
           out.println(a);
          %>
          </body></html>

            同樣,該文件是用UltraEdit編寫的,“中文”這兩個字也是存為GB2312編碼“D6 D0 CE C4”。先模擬一下生成的JAVA文件和CLASS文件的過程:jspc用ISO-8859-1來解釋“中文”,并把它映射到Unicode。由于ISO-8859-1是8位的,且是拉丁語系,其映射規則就是在每個字節前加“00”,所以,映射后的Unicode編碼應為“\u00D6\u00D0\u00CE\u00C4”,轉化成UTF后應該是“C3 96 C3 90 C3 8E C3 84”。好,打開文件看一下,JAVA文件和CLASS文件中,“中文”果然都表示為“C3 96 C3 90 C3 8E C3 84”。

            如果上述代碼中不指定<Jsp-charset>,即把第一行寫成“<%@ page contentType="text/html" %>”,JSPC會使用file.encoding的設置來解釋JSP文件。在RedHat 6.2上,其處理結果與指定為ISO-8859-1是完全相同的。

            到現在為止,已經解釋了從JSP文件到CLASS文件的轉變過程中中文字符的映射過程。一句話:從“JspCharSet到Unicode再到UTF”。下表總結了這個過程:

            表2 “中文”從JSP到CLASS的轉化過程

          Jsp-CharSet JSP文件中 JAVA文件中 CLASS文件中
          GB2312 D6 D0 CE C4(GB2312) 從\u4E2D\u6587(Unicode)到E4 B8 AD E6 96 87 (UTF) E4 B8 AD E6 96 87 (UTF)
          ISO-8859-1 D6 D0 CE C4
          (GB2312)
          從\u00D6\u00D0\u00CE\u00C4 (Unicode)到C3 96 C3 90 C3 8E C3 84 (UTF) C3 96 C3 90 C3 8E C3 84 (UTF)
          無(默認=file.encoding) 同ISO-8859-1 同ISO-8859-1 同ISO-8859-1

            下節先討論Servlet從JAVA文件到CLASS文件的轉化過程,然后再解釋從CLASS文件如何輸出到客戶端。之所以這樣安排,是因為JSP和Servlet在輸出時處理方法是一樣的。
          posted @ 2005-08-13 15:21 小海船 閱讀(89) | 評論 (0)編輯 收藏

          僅列出標題
          共3頁: 上一頁 1 2 3 下一頁 
          主站蜘蛛池模板: 贺兰县| 明光市| 微博| 禹州市| 闸北区| 喀什市| 蓝山县| 崇仁县| 南岸区| 黄骅市| 德州市| 盐源县| 普宁市| 阿克陶县| 奉新县| 简阳市| 甘洛县| 南江县| 衡阳县| 孝感市| 东阳市| 石景山区| 玛沁县| 安阳市| 台湾省| 长白| 惠州市| 桐庐县| 大港区| 大竹县| 西乌珠穆沁旗| 湖口县| 波密县| 准格尔旗| 鲁甸县| 农安县| 乐安县| 普安县| 镇远县| 嘉峪关市| 额尔古纳市|