<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    海鷗航際

    JAVA站
    posts - 11, comments - 53, trackbacks - 1, articles - 102

    基于J2EE的Blog平臺

    Posted on 2005-01-12 13:36 海天一鷗 閱讀(570) 評論(1)  編輯  收藏 所屬分類: J2EE


    作者:xuefengl(dev2dev ID)
    摘要:

      本文講述如何在J2EE平臺上創建一個Blog系統,以.Text的功能和界面為原型,Springframework為框架,實現一個運行在WebLogic Server上的靈活的多層結構的Blog平臺。

    目錄:

    1. 設計目標
    2. 開發環境
    2.1. 選擇平臺和框架
    2.2. 配置服務器
    2.3. 編寫Ant腳本
    3. 系統設計
    3.1. 持久層設計
    3.1.1. 設計Domain對象
    3.1.2. 配置iBatis
    3.1.3. 使用DAO模式
    3.2. 邏輯層設計
    3.3. Web層設計
    3.3.1. 使用MVC模式
    3.3.2. 實現Skin
    4. 附加功能
    4.1.1. 實現圖片上傳
    4.1.2. 生成縮略圖
    4.1.3. 實現RSS
    4.1.4. 實現全文搜索
    4.1.5. 發送Email
    5. 測試
    6. 中文支持
    7. 總結
    8. 源代碼下載
    9. 相關資源下載
    10. 參考
    11. 關于作者

    設計目標 (目錄)

      Blog(WebLog)在Internet上越來越流行。許多網友都有了自己的Blog,通過Blog展示自己,結識更過的網友。比較著名的Blog平臺是基于ASP.net的開源項目.Text。但是它的邏輯全部以存儲過程的形式放在數據庫中。雖然存儲過程能大大提高數據操作的效率,但是存儲過程本身是結構化的程序,無法發揮面向對象的威力,也不便于實現代碼復用。因此,我決定實現一個基于J2EE體系的多層結構的Blog平臺,功能和界面和.Text非常類似,暫命名為Crystal Blog。實現的功能有:發表和編輯文章;多用戶支持;全文檢索;RSS支持;圖片管理;SMTP郵件發送等常見功能。界面如下:


    選擇平臺和框架 (目錄)
      由于使用J2EE平臺,我們準備采用WebLogic Server 8.1作為運行平臺,使用WebLogic Workshop8.1這個強大的集成化IDE作為開發工具。
      數據庫選擇MS SQL Server 2000 SP3,建立一個名為blog的數據庫存儲所有的用戶數據。由于我們并沒有針對特定數據庫編碼,稍后我們會使用其他數據庫測試。在系統設計之前,選擇一個優秀的框架能大大提高開發效率。Spring是一個輕量級的J2EE框架。它覆蓋了從后臺數據庫的JDBC封裝到前臺Web框架的幾乎所有方?面。并且,Spring的各個模塊耦合非常松散,我們既可以用它作為整個應用程序的框架,也可以僅僅使用它的某一個模塊。此外,Spring非常強大的集成功能使我們可以輕易地集成Struts編寫的Web端,或者使用Hibernate作為后端的O/R Mapping方案。
      Spring的核心思想便是IoC和AOP,Spring本身是一個輕量級容器,和EJB容器不同,Spring的組件就是普通的Java Bean,這使得單元測試可以不再依賴容器,編寫更加容易。Spring負責管理所有的Java Bean組件,同樣支持聲明式的事務管理。我們只需要編寫好Java Bean組件,然后將它們“裝配”起來就可以了,組件的初始化和管理均由Spring完成,只需在配置文件中聲明即可。這種方式最大的優點是各組件的耦合極為松散,并且無需我們自己實現Singleton模式。
      由于后臺要使用關系數據庫存儲數據,使用O/R Mapping必不可少。iBatis是又一個類似于Hibernate的O/R Mapping方案,特點是小巧,配置簡單,查詢靈活,完全符合我們的要求。
      除了Spring和iBatis,用到的第三方組件還有:用于全文搜索的Lucene引擎,用于文件上傳的common-file-upload 1.0,用于輸出RSS的RSSLibJ1.0 RC2。
      由于使用Spring這個輕量級框架,就無需EJB服務器,只需要Web服務器即可。因此,系統可以運行在WebLogic Server,Tomcat和Resin等支持Servlet和JSP的Web服務器上。

    系統設計 (目錄)
      很顯然,多層結構的J2EE架構能保證系統的靈活性和可擴展性。我們仍然采用表示層/邏輯層/持久層三層設計。

      整個系統以Spring為基礎,持久層采用DAO模式和iBatis O/R Mapping,封裝所有數據庫操作;中間層是由Spring管理的普通的JavaBean,采用Fa?ade模式;表示層使用Spring提供的MVC框架。由于Spring對其他框架的良好集成,我們采用Velocity作為View。由于Velocity不能調用Java代碼,從而強制使用MVC模式而不是在View中嵌入邏輯代碼。

    配置服務器 (目錄)
      在WebLogic中新建一個Configuration,命名為blog,添加一個數據源,命名為jdbc/blog:

      整個應用程序的目錄結構如下:

    crystalblog/
    + doc/ (存放API文檔)
    + report/ (存放JUnit測試結果)
    + src/ (存放java源程序)
    + web/ (web目錄)
    | + manage/ (存放blog管理頁)
    | + skin/ (存放blog界面頁)
    | + upload/ (存放用戶上傳的圖片)
    | + WEB-INF/
    |   + classes/ (存放編譯的class文件)
    |   + lib/ (存放用到的所有jar文件)
    |   + search/ (存放Lucene的index)
    |   + c.tld (使用jstl必須的文件)
    |   + dispatcher-servlet.xml (Spring配置文件)
    |   + web.xml (標準web配置文件)
    + blog.war (打包的可部署應用)
    + build.xml (ant腳本)

    編寫Ant?腳本 (目錄)
      Ant是一個非常棒的執行批處理任務的工具。使用Ant能使編譯、測試、打包、部署和生成文檔等一系列任務全自動化,從而大大節省開發時間。
      首先我們把用到的所有.jar文件放到/web/WEB-INF/lib中,然后編寫compile任務,生成的class文件直接放到web/WEB-INF/classes目錄下。如果編譯成功,就進行單元測試,單元測試的結果以文本文件存放在report目錄中。如果測試通過,下一步便是打包成blog.war文件。接著把應用部署到服務器上,直接將web目錄的內容復制到%BEA_HOME%/user_projects/domains/blogdomain/applications/blog/目錄下即可。如果要在Tomcat上部署,直接將整個web目錄復制到%TOMCAT%/webapps/blog/下。
      最后,如果需要,可以用javadoc生成api文檔。

    系統設計 (目錄)
      Crystal Blog共分成三層結構:后臺數據持久層,采用DAO模式;中間邏輯層,采用Facade模式;前端Web層,采用MVC結構,使用JSP作為視圖。以下是Rational Rose的UML圖:

    設計Domain對象 (目錄)

      設計Domain對象
      Domain層是抽象出的實體。根據我們要實現的功能,設計以下實體,它們都是普通的Java Bean:
      Account:封裝一個用戶,包括用戶ID,用戶名,口令,用戶設置等等。
      Category:封裝一個分類,一共有3種Category,分別用來管理Article,Image和Link,一個Account對應多個Category。
      Article:封裝一篇文章,包括Title,Summary,Content等等,一個Category對應多個Article。
      Feedback:封裝一個回復,包括Title,Username,Url和Content,一個Article對應多個Feedback。
      Image:封裝一個圖片,Image只包含圖片信息(ImageId,Type),具體的圖片是以用戶上傳到服務器的文件的形式存儲的。一個Category對應多個Image。
      Link:封裝一個鏈接,和Category是多對一的關系。有Title,Url,Rss等屬性。
      Message:封裝一個消息,使其他用戶在不知道Email地址的情況下能夠通過系統發送郵件給某個用戶。

      最后,為了唯一標識每條數據庫記錄,我們需要一個主鍵。在MS SQL Server和Oracle中可以使用自動遞增的主鍵生成方式。但是很多數據庫不支持自動遞增的主鍵,考慮到移植性,我們自己定義一個Sequence表,用于生成遞增的主鍵。Sequence表有且僅有7條記錄,分別記錄Account到Message對象的當前最大主鍵值。系統啟動時,由SqlConfig負責初始化Sequence表。
      SequenceDao負責提供下一個主鍵,為了提高效率,一次緩存10個主鍵。

    配置iBatis (目錄)
      接下來,使用iBatis實現O/R Mapping。首先從http://www.ibatis.com下載iBatis 2.0,將所需的jar文件復制到web/WEB-INF/lib/目錄下。iBatis使用XML配置數據庫表到Java對象的映射,先編寫一個sql-map-config.xml:

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE sqlMapConfig
      PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
      "http://www.ibatis.com/dtd/sql-map-config-2.dtd">
    <sqlMapConfig>
      <settings cacheModelsEnabled="false" enhancementEnabled="true"
        lazyLoadingEnabled="true" maxRequests="32"
        maxSessions="10" maxTransactions="5"
        useStatementNamespaces="false"
      />
      <transactionManager type="JDBC">
        <dataSource type="JNDI">
          <property name="DataSource" value="jdbc/blog" />
        </dataSource>
      </transactionManager>
      <!-- 如果有其他xml配置文件,可以包含進來 -->
      <sqlMap resource="Account.xml" />
    </sqlMapConfig>

      將sql-map-config.xml放到web/WEB-INF/classes/目錄下,iBatis就能搜索到這個配置文件,然后編寫一個初始化類:

    public class SqlConfig {
        private SqlConfig() {}
        private static final SqlMapClient sqlMap;
        static {
            try {
                java.io.Reader reader = Resources.getResourceAsReader ("sql-map-config.xml");
                sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Error initializing SqlConfig. Cause: " + e);
            }
        }
        public static SqlMapClient getSqlMapInstance () {
            return sqlMap;
        }
    }


      SqlMapClient封裝了訪問數據庫的大部分操作,可以直接使用SqlConfig.getSqlMapInstance()獲得這個唯一實例。

    使用DAO模1 式 (目錄)
      為了分離邏輯層和數據庫持久層,定義一系列DAO接口:AccountDao,CategoryDao,ArticleDao……其實現類對應為SqlMapAccountDao,SqlMapCategoryDao,SqlMapArticleDao……這樣就使得邏輯層完全脫離了數據庫訪問代碼。如果將來需要使用其它的O/R Mapping方案,直接實現新的DAO接口替代現有的SqlMapXxxDao即可。
      以SqlMapAccountDao為例,實現一個login()方法是非常簡單的:

    public int login(String username, String password) throws AuthorizationException {
        try {
            Map map = new HashMap();
            map.put("username", username);
            map.put("password", password);
            Integer I = (Integer)sqlMap.queryForObject("login", map);
            if(I==null)
                throw new RuntimeException("Failed: Invalid username or password.");
            return I.intValue();
        }
        catch(SQLException sqle) {
            throw new RuntimeException("Sql Exception: " + sqle);
        }
    }

      在Account.xml配置文件中定義login查詢:

    <select id="login" parameterClass="java.util.Map" resultClass="int">
      select [accountId] from [Account] where
      [username] = #username# and password = #password#
    </select>

    邏輯層設計 (目錄)
      由于DAO模式已經實現了所有的數據庫操作,業務邏輯主要是檢查輸入,調用DAO接口,因此業務邏輯就是一個簡單的Facade接口:

    public class FacadeImpl implements Facade {
        private AccountDao  accountDao;
        private ArticleDao  articleDao;
        private CategoryDao categoryDao;
        private FeedbackDao feedbackDao;
        private ImageDao    imageDao;
        private LinkDao     linkDao;
        private SequenceDao sequenceDao;
    }

      對于普通的getArticle()等方法,Facade僅僅簡單地調用對應的DAO接口:

    public Article getArticle(int articleId) throws QueryException {
        return articleDao.getArticle(articleId);
    }

      對于需要身份驗證的操作,如deleteArticle()方法,Facade需要首先驗證用戶身份:

    public void deleteArticle(Identity id, int articleId) throws DeleteException {
        Article article = getArticleInfo(articleId);
        if(article.getAccountId()!=id.getAccountId())
            throw new AuthorizationException("Permission denied.");
        articleDao.deleteArticle(articleId);
    }

      要分離用戶驗證邏輯,可以使用Proxy模式,或者使用Spring的AOP,利用MethodInterceptor實現,不過,由于邏輯很簡單,完全可以直接寫在一塊,不必使用過于復雜的設計。
      至此,我們的Blog已經實現了所有的后臺業務邏輯,并且提供統一的Facade接口。前臺Web層僅僅依賴這個Facade接口,這樣,Web層和后臺耦合非常松散,即使替換整個Web層也非常容易。

    Web層設計 (目錄)
    使用MVC模式 (目錄)
      對于復雜的Web層,使用MVC模式是必不可少的。雖然Spring能輕易集成Struts,WebWorks等Web框架,但Spring本身就提供了一個非常好的Web框架,能完全實現MVC模式。
      Spring使用一個DispatcherServlet,所有的特定請求都被轉發到DispatcherServlet,然后由相應的Controller處理,Controller返回一個ModelAndView對象(因為Java語言的方法調用只能返回一個結果,而且不支持ref參數,所以將Model和View對象合在一起返回),Model是一個Java對象,通常是Map,View是視圖的邏輯名字,通常是JSP文件名,但也可以使用Velocity等作為視圖。返回的View通過viewResolver得到真正的文件名。
      首先配置Spring的MVC,在web.xml中聲明DispatcherServlet,處理所有以.c結尾的請求:

    <web-app>
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>*.c</url-pattern>
        </servlet-mapping>
    </web-app>


      Spring會在WEB-INF下查找一個名為dispatcher-servlet.xml的文件,我們需要創建這個文件:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
     "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    </beans>


     用到的所有的Java Bean組件都要在這個文件中聲明和配置,以下是配置URL映射的Bean:

    <bean id="urlMapping"
     class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/article.c">articleController</prop>
            </props>
        </property>
    </bean>

      凡是匹配/article.c的Request都會被名為articleController的Bean處理,同樣需要聲明這個articleController:

    <bean id="articleController" class="example.ViewArticleController">
    </bean>


     ViewArticleController處理請求,然后生成Model,并選擇一個View:

    public class ViewArticleController implements Controller {
        private Facade facade;
        public void setFacade(Facade facade) { this.facade = facade; }
    public ModelAndView handleRequest(HttpServletRequest request,
        HttpServletResponse response) throws Exception {
            // 獲得參數:
            int articleId = Integer.parseInt(request.getParameter("articleId"));
            // 使用facade處理請求:
            Article article = facade.getArticle(articleId);
            // 生成Model:
            Map map = new HashMap();
            map.put("article", article);
            // 返回Model和視圖名“skin/blueskysimple/article”:
            return new ModelAndView("skin/blueskysimple/article", map);
        }
    }


      最后,skin/bluesky/article視圖會將結果顯示給用戶。
      我們注意到,ViewArticleController并不自己查找或者創建Facade,而是由容器通過setFacade(Facade)方法設置的,這就是所謂的IoC(Inversion of Control)或者Dependency Injection。容器通過配置文件完成所有組件的初始化工作:

    <!-- 聲明一個Facade -->
    <bean id="facade" class="example.Facade" />
    <!-- 聲明一個Controller -->
    <bean id="articleController" class="example.ViewArticleController">
        <!-- 為articleController設置facade屬性 -->
    <property name="facade">
            <!-- 將名為facade的Bean的引用傳進去 -->
            <ref bean="facade" />
        </property>
    </bean>

      以上配置文件實現的功能大致為:

    Facade facade = new Facade();
    ViewArticleController articleController = new ViewArticleController();
    articleController.setFacade(facade);

      但是我們不必編寫以上代碼,只需在xml文件中裝配好我們的組件就可以了。所有組件由Spring管理,并且,缺省的創建模式是Singleton,確保了一個組件只有一個實例。
      此外,所有自定義異常都是RuntimeException,Spring提供的AOP使我們能非常方便地實現異常處理。在Web層定義ExceptionHandler,處理所有異常并以統一的error頁面把出錯信息顯示給用戶,因此,在代碼中只需拋出異常,完全不必在Controller中處理異常:

    <bean id="handlerExceptionResolver" class="org.crystalblog.web.ExceptionHandler" />

    使用Velocity作為View
      采用Velocity作為View的最大的優點是簡潔,能通過簡單明了的語法直接在Html中輸出Java變量。Velocity本身是一個模板引擎,通過它來渲染Model輸出Html非常簡單,比如顯示用戶名:

    <b>${account.username}</b>
      換成JSP不得不寫成:
    <b><%=account.getUsername()%></b>
    <%...%>標記在Dreamwaver中無法直接看到其含義。如果需要在標簽中嵌入JSP就更晦澀了:
    <a href="somelink?id=<%=id %>">Link</a>

      這種嵌套的標簽往往使得可視化Html編輯器難以正常顯示,而Velocity用直接嵌入的語法解決了“ <%...%>”的問題。
      在Spring中集成Velocity是非常簡單的事情,甚至比單獨使用Velocity更簡單,只需在dispatcher-servlet.xml中申明:

    <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
    </bean>
    <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <property name="resourceLoaderPath"><value>/</value></property>
        <property name="configLocation"><value>/WEB-INF/velocity.properties</value></property>
    </bean>

      雖然標準的Velocity模板以.vm命名,但我們的所有Velocity模板頁均以.html作為擴展名,不但用Dreamwaver編輯極為方便,甚至可以直接用IE觀看頁面效果。

    實現Skin (目錄)
      許多Blog系統都允許用戶選擇自己喜歡的界面風格。要實現Skin功能非常簡單,為每個Skin編寫Velocity模板,存放在web/skin/目錄下,然后在Controller中返回對應的視圖:

    String viewpath = skin.getSkin(account.getSkinId()) + "viewArticle";

      由于使用了MVC模式,我們已經為每個頁面定義好了Model,添加一個新的skin非常容易,只需要編寫幾個必須的.html文件即可,可以參考現有的skin。
      SkinManager的作用是在啟動時自動搜索/skin/目錄下的所有子目錄并管理這些skin,將用戶設定的skinId映射到對應的目錄。目前只有一個skin,您可以直接用可視化Html編輯器如Dreamwaver改造這個skin。

    附加功能 (目錄)
    實現圖片上傳 (目錄)
      用戶必須能夠上傳圖片,因此需要文件上傳的功能。比較常見的文件上傳組件有Commons FileUpload(http://jakarta.apache.org/commons/fileupload/a>)和COS FileUploadhttp://www.servlets.com/cos),Spring已經完全集成了這兩種組件,這里我們選擇Commons FileUpload。
      由于Post一個包含文件上傳的Form會以multipart/form-data請求發送給服務器,必須明確告訴DispatcherServlet如何處理MultipartRequest。首先在dispatcher-servlet.xml中聲明一個MultipartResolver:

    <bean id="multipartResolver"
           class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <!-- 設置上傳文件的最大尺寸為1MB -->
            <property name="maxUploadSize">
            <value>1048576</value>
        </property>
    </bean>

      這樣一旦某個Request是一個MultipartRequest,它就會首先被MultipartResolver處理,然后再轉發相應的Controller。
      在UploadImageController中,將HttpServletRequest轉型為MultipartHttpServletRequest,就能非常方便地得到文件名和文件內容:

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 轉型為MultipartHttpRequest:
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        // 獲得文件:
        MultipartFile file = multipartRequest.getFile("file");
        // 獲得文件名:
        String filename = file.getOriginalFilename();
        // 獲得輸入流:
        InputStream input = file.getInputStream();
        // 寫入文件...
    }

    生成縮略圖 (目錄)
      當用戶上傳了圖片后,必須生成縮略圖以便用戶能快速瀏覽。我們不需借助第三方軟件,JDK標準庫就包含了圖像處理的API。我們把一張圖片按比例縮放到120X120大小,以下是關鍵代碼:

    public static void createPreviewImage(String srcFile, String destFile) {
        try {
            File fi = new File(srcFile);
    // src
            File fo = new File(destFile);
    // dest
            BufferedImage bis = ImageIO.read(fi);

            int w = bis.getWidth();
            int h = bis.getHeight();
            double scale = (double)w/h;
            int nw = IMAGE_SIZE;
    // final int IMAGE_SIZE = 120;
            int nh = (nw * h) / w;
            if( nh>IMAGE_SIZE ) {
                nh = IMAGE_SIZE;
                nw = (nh * w) / h;
            }
            double sx = (double)nw / w;
            double sy = (double)nh / h;

            transform.setToScale(sx,sy);
            AffineTransformOp ato = new AffineTransformOp(transform, null);
            BufferedImage bid = new BufferedImage(nw, nh, BufferedImage.TYPE_3BYTE_BGR);
            ato.filter(bis,bid);
            ImageIO.write(bid, "jpeg", fo);
        } catch(Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Failed in create preview image. Error: " + e.getMessage());
        }
    }

    實現RSS (目錄)
      RSS是一個標準的XML文件,Rss閱讀器可以讀取這個XML文件獲得文章的信息,使用戶可以通過Rss閱讀器而非瀏覽器閱讀Blog,我們只要動態生成這個XML文件便可以了。RSSLibJ是一個專門讀取和生成RSS的小巧實用的Java庫,大小僅25k,可以從http://sourceforge.net/projects/rsslibj/下載rsslibj-1_0RC2.jar和它需要的EXMLjar兩個文件,然后復制到web/WEB-INF/lib/下。
      使用RSSLibJ異常簡單,我們先設置好HttpServletResponse的Header,然后通過RSSLibJ輸出XML即可:

    Channel channel = new Channel();
    channel.setDescription(account.getDescription());
    baseUrl = baseUrl.substring(0, n);
    channel.setLink("http://server-name/home.c?accountId=" + accountId);
    channel.setTitle(account.getTitle());
    List articles = facade.getArticles(accountId, account.getMaxPerPage(), 1);
    Iterator it = articles.iterator();
    while(it.hasNext()) {
        Article article = (Article)it.next();
        channel.addItem("http://server-name/article.c?articleId=" + article.getArticleId(),
            article.getSummary(), article.getTitle()
        );
    }
    // 輸出xml:
    response.setContentType("text/xml");
    PrintWriter pw = response.getWriter();
    pw.print(channel.getFeed("rss"));
    pw.close();

    實現全文搜索 (目錄)
      全文搜索能大大方便用戶快速找到他們希望的文章,為blog增加一個全文搜索功能是非常必要的。然而,全文搜索不等于SQL的LIKE語句,因為關系數據庫的設計并不是為全文搜索設計的,數據庫索引對全文搜索無效,在一個幾百萬條記錄中檢索LIKE '%A%'可能會耗時幾分鐘,這是不可接受的。幸運的是,我們能使用免費并且開源的純Java實現的Lucene全文搜索引擎,Lucene可以非常容易地集成到我們的blog中。
      Lucene不提供直接對文件,數據庫的索引,只提供一個高性能的引擎,但接口卻出人意料地簡單。我們只需要關心以下幾個簡單的接口:
      Document:代表Lucene數據庫的一條記錄,也代表搜索的一條結果。
      Field:一個Document包含一個或多個Field,類似關系數據庫的字段。
      IndexWriter:用于創建新的索引,也就是向數據庫添加新的可搜索的大段字符串。
      Analyzer:將字符串拆分成單詞(Token),不同的文本對應不同的Analyzer,如HtmlAnalyzer,PDFAnalyzer。
      Query:封裝一個查詢,用于解析用戶輸入。例如,將“bea blog”解析為“同時包含bea和blog的文章”。
      Searcher:搜索一個Query,結果將以Hits返回。
      Hits:封裝一個搜索結果,包含Document集合,能非常容易地輸出結果。
      下一步,我們需要為Article表的content字段建立全文索引。首先為Lucene新建一個數據庫,請注意這個數據庫是Lucene專用的,我們不能也不必知道它的內部結構。Lucene的每個數據庫對應一個目錄,只需要指定目錄即可:

    String indexDir = "C:/search/blog";
    IndexWriter indexWriter = new IndexWriter(indexDir, new StandardAnalyzer(), true);
    indexWriter.close();


      然后添加文章,讓Lucene對其索引:

    String title = "文章標題" // 從數據庫讀取
    String content = "文章內容" // 從數據庫讀取
    // 打開索引:
    IndexWriter indexWriter = new IndexWriter(indexDir, new StandardAnalyzer(), false);
    // 添加一個新記錄:
    Document doc = new Document();
    doc.add(Field.Keyword("title", title));
    doc.add(Field.Text("content", content));
    // 建立索引:
    indexWriter.addDocument(doc);
    // 關閉:
    indexWriter.close();

     要搜索文章非常簡單:
     然后添加文章,讓對其索引:

    String title = "文章標題" // 從數據庫讀取
    String content = "文章內容" // 從數據庫讀取
    // 打開索引:
    IndexWriter indexWriter = new IndexWriter(indexDir, new StandardAnalyzer(), false);
    // 添加一個新記錄:
    Document doc = new Document();
    doc.add(Field.Keyword("title", title));
    doc.add(Field.Text("content", content));
    // 建立索引:
    indexWriter.addDocument(doc);
    // 關閉:
    indexWriter.close();

      要搜索文章非常簡單:

    Searcher searcher = new IndexSearcher(dir);
    Query query = QueryParser.parse(keyword, "content", new StandardAnalyzer());
    Hits hits = searcher.search(query);
    if(hits != null){
        for(int i = 0;i < hits.length(); i++){
            Document doc = hits.doc(i);
            System.out.println("found in " + doc.get("title"));
            System.out.println(doc.get("content"));
        }
    }
    searcher.close();

      我們設計一個LuceneSearcher類封裝全文搜索功能,由于必須鎖定數據庫所在目錄,我們把數據庫設定在/WEB-INF/search/下,確保用戶不能訪問,并且在配置文件中初始化目錄:

    <bean id="luceneSearcher" class="org.crystalblog.search.LuceneSearcher">
        <property name="directory">
           <value>/WEB-INF/search/</value>
        </property>
    </bean>

    效果如下:


    (圖4:search)

    發送Email (目錄)
      Blog用戶可以讓系統將來訪用戶的留言發送到注冊的Email地址,為了避免使用SMTP發信服務器,我們自己手動編寫一個SendMail組件,直接通過SMTP協議將Email發送到用戶信箱。
      SendMail組件只需配置好DNS服務器的IP地址,即可向指定的Email信箱發送郵件。并且,SendMail使用緩沖隊列和多線程在后臺發送Email,不會中斷正常的Web服務。具體代碼請看SendMail.java。

    測試 (目錄)
      服務器配置為:P4 1.4G,512M DDR,100M Ethernet,Windows XP Professional SP2。
      測試服務器分別為WebLogic Server 8.1,Tomcat 4.1/5.0,Resin 2.1.1。
      測試數據庫為MS SQL Server 2000 SP3。如果你使用Oracle或者DB2,MySQL等其他數據庫并測試成功,請將SQL初始化腳本和詳細配置過程發一份給我,謝謝。
      由于時間有限,沒有作進一步的調優。WebLogic Server和iBatis有很多優化選項,詳細配置可以參考相關文檔。

    中文支持 (目錄)
      測試發現,中文不能在頁面中正常顯示,為了支持中文,首先在web.xml加入Filter,用于將輸入編碼設置為gb2312:

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.crystalblog.web.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>gb2312</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


      然后用文本工具搜索所有的.htm,.html,.properties文件,將“iso-8859-1”替換為“gb2312”,現在頁面中文已經能正常顯示,但是Lucene仍不能正常解析中文,原因是標準的StandardA?nalyzer只能解析英文,可以從網上下載一個支持中文的Analyzer。

    總結 (目錄)
      Spring的確是一個優秀的J2EE框架,通過Spring強大的集成和配置能力,我們能輕松設計出靈活的多層J2EE應用而無需復雜的EJB組件支持。由于時間倉促,水平有限,文中難免有不少錯誤,懇請讀者指正。

    源代碼下載 (目錄)
      源代碼可以從http://www.javasprite.com/crystal/download.asp下載。

    相關資源下載 (目錄)
    JDK 1.4.2可以從http://java.sun.com下載。
    Spring framework 1.1可以從http://www.springframework.org下載。
    iBatis 2.0可以從http://www.ibatis.com下載。
    Tomcat 4.1/5.0、Ant 1.6可以從http://www.apache.org下載。
    Resin 2.1.1可以從http://www.caucho.com下載。
    WebLogic Server / Workshop 8.1可以從http://commerce.bea.com下載。
    JUnit 3.8可以從http://www.junit.org下載。
    MySQL 4及其JDBC驅動可以從http://www.mysql.com下載。
    MS SQL Server 2000 JDBC驅動可以從http://www.microsoft.com/downloads/details.aspx?FamilyID=07287b11-0502-461a-b138-2aa54bfdc03a&DisplayLang=en下載。
    RSSLibJ 1.0可以從http://sourceforge.net/projects/rsslibj/下載。

    參考 (目錄)
    “Spring Reference”,Rod Johnson等。
    “iBatis SQL Maps Guide”。
    “Apache Ant 1.6.2 Manual”。
    “Lucene Getting Started”。
    Springframework的JPetStore示例是非常棒的設計,本文參考了JPetStore的許多設計模式。

    關于作者
    廖雪峰,北京郵電大學本科畢業,對J2EE/J2ME有濃厚興趣,歡迎交流:asklxf@163.com
    個人網站:http://www.javasprite.com,歡迎訪問。
    個人Blog站點:http://blog.csdn.net/asklxf/,歡迎訪問。

    Feedback

    # re: 基于J2EE的Blog平臺   回復  更多評論   

    2007-04-10 20:43 by wj
    你好~~我現在做的畢業設計與這個類似,請問能否將你的源代碼給我參考下?謝謝!!
    我的email:supercoca@gmail.com
    主站蜘蛛池模板: 中文字幕无线码免费人妻| 久久久久久曰本AV免费免费| 国产成人精品日本亚洲专区61 | 国产AV无码专区亚洲AV蜜芽| 激情综合色五月丁香六月亚洲| 免费国产午夜高清在线视频| avtt天堂网手机版亚洲| 伊人久久综在合线亚洲91| 免费在线看v网址| 国产无限免费观看黄网站| 亚洲狠狠成人综合网| 亚洲综合伊人久久综合| 毛片免费观看网站| 中国好声音第二季免费播放| 亚洲不卡影院午夜在线观看| 久久亚洲高清观看| 免费a级毛片在线观看| 久久免费看黄a级毛片| 最近免费字幕中文大全| 亚洲AV香蕉一区区二区三区| 亚洲一区二区三区首页| 亚洲女同成人AⅤ人片在线观看| www.黄色免费网站| 久久免费观看国产精品88av| 免费夜色污私人影院网站电影| 亚洲国产电影在线观看| 久久精品国产亚洲av四虎| 亚洲?V乱码久久精品蜜桃| 最近免费中文字幕大全| 最近新韩国日本免费观看 | h视频在线观看免费完整版| 国产免费MV大全视频网站 | 性做久久久久久久免费看| a级在线免费观看| 人体大胆做受免费视频| 亚洲精品无码一区二区| 亚洲av无码久久忘忧草| 91久久亚洲国产成人精品性色| 亚洲色大成网站WWW久久九九| 免费jjzz在线播放国产| 日本免费电影一区|