posts - 9,  comments - 1,  trackbacks - 0
            2006年9月7日
          <SCRIPT language=javascript1.2>
          function showsubmenu(sid)
          {
          whichEl = eval("submenu" + sid);
          if (whichEl.style.display == "none")
          {
          eval("submenu" + sid + ".style.display=\"\";");
          }
          else
          {
          eval("submenu" + sid + ".style.display=\"none\";");
          }
          }
          </SCRIPT>


          用法:

          <tr>
          ??????????? <td height="26" background="../image/menu.gif" id="menuTitle1" onClick="showsubmenu(1)" style="cursor:hand;">&nbsp;&nbsp;&nbsp;
          ??????????? <font color="#FF9900">■&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="#FFFFFF"><strong>知識庫分類</strong></font>
          ??????????? </td>
          ????????? </tr>
          ????????? <tr>
          <td style="display:none" id='submenu1'>
          ???? <table cellspacing="0" align="center" width="100%">
          ??????????????? <tr>
          ????????????????? <td class="nav">· <a href="<%=request.getContextPath()%>/knowledge/knowledgeCategoryList.htm?order=list" target="mainFrame">編輯知識庫分類</a></td>
          ??????????????? </tr>
          ??????????????? <tr>
          ????????????????? <td class="nav">· <a href="<%=request.getContextPath()%>/knowledge/knowledgeCategoryList.htm?order=edit" target="mainFrame">新增知識庫分類</a></td>
          ??????????????? </tr>
          ????????????? </table>
          posted @ 2006-10-25 17:37 lovetq 閱讀(470) | 評論 (0)編輯 收藏
          在很多的列表中,后面的操作一欄會有刪除鏈接,并且會讓你確認。此處的代碼:?
          <a href='javascript:deleteCategory("<%=article.getId() %>")'>刪除</a>
          ?????

          javascript函數
          <script>
          ?function deleteCategory(articleId)
          ?{
          ??if(confirm('是否真的要刪除該記錄'))
          ???window.location='<%=request.getContextPath()%>/info/infoArticleEdit.htm?order=delete&articleId='+articleId;
          ?}
          </script>

          一些鏈接:
          <input class="button3" type=button value="返回" onclick="javascript:window.location='<%=request.getContextPath() %>/info/infoArticleList.htm'">

          <td>文件名:<a href='<%=request.getContextPath()%>/info/infoArticleEdit.htm?order=download&fileId=<%=accessory.getId() %>'><%=accessory.getFileExt() %></a>
          ?????
          <c:url value="........" />
          ??
          一些傳值
          <input type=hidden name=order value=save>
          <input type=hidden name=id value="<c:out value="${infoArticle.id}" />">
          <input type=hidden name=navId value="<%=request.getParameter("navId") %>">
          <input type=hidden name=fileCount value=1>
          posted @ 2006-10-25 16:32 lovetq 閱讀(181) | 評論 (0)編輯 收藏

          頁面代碼:
          <!--? 這個是顯示的多選框頁面 已有的角色要打勾,可以重新選擇角色-->
          <form action="<c:url value="/sys/userRolesEdit.htm"/>" method="post" >

          ????? <table class="grid" width="100%" cellspacing="1">
          ?????
          ??? <tr>
          ?????? <td colspan="6" class="title">
          ????????? 角色列表
          ?????? </td>
          ??? </tr>
          ??? <tr>
          ??? ? <td class="column"></td>
          ?????? <td class="column">角色ID</td>
          ?????? <td class="column">角色名</td>
          ?????? <td class="column">角色代碼</td>
          ?????? <td class="column">角色描述</td>??????

          ??? </tr>

          ??? <!-- 循環顯示列表userRoles的每一項,并判斷如果數據庫中已有的打勾<c:if test標簽正確才執行中間的代碼-->
          ??? <c:forEach items="${roles}" var="item">????
          ???<tr>??????
          ???? <td>
          ???? <input type="checkbox" name="rolesId" <c:forEach items="${userRoles}" var = "selectedRoles">
          ???? <c:if test="${item.srolId == selectedRoles.srolId}">checked="true"</c:if></c:forEach>
          ????? value="<c:out value="${item.srolId}"/>" />
          ???? </td>
          ???
          ???? <td><c:out value="${item.srolId}"/></td>
          ??????? <td><c:out value="${item.srolName}"/> </td>
          ???????????????? <td><c:out value="${item.srolCode}"/></td>
          ???????????????? <td><c:out value="${item.srolDesc}"/></td>????????????????
          ???? <tr>???????
          ??? </c:forEach> ??
          ???
          ??? <tr>

          ?????? <td colspan="6" class="nav">&nbsp;
          ?????? ??? <input type="hidden" name="userID"
          ???value=<c:out value="${userInfo.suinId}"/>>
          ????? <input type="submit" class="button3" value="提交"/>
          ????? <INPUT type="button" class="button2" value="返回" onclick="history.back()" />
          ?????
          ?????? </td>
          ??? </tr>
          ?? </table>?
          ?? </form>

          控制器代碼:
          protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
          ??// TODO Auto-generated method stub
          ??
          ??Map result = new HashMap();
          ??
          ??SysUserinfo userInfo = null;
          ??????? String id = request.getParameter("userID");

          ??????? System.out.println(id);
          ??????? if (id != null && !id.equals(""))
          ??????? {
          ??????????? userInfo = (SysUserinfo) sysService.get(SysUserinfo.class,
          ??????????????????? new Long(id));
          ???????????
          ??????? }
          ??????? else
          ??????? {
          ???????????
          ??????? }
          ???????
          ??????? result.put("userInfo", userInfo);
          ??????? result.put("roles", sysService.getSupportData("SysRole", null));
          ??????? if (userInfo != null)
          ??????? {
          ??????? ?result.put("userRoles", sysService.getUserRoles(userInfo));
          ??????? }
          ???????
          ?
          ???????
          ??????? SysUserRole sysUserRole = null;
          ??????? SysRole sysRole = null;
          ??????? String roleId = null;
          ???????
          ??????? // 記錄父列表ID
          ??????? result.put("parentlistID", request.getParameter("parentlistID"));
          ??????? result.put("userInfo", userInfo);
          ????????
          ?????????//遍歷所有map里的key
          ??????? Iterator it = result.keySet().iterator();
          ??????? while (it.hasNext())
          ??????? {
          ??????????? String key = (String) it.next();
          ??????????? request.setAttribute(key, result.get(key));
          ??????? }
          ????????
          ?????????//這里就是從列表多選框傳過來的數值。根據name屬性獲得所有的value
          ??????? String[] rolesId = null;
          ??????? rolesId = request.getParameterValues("rolesId");
          ??????? System.out.println("------------------------" + rolesId + "-------------------");
          ???????
          ??????? if (rolesId == null || rolesId.equals(null))
          ??????? {
          ??????? ?return new ModelAndView(formView);
          ??????? }
          ???????
          ??????? else
          ??????? {?????
          ??????? ?List userRoles = (List)sysService
          ??????? ??.getUsersUserRoles(userInfo.getSuinId());


          ??????//先刪除數據庫中所有的,再插入所選的值
          ?????????for (int k = 0; k < userRoles.size(); k++)
          ??? ??{
          ??? ???SysUserRole userRole = (SysUserRole)userRoles.get(k);?? ???
          ??? ???sysService.delete(userRole);
          ??? ???
          ??? ??}
          ??????? ?
          ??????? ?for (int i = 0; i < rolesId.length; i++)
          ??????? ?{
          ??????? ??roleId = rolesId[i];
          ??????? ??
          ??????? ??sysRole = (SysRole)sysService.get(SysRole.class, new Long(roleId));
          ??????? ??sysUserRole = new SysUserRole();
          ??????? ??sysUserRole.setUser(userInfo);
          ??????? ??sysUserRole.setRole(sysRole);
          ??????? ??sysService.save(sysUserRole);?????? ?
          ??????? ?}

          ?????????//如果在###-servlet.xml配置文件中,successView不能傳遞參數,可在這里直接有response導向別的頁面
          ?????????response.sendRedirect(request.getContextPath()
          ??????? ???+ "/sys/userRolesList.htm?userID=" + userInfo.getSuinId().toString());
          ??????? ?return null;
          ??????? }

          ?}


          posted @ 2006-10-25 09:27 lovetq 閱讀(1787) | 評論 (0)編輯 收藏
          StringTokenizer:字符串分隔解析類型
          屬于:java.util包。

          1、構造函數。

            1. StringTokenizer(String str) :構造一個用來解析str的StringTokenizer對象。java默認的分隔符是“空格”、“制表符(‘\t’)”、“換行符(‘\n’)”、“回車符(‘\r’)”。
            2. StringTokenizer(String str, String delim) :構造一個用來解析str的StringTokenizer對象,并提供一個指定的分隔符。
            3. StringTokenizer(String str, String delim, boolean returnDelims) :構造一個用來解析str的StringTokenizer對象,并提供一個指定的分隔符,同時,指定是否返回分隔符。


          2、方法。
          說明:
          1. 所有方法均為public;
          2. 書寫格式:[修飾符] <返回類型> <方法名([參數列表])>

          如:
          static int parseInt(String s) 表示:此方法(parseInt)為類方法(static),返回類型為(int),方法所需參數為String類型。


            1. int countTokens() :返回nextToken方法被調用的次數。如果采用構造函數1和2,返回的就是分隔符數量(例2)。
            2. boolean hasMoreTokens() :返回是否還有分隔符。
            3. boolean hasMoreElements() :結果同2。
            4. String nextToken() :返回從當前位置到下一個分隔符的字符串。
            5. Object nextElement() :結果同4。
            6. String nextToken(String delim) :與4類似,以指定的分隔符返回結果。


          例子:
          		String s = new String("The Java platform is the ideal platform for network computing");
          StringTokenizer st = new StringTokenizer(s);
          System.out.println( "Token Total: " + st.countTokens() );
          while( st.hasMoreElements() ){
          System.out.println( st.nextToken() );
                     }
          結果為:
          Token Total: 10
          The
          Java
          platform
          is
          the
          ideal
          platform
          for
          network
          computing

          例2:
          		String s = new String("The=Java=platform=is=the=ideal=platform=for=network=computing");
          StringTokenizer st = new StringTokenizer(s,"=",true);
          System.out.println( "Token Total: " + st.countTokens() );
          while( st.hasMoreElements() ){
          System.out.println( st.nextToken() );
          }
          結果為:
          Token Total: 19
          The
          =
          Java
          =
          platform
          =
          is
          =
          the
          =
          ideal
          =
          platform
          =
          for
          =
          network
          =
          computing
          posted @ 2006-10-08 17:33 lovetq 閱讀(268) | 評論 (0)編輯 收藏
          jsp
          JSP語法(9)——jsp:getProperty

          <jsp:getProperty>

          獲取Bean的屬性值,用于顯示在頁面中

          JSP 語法

          <jsp:getProperty name="beanInstanceName" property="propertyName" />

          例子

          <jsp:useBean id="calendar" scope="page" class="employee.Calendar" />

          <h2>

          Calendar of <jsp:getProperty name="calendar" property="username" />

          </h2>

          描述

          這個<jsp:getProperty>元素將獲得Bean的屬性值,并可以將其使用或顯示在JSP頁面中.在你使用<jsp:getProperty>之前,你必須用<jsp:useBean>創建它.

          <jsp:getProperty>元素有一些限制:

          你不能使用<jsp:getProperty>來檢索一個已經被索引了的屬性

          你能夠和JavaBeans組件一起使用<jsp:getProperty>,但是不能與Enterprise

          Bean一起使用。

          屬性

          name="beanInstanceName"
          bean的名字,由<jsp:useBean>指定

          property="propertyName"
          所指定的Bean的屬性名。

          技巧:

          在sun的JSP參考中提到,如果你使用<jsp:getProperty>來檢索的值是空值,那么NullPointerException將會出現,同時如果使用程序段或表達式來檢索其值,那么在瀏覽器上出現的是null(空).



          jsp:getProperty Action

          語法:

            <jsp:getProperty name="beanInstanceName" property="propertyName" />

            這個屬性檢索出bean的屬性的值并將之轉化為一個字符串,然后將之插入到輸出。它有兩個必選屬性:name,在之前用jsp:useBean引入的名稱,property,必須被插入值的屬性。

          posted @ 2006-09-21 13:46 lovetq 閱讀(161) | 評論 (0)編輯 收藏

          java.util.*;

          public class ShowDate {

          public static void main(String[] args) {
          Calendar calendar = new GregorianCalendar();
          Date trialTime = new Date();
          calendar.setTime(trialTime);

          // print out a bunch of interesting things
          System.out.println("ERA: " + calendar.get(Calendar.ERA));
          System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
          System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
          System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
          System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
          System.out.println("DATE: " + calendar.get(Calendar.DATE));
          System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
          System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
          System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
          System.out.println("DAY_OF_WEEK_IN_MONTH: " + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
          System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
          System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
          System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
          System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
          System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
          System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
          System.out.println("ZONE_OFFSET: " + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000)));
          System.out.println("DST_OFFSET: " + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000)));

          System.out.println("Current Time, with hour reset to 3");
          calendar.clear(Calendar.HOUR_OF_DAY); // so doesn't override
          calendar.set(Calendar.HOUR, 3);
          System.out.println("ERA: " + calendar.get(Calendar.ERA));
          System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
          System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
          System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
          System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
          System.out.println("DATE: " + calendar.get(Calendar.DATE));
          System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
          System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
          System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
          System.out.println("DAY_OF_WEEK_IN_MONTH: " + calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
          System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
          System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
          System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
          System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
          System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
          System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
          System.out.println("ZONE_OFFSET: " + (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); // in hours
          System.out.println("DST_OFFSET: " + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours
          }

          }


          import java.text.DateFormat;
          import java.util.*;

          public class JspCalendar {
          ??? Calendar? calendar = null;

          ??? public JspCalendar() {
          ?calendar = Calendar.getInstance();
          ?Date trialTime = new Date();
          ?calendar.setTime(trialTime);
          ??? }

          ??? public int getYear() {
          ?return calendar.get(Calendar.YEAR);
          ??? }
          ???
          ??? public String getMonth() {
          ?int m = getMonthInt();
          ?String[] months = new String [] { "January", "February", "March",
          ?????"April", "May", "June",
          ?????"July", "August", "September",
          ?????"October", "November", "December" };
          ?if (m > 12)
          ???? return "Unknown to Man";
          ?
          ?return months[m - 1];

          ??? }

          ??? public String getDay() {
          ?int x = getDayOfWeek();
          ?String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",
          ????????? "Thursday", "Friday", "Saturday"};

          ?if (x > 7)
          ???? return "Unknown to Man";

          ?return days[x - 1];

          ??? }
          ???
          ??? public int getMonthInt() {
          ?return 1 + calendar.get(Calendar.MONTH);
          ??? }

          ??? public String getDate() {
          ?return getMonthInt() + "/" + getDayOfMonth() + "/" +? getYear();

          ??? }

          ??? public String getTime() {
          ?return getHour() + ":" + getMinute() + ":" + getSecond();
          ??? }

          ??? public int getDayOfMonth() {
          ?return calendar.get(Calendar.DAY_OF_MONTH);
          ??? }

          ??? public int getDayOfYear() {
          ?return calendar.get(Calendar.DAY_OF_YEAR);
          ??? }

          ??? public int getWeekOfYear() {
          ?return calendar.get(Calendar.WEEK_OF_YEAR);
          ??? }

          ??? public int getWeekOfMonth() {
          ?return calendar.get(Calendar.WEEK_OF_MONTH);
          ??? }

          ??? public int getDayOfWeek() {
          ?return calendar.get(Calendar.DAY_OF_WEEK);
          ??? }
          ????
          ??? public int getHour() {
          ?return calendar.get(Calendar.HOUR_OF_DAY);
          ??? }
          ???
          ??? public int getMinute() {
          ?return calendar.get(Calendar.MINUTE);
          ??? }


          ??? public int getSecond() {
          ?return calendar.get(Calendar.SECOND);
          ??? }

          ??? public static void main(String args[]) {
          ?JspCalendar db = new JspCalendar();
          ?p("date: " + db.getDayOfMonth());
          ?p("year: " + db.getYear());
          ?p("month: " + db.getMonth());
          ?p("time: " + db.getTime());
          ?p("date: " + db.getDate());
          ?p("Day: " + db.getDay());
          ?p("DayOfYear: " + db.getDayOfYear());
          ?p("WeekOfYear: " + db.getWeekOfYear());
          ?p("era: " + db.getEra());
          ?p("ampm: " + db.getAMPM());
          ?p("DST: " + db.getDSTOffset());
          ?p("ZONE Offset: " + db.getZoneOffset());
          ?p("TIMEZONE: " + db.getUSTimeZone());
          ??? }

          ??? private static void p(String x) {
          ?System.out.println(x);
          ??? }


          ??? public int getEra() {
          ?return calendar.get(Calendar.ERA);
          ??? }

          ??? public String getUSTimeZone() {
          ?String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",
          ?????????? "Mountain", "Central", "Eastern"};
          ?
          ?return zones[10 + getZoneOffset()];
          ??? }

          ??? public int getZoneOffset() {
          ?return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);
          ??? }


          ??? public int getDSTOffset() {
          ?return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
          ??? }

          ???
          ??? public int getAMPM() {
          ?return calendar.get(Calendar.AM_PM);
          ??? }
          }

          posted @ 2006-09-21 11:45 lovetq 閱讀(647) | 評論 (0)編輯 收藏
          <jsp:useBean id=“calculator“ scope=“request“ class=“com.jspdev.SimpleCalculator“>
          <jsp:setProperty name=“calculator“ property=“*“/>
          </jsp:useBean>

          (1)property=”*”
          設置Bean 屬性的快捷方式,在Bean 中屬性的名字,類型必須和request對象中的參數名稱相匹配。由于表單中傳過來的數據類型都是String 類型的,Jsp內在機制會把這些參數轉化成Bean屬性對應的類型。
          (2)property=“propertyName“
          使用request對象中的一個參數值來指定Bean中的一個屬性值。在這個語法中,property指定Bean 的屬性名,而且Bean 屬性和request參數的名字應相同。也就是說,如果在Bean 中有setUserName(String userName)方法,那么,propertyName的值就是“userName“

          <jsp:setProperty name="Name" property="*" />
          根據已提交表單中的數據,設置這個javaBean中相應的屬性值。
          *:JavaBean屬性的名稱和表單對象的名稱相同時才能夠賦值
          <jsp:setProperty name="Name" property="propertyName" value="PropertyValue" | param="ParameterName" />
          把JavaBean指定的屬性設為指定的值

          jsp:setProperty用來設置Bean的屬性值。
          有兩種方法使用
          1。在jsp:useBean之后2。在jsp:useBean實體這中間有區別的第一種情況jsp:setProperty
          不管是新生成的實例還是原來的對象,都會進行
          2。只有在新生成的情況下才可以執行
          <jsp:setProperty>中的name?應該和jsp:useBean中的id一樣的
          property="*"?表示用用戶輸入的所有值來匹配Bean中的屬性,當然你也可以指定的

          posted @ 2006-09-21 11:34 lovetq 閱讀(572) | 評論 (0)編輯 收藏
          ?
          ?理解spring(轉的別人的)


          最近研究Spring,她包含的編程思想讓我耳目一新。所以寫下這篇入門級文章供新手參考。我不是什么Spring的資深研究人員,我只是現學現賣。所以文章也只能是膚淺單薄,錯誤難免,還請見諒。
          一、????Spring誕生
          Spring是一個開源框架,目前在開源社區的人氣很旺,被認為是最有前途的開源框架之一。她是由Rod?Johnson創建的,她的誕生是為了簡化企業級系統的開發。說道Spring就不得不說EJB,因為Spring在某種意義上是EJB的替代品,她是一種輕量級的容器。用過EJB的人都知道EJB很復雜,為了一個簡單的功能你不得不編寫多個Java文件和部署文件,他是一種重量級的容器。也許你不了解EJB,你可能對“輕(重)量級”和“容器”比較陌生,那么這里我簡單介紹一下。
          1、什么是容器
          “容器”,這個概念困擾我好久。從學習Tomcat開始就一直對此感到困惑。感性的來講,容器就是可以用來裝東西的物品。那么在編程領域就是指用來裝對象(OO的思想,如果你連OO都不了解,建議你去學習OO先)的對象。然而這個對象比較特別,它不僅要容納其他對象,還要維護各個對象之間的關系。這么講可能還是太抽象,來看一個簡單的例子:
          代碼片斷1:
          1. public?class?Container?
          2. {
          3. ????public?void?init()
          4. ????{
          5. ????Speaker?s?=?new?Speaker();
          6. ????Greeting?g?=?new?Greeting(s);
          7. ????}
          8. }

          可以看到這里的Container類(容器)在初始化的時候會生成一個Speaker對象和一個Greeting對象,并且維持了它們的關系,當系統要用這些對象的時候,直接問容器要就可以了。這就是容器最基本的功能,維護系統中的實例(對象)。如果到這里你還是感到模糊的話,別擔心,我后面還會有相關的解釋。

          2、輕量級與重量級
          所謂“重量級”是相對于“輕量級”來講的,也可以說“輕量級”是相對于重量級來講的。在Spring出現之前,企業級開發一般都采用EJB,因為它提供的事務管理,聲明式事務支持,持久化,分布計算等等都“簡化”了企業級應用的開發。我這里的“簡化”打了雙引號,因為這是相對的。重量級容器是一種入侵式的,也就是說你要用EJB提供的功能就必須在你的代碼中體現出來你使用的是EJB,比如繼承一個接口,聲明一個成員變量。這樣就把你的代碼綁定在EJB技術上了,而且EJB需要JBOSS這樣的容器支持,所以稱之為“重量級”。
          相對而言“輕量級”就是非入侵式的,用Spring開發的系統中的類不需要依賴Spring中的類,不需要容器支持(當然Spring本身是一個容器),而且Spring的大小和運行開支都很微量。一般來說,如果系統不需要分布計算或者聲明式事務支持那么Spring是一個更好的選擇。

          二、????幾個核心概念
          在我看來Spring的核心就是兩個概念,反向控制(IoC),面向切面編程(AOP)。還有一個相關的概念是POJO,我也會略帶介紹。
          1、POJO
          我所看到過的POJO全稱有兩個,Plain?Ordinary?Java?Object,Plain?Old?Java?Object,兩個差不多,意思都是普通的Java類,所以也不用去管誰對誰錯。POJO可以看做是簡單的JavaBean(具有一系列Getter,Setter方法的類)。嚴格區分這里面的概念沒有太大意義,了解一下就行。
          2、????IoC
          IoC的全稱是Inversion?of?Control,中文翻譯反向控制或者逆向控制。這里的反向是相對EJB來講的。EJB使用JNDI來查找需要的對象,是主動的,而Spring是把依賴的對象注入給相應的類(這里涉及到另外一個概念“依賴注入”,稍后解釋),是被動的,所以稱之為“反向”。先看一段代碼,這里的區別就很容易理解了。
          代碼片段2:
          1. public?void?greet()
          2. {
          3. Speaker?s?=?new?Speaker();
          4. s.sayHello();
          5. }

          代碼片段3:
          1. public?void?greet()
          2. {
          3. Speaker?s?=?(Speaker)context.lookup("ejb/Speaker");
          4. s.sayHello();
          5. }

          代碼片段4:
          1. public?class?Greeting?
          2. {
          3. ????public?Speaker?s;
          4. ????public?Greeting(Speaker?s)
          5. ????{
          6. ????????this.s?=?s;
          7. ????}
          8. ????public?void?greet()
          9. ????{
          10. ????????s.sayHello();
          11. ????}
          12. }

          我們可以對比一下這三段代碼。其中片段2是不用容器的編碼,片段3是EJB編碼,片段4是Spring編碼。結合代碼片段1,你能看出來Spring編碼的優越之處嗎?也許你會覺得Spring的編碼是最復雜的。不過沒關系,我在后面會解釋Spring編碼的好處。
          這里我想先解釋一下“依賴注入”。根據我給的例子可以看出,Greeting類依賴Speaker類。片段2和片段3都是主動的去獲取Speaker,雖然獲取的方式不同。但是片段4并沒有去獲取或者實例化Speaker類,而是在greeting函數中直接使用了s。你也許很容易就發現了,在構造函數中有一個s被注入(可能你平時用的是,傳入)。在哪里注入的呢?請回頭看一下代碼片段1,這就是使用容器的好處,由容器來維護各個類之間的依賴關系(一般通過Setter來注入依賴,而不是構造函數,我這里是為了簡化示例代碼)。Greeting并不需要關心Speaker是哪里來的或是從哪里獲得Speaker,只需要關注自己分內的事情,也就是讓Speaker說一句問候的話。
          3、????AOP
          AOP全稱是Aspect-Oriented?Programming,中文翻譯是面向方面的編程或者面向切面的編程。你應該熟悉面向過程的編程,面向對象的編程,但是面向切面的編程你也許是第一次聽說。其實這些概念聽起來很玄,說到底也就是一句話的事情。
          現在的系統往往強調減小模塊之間的耦合度,AOP技術就是用來幫助實現這一目標的。舉例來說,假如上文的Greeting系統含有日志模塊,安全模塊,事務管理模塊,那么每一次greet的時候,都會有這三個模塊參與,以日志模塊為例,每次greet之后,都要記錄下greet的內容。而對于Speaker或者Greeting對象來說,它們并不知道自己的行為被記錄下來了,它們還是像以前一樣的工作,并沒有任何區別。只是容器控制了日志行為。如果這里你有點糊涂,沒關系,等講到具體Spring配置和實現的時候你就明白了。
          假如我們現在為Greeting系統加入一個Valediction功能,那么AOP模式的系統結構如下:
          G|RET|TIN|G
          V|ALE|DIT|ION
          |???|???|
          日志?安全?事務

          這些模塊是貫穿在整個系統中的,為系統的不同的功能提供服務,可以稱每個模塊是一個“切面”。其實“切面”是一種抽象,把系統不同部分的公共行為抽取出來形成一個獨立的模塊,并且在適當的地方(也就是切入點,后文會解釋)把這些被抽取出來的功能再插入系統的不同部分。
          從某種角度上來講“切面”是一個非常形象的描述,它好像在系統的功能之上橫切一刀,要想讓系統的功能繼續,就必須先過了這個切面。這些切面監視并攔截系統的行為,在某些(被指定的)行為執行之前或之后執行一些附加的任務(比如記錄日志)。而系統的功能流程(比如Greeting)并不知道這些切面的存在,更不依賴于這些切面,這樣就降低了系統模塊之間的耦合度。

          三、????Spring初體驗
          這一節我用一個具體的例子Greeting,來說明使用Spring開發的一般流程和方法,以及Spring配置文件的寫法。
          首先創建一個Speaker類,你可以把這個類看做是POJO。
          代碼片段5:
          1. public?class?Speaker?
          2. {
          3. ????public?void?sayHello()
          4. ????{
          5. ????????System.out.println("Hello!");
          6. ????}
          7. }
          再創建一個Greeting類。
          代碼片段6:
          1. public?class?Greeting?
          2. {
          3. ????private?Speaker?speaker;
          4. ????public?void?setSpeaker(Speaker?speaker)
          5. ????{
          6. ????????this.speaker?=?speaker;
          7. ????}
          8. ????public?void?greet()
          9. ????{
          10. ????????speaker.sayHello();
          11. ????}
          12. }

          然后要創建一個Spring的配置文件把這兩個類關聯起來。
          代碼片段7(applicationContext.xml):
          1. <?xml?version="1.0"?encoding="UTF-8"?>
          2. <!DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN//EN"?
          3. ????"http://www.springframework.org/dtd/spring-beans.dtd">
          4. <beans>
          5. ????<bean?id="Speaker"?class="Speaker"></bean>
          6. ????<bean?id="Greeting"?class="Greeting">
          7. ????????<property?name="speaker">
          8. ????????????<ref?bean="Speaker"/>
          9. ????????</property>
          10. ????</bean>
          11. </beans>

          要用Spring?Framework必須把Spring的包加入到Classpath中,我用的是Eclipse+MyEclipse,這些工作是自動完成的。推薦用Spring的配置文件編輯器來編輯,純手工編寫很容易出錯。我先分析一下這個xml文件的結構,然后再做測試。從<beans>節點開始,先聲明了兩個<bean>,第二個bean有一個speaker屬性(property)要求被注入,注入的內容是另外一個bean?Speaker。這里的命名是符合JavaBean規范的,也就是說如果是speaker屬性,那么Spring容器就會調用setSpeaker()來注入這個屬性。<ref>是reference的意思,表示引用另外一個bean。
          下面看一段簡單的測試代碼:
          代碼片段8:
          1. public?static?void?main(String[]?args)?
          2. {
          3. ????????ApplicationContext?context?=?
          4. ????????????New?ClassPathXmlApplicationContext("applicationContext.xml");
          5. ????????Greeting?greeting?=?(Greeting)context.getBean("Greeting");
          6. ????????greeting.greet();
          7. }

          這段代碼很簡單,如果你上文都看懂了,那么這里應該沒有問題。值得注意的是Spring有兩種方式來創建容器(我們不再用上文我們自己編寫的Container),一種是ApplicationContext,另外一種是BeanFactory。ApplicationContext更強大一些,而且使用上兩者沒有太大區別,所以一般說來都用ApplicationContext。Spring容器幫助我們維護我們在配置文件中聲明的Bean以及它們之間的依賴關系,我們的Bean只需要關注自己的核心業務。

          四、????面向接口的編程
          看了這么多,也許你并沒有覺得Spring給開發帶來了很多便利。那是因為我舉的例子還不能突出Spring的優越之處,接下來我將通過接口編程來體現Spring的強大。
          假如現在要求擴展Greeting的功能,要讓Speaker用不同的語言來問候,也就是說有不同的Speaker,比如ChineseSpeaker,?EnglishSpeaker。那么對上文提到的三種編碼方式(代碼片段2、3、4)分別加以修改,你會發現很麻煩。假如下次又要加入一個西班牙語,又得重復勞動。很自然的會考慮到使用一個ISpeaker接口來簡化工作,,更改后的代碼如下(這里沒有列出接口的相關代碼,我想你應該明白怎么寫):?????????

          代碼片段9:
          1. public?void?greet()
          2. {
          3. ISpeaker?s?=?new?ChineseSpeaker();
          4. s.sayHello();
          5. }

          代碼片段10:
          1. public?void?greet()
          2. {
          3. ISpeaker?s?=?(ISpeaker)context.lookup("ejb/ChineseSpeaker");
          4. s.sayHello();
          5. }

          代碼片段11:
          1. public?class?Greeting?
          2. {
          3. ????public?ISpeaker?s;
          4. ????public?Greet(ISpeaker?s)
          5. ????{
          6. ????????this.s?=?s;
          7. ????}
          8. ????public?void?greet()
          9. ????{
          10. ????????s.sayHello();
          11. ????}
          12. }
          對比三段代碼,你會發現,第一種方法還是把具體的Speaker硬編碼到代碼中了,第二中方法稍微好一點,但是沒有本質改變,而第三種方法就不一樣了,代碼中并沒有關于具體Speaker的信息。也就是說,如果下次還有什么改動的話,第三種方法的Greeting類是不需要修改,編譯的。根據上文Spring的使用介紹,只需要改動xml文件就能給Greeting注入不同的Speaker了,這樣代碼的擴展性是不是提高了很多?
          關于Spring的接口編程還有很多東西可以去挖掘,后文還會提到有關Spring?Proxy的接口編程,我這里先介紹這么多,有興趣話可以去google更多的資料。

          五、????應用Spring中的切面
          Spring生來支持AOP,首先來看幾個概念:
          1、????切面(Aspect):切面是系統中抽象出來的的某一個功能模塊,上文已經有過介紹,這里不再多說。
          2、????通知(Advice):通知是切面的具體實現。也就是說你的切面要完成什么功能,具體怎么做就是在通知里面完成的。這個名稱似乎有點讓人費解,等后面看了代碼就明白了。
          3、????切入點(Pointcut):切入點定義了通知應該應用到系統的哪些地方。Spring只能控制到方法(有的AOP框架可以控制到屬性),也就是說你能在方法調用之前或者之后選擇切入,執行額外的操作。
          4、????目標對象(Target):目標對象是被通知的對象。它可以是任何類,包括你自己編寫的或者第三方類。有了AOP以后,目標對象就只需要關注自己的核心業務,其他的功能,比如日志,就由AOP框架支持完成。
          5、????代理(Proxy):簡單的講,代理就是將通知應用到目標對象后產生的對象。Spring在運行時會給每個目標對象生成一個代理對象,以后所有對目標對象的操作都會通過代理對象來完成。只有這樣通知才可能切入目標對象。對系統的其他部分來說,這個過程是透明的,也就是看起來跟沒用代理一樣。
          我為了簡化,只介紹這5個概念。通過這幾個概念應該能夠理解Spring的切面編程了。如果需要深入了解Spring?AOP的話再去學習其他概念也很快的。
          下面通過一個實際的例子來說明Spring的切面編程。繼續上文Greeting的例子,我們想在Speaker每次說話之前記錄Speaker被調用了。
          首先創建一個LogAdvice類:
          代碼片段12:
          1. public?class?LogAdvice?implements?MethodBeforeAdvice?
          2. {
          3. ????public?void?before(Method?arg0,?Object[]?arg1,?Object?arg2)throws?Throwable?
          4. ????{
          5. ????????System.out.println("Speaker?called!");
          6. ????}
          7. }
          這里涉及到一個類,MethodBeforeAdvice,這個類是Spring類庫提供的,類似的還有AfterReturningAdvice等等,從字面就能理解它們的含義。先不急著理解這個類,我稍后解釋。我們繼續看如何把這個類應用到我們的系統中去。
          代碼片段13:
          1. <beans>
          2. ????<bean?id="Speaker"?class="Speaker"/>
          3. ????<bean?id="Greeting"?class="Greeting">
          4. ????????<property?name="speaker">
          5. ????????????<ref?bean="SpeakerProxy"/>
          6. ????????</property>
          7. ????</bean>
          8. ????<bean?id="LogAdvice"?class="LogAdvice"/>
          9. ????<bean?id="SpeakerProxy"?class="org.springframework.aop.framework.ProxyFactoryBean">
          10. ????????<property?name="proxyInterfaces">
          11. ????????????<value>ISpeaker</value>
          12. ????????</property>
          13. ????????<property?name="interceptorNames">
          14. ????????????<list>
          15. ????????????????<value>LogAdvice</value>
          16. ????????????</list>
          17. ????????</property>
          18. ????????<property?name="target">
          19. ????????????<ref?local="Speaker"/>
          20. ????????</property>
          21. ????</bean>
          22. </beans>

          可以看到我們的配置文件中多了兩個bean,一個LogAdvice,另外一個SpeakerProxy。LogAdvice很簡單。我著重分析一下SpeakerProxy。這個Bean實際上是由Spring提供的ProxyFactoryBean實現。下面定義了三個依賴注入的屬性。
          1、????proxyInterfactes:這個屬性定義了這個Proxy要實現哪些接口,可以是一個,也可以是多個(多個的話,要用list標簽)。我前面講過Proxy是在運行是動態創建的,那么這個屬性就告訴Spring創建這個Proxy的時候實現哪些接口。
          2、????interceptorNames:這個屬性定義了Proxy被切入了哪些通知,這里只有一個LogAdvice。
          3、????target:這個屬性定義了被代理的對象。在這個例子中target是Speaker。
          這樣的定義實際上約束了被代理的對象必須實現一個接口,這與上文講的面向接口的編程有點類似。其實可以這樣理解,接口的定義可以讓系統的其他部分不受影響,以前用ISpeaker接口來調用,現在加入了Proxy還是一樣的。但實際上內容已經不一樣了,以前是Speaker,現在是一個Proxy。而target屬性讓proxy知道具體的方法實現在哪里。Proxy可以看作是target的一個包裝。當然Spring并沒有強制要求用接口,通過CGLIB(一個高效的代碼生成開源類庫)也可以直接根據目標對象生成子類,但這種方式并不推薦。
          我們還像以前一樣的測試我們的Greeting系統,測試代碼和代碼片段8是一樣的。運行結果如下:
          Speaker?called!
          Hello!

          看到效果了吧!而且你可以發現,我們加入Log功能并沒有改變以前的代碼,甚至測試代碼都沒有改變,這就是AOP的魅力所在!我們更改的只是配置文件。
          下面解釋一下剛才落下的MethodBeforeAdvice。關于這個類我并不詳細介紹,因為這涉及到Spring中的另外一個概念“連接點(Jointpoint)”,我詳細介紹一個before這個方法。這個方法有三個參數arg0表示目標對象在哪個點被切入了,既然是MethodBeforeAdvice,那當然是在Method之前被切入了。那么arg0就是表示的那個Method。第二個參數arg1是Method的參數,所以類型是Object[]。第三個參數就是目標對象了,在Greeting例子中arg2的類型實際上是Speaker。
          在Greeting例子中,我們并沒有指定目標對象的哪些方法要被切入,而是默認切入所有方法調用(雖然Speaker只有一個方法)。通過自定義Pointcut,可以控制切入點,我這里不再介紹了,因為這并不影響理解Spring?AOP,有興趣的話去google一下就知道了。

          六、實戰Spring
          雖然這部分取名為“實戰Spring”,但實際上我并不打算在這里介紹實際開發Spring的內容,因為我寫這篇文章的目的是介紹Spring的概念和用Spring開發的思路,而不是有關Spring的實踐和詳細介紹。文中介紹的內容和用Spring做實際開發還相去甚遠。之所以取名“實戰Spring”是我覺得理解了上文講的內容以后,可以開始為深入學習Spring和學習如何在項目中應用Spring了。
          要系統的學習Spring還是需要閱讀一本詳細介紹Spring的書,我推薦Spring?in?Action,因為我看的就是這本書。希望這篇文章能為你系統的學習Spring掃除一些障礙。


          posted @ 2006-09-07 14:22 lovetq 閱讀(187) | 評論 (0)編輯 收藏
          主站蜘蛛池模板: 沁源县| 红安县| 桂平市| 安新县| 兰州市| 北京市| 木兰县| 温宿县| 科技| 昭觉县| 乳山市| 沧源| 普定县| 沁阳市| 屏边| 西充县| 交口县| 三亚市| 克什克腾旗| 墨脱县| 如皋市| 安阳市| 新和县| 苏尼特右旗| 永城市| 叶城县| 富蕴县| 凉城县| 磐安县| 淅川县| 永顺县| 永仁县| 应用必备| 杭锦后旗| 长宁区| 清水河县| 垣曲县| 河间市| 轮台县| 綦江县| 安徽省|