本篇博客是在上一篇《Lucene搜索引擎+HDFS+MR完成垂直搜索》的基礎上,在數據收集之後的JSP/Servlet方面,換為SpringMVC框架來實現。 藉助SpringMVC技術完成資料庫、HDFS、頁面的交互,以達到實現垂直搜索引擎。 本篇博客的思想:一是深入數據收集、分析、關鍵詞搜索呈現 ...
本篇博客是在上一篇《Lucene搜索引擎+HDFS+MR完成垂直搜索》的基礎上,在數據收集之後的JSP/Servlet方面,換為SpringMVC框架來實現。
藉助SpringMVC技術完成資料庫、HDFS、頁面的交互,以達到實現垂直搜索引擎。
本篇博客的思想:一是深入數據收集、分析、關鍵詞搜索呈現的流程實現;
一是藉此實踐學習SpringMVC框架的技術。
首先簡單闡述實現垂直搜索引擎的流程:1‘網路爬蟲的數據存取到HDFS和資料庫中;
2’MR對數據進行分析規約;
3‘SpringMVC實現關鍵詞搜索並呈現到網頁(此處為本篇重點闡述)
----->
目錄:
1、SpringMVC闡述
2、創建web項目(添加Spring框架支持)
3、配置applicationContext.xml★★
4、配置web.xml文件★★
5、添加log4j.properties文件至src根目錄
6、實現DAO操作類
7、修改頁面源碼
8、編寫控制器來自動接收參數,以及進行數據操作★★
9、過濾器解決亂碼問題
10、結果顯示
11、總結
------>
1、 SpringMVC闡述
SpringMVC是Spring的一個子框架,主要是用來處理MVC設計模式中的View和Control。
MVC:
Model:模型層,也就是資料庫操作層。DAO部分代碼
View:展示層,也就是頁面顯示部分。Servlet部分
Controller:控制層,也就是業務邏輯層。JSP部分
使用SpringMVC以後,這三部分代碼都會有改變,由Spring來進行調整。
企業用的最多的是SpringMVC + MyBatis。
這裡我們就只使用SpringMVC + JDBC來完成。
Spring 在這三層可以有不同的作用:
Model層中Spring可以幫助進行數據源連接池的配置,還可以簡化JDBC的操作代碼,同時還能幫助完成自動的打開和關閉資料庫連接。
View層中,Spring可以幫助我們簡化表單提交的參數代碼,也可以自動接收Control中返回的數據信息。
Control層中,Spring可以幫助我們自動接收頁面提交的參數。
Spring的核心在於配置文件,所有的類的信息基本都要加入到配置文件中或使用Annotation來進行標註。
2、創建web項目(添加Spring框架支持)
【項目整體呈現】
創建一個新的web項目,導入上一篇項目所需的jar包,以及拷貝vo類(DAO介面類)和utils類(關鍵詞查詢類)所在的包。
先為項目加入Spring的框架支持。
在項目上點右鍵,選擇MyEclipse,然後找到install Spring … 的選項。
按照固定的步驟加入支持,最後一步時,一定要註意選擇好需要的支持庫,這裡必須用到的是Persistence和Web
3、配置applicationContext.xml
這個文件Spring的配置文件,主要是將各種POJO,JAVA,action配置到XML轉交給beanfactory管理,降低耦合度。
主要的配置組件:
<bean id="射影class的名字" class="寫的JAVA類"/>
然後就是這些<bean>之間的依賴關係,比如:
<bean id="mySerive" class="org.haha.MyServiceImpl"/>
<bean id="loginAction" class="org.haha.LoginAction" scope="prototype">
<!--依賴註入業務邏輯組件-->
<property name="ms" ref="myService" />
</bean>
以上代碼的意思會在loginAction的代碼里引用MyServiceImpl類,但是只需要用ms代替就可以
例如:
public String execute() throws Exception{
ms.sayhello();
}
正常情況應該 new MyServiceImpl,但是通過XML配置之後就直接用以上代碼就可以實現
new 的效果。
以下是該項目的配置文件呈現:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 7 xmlns:context="http://www.springframework.org/schema/context" 8 xmlns:mvc="http://www.springframework.org/schema/mvc" 9 10 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 11 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd 12 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"> 13 14 <!-- 加入SpringMVC的庫支持 --> 15 <mvc:annotation-driven></mvc:annotation-driven> 16 <!-- 加入Context支持 --> 17 <context:annotation-config></context:annotation-config> 18 <!-- 配置使用Annotation的包範圍 --> 19 <context:component-scan base-package="org.liky.sina.dao.impl,org.liky.sina.action"></context:component-scan> 20 21 <!-- 配置資料庫連接池 --> 22 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> 23 <property name="driverClassName" value="org.gjt.mm.mysql.Driver"></property> 24 <property name="url" value="jdbc:mysql://localhost:3306/sina_news"></property> 25 <property name="username" value="root"></property> 26 <property name="password" value="admin"></property> 27 </bean> 28 29 <!-- 配置一個模板類 --> 30 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 31 <property name="dataSource"> 32 <ref bean="dataSource"/> 33 </property> 34 </bean> 35 36 <!-- 配置自動管理連接的一個事務操作支持 --> 37 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 38 <property name="dataSource"> 39 <ref bean="dataSource"/> 40 </property> 41 </bean> 42 43 44 <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> 45 <property name="transactionManager"> 46 <ref bean="transactionManager"/> 47 </property> 48 49 <!-- 配置事務處理方式 *表示service中的所有方法都要進行事務處理。PROPAGATION_REQUIRED表示事務處理方式,有以下三種選擇: 50 PROPAGATION_REQUIRED:正常事務處理 51 PROPAGATION_REQUIRED_NEWS:每個操作單獨進行一個事務處理 52 PROPAGATION_REQUIRED_NEVER:不使用事務,出錯後後面的不進行添加 53 --> 54 55 <property name="transactionAttributes"> 56 <props> 57 <prop key="*">PROPAGATION_REQUIRED</prop> 58 </props> 59 </property> 60 61 </bean> 62 63 <!-- 配置事務管理器的作用範圍 --> 64 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 65 <property name="interceptorNames"> 66 <list> 67 <value>transactionInterceptor</value> 68 </list> 69 </property> 70 <property name="beanNames"> 71 <list> 72 <value>*DAOImpl</value> 73 </list> 74 </property> 75 </bean> 76 77 78 79 </beans>
4、配置web.xml文件
這個文件是需要在裡面讓伺服器啟動時,可以自動載入spring的配置文件。
代碼呈現:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 5 id="WebApp_ID" version="3.0"> 6 <display-name>SinaNewsSpringMVC</display-name> 7 <welcome-file-list> 8 <welcome-file>index.html</welcome-file> 9 </welcome-file-list> 10 <listener> 11 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 12 </listener> 13 <context-param> 14 <param-name>contextConfigLocation</param-name> 15 <param-value>classpath:applicationContext.xml</param-value> 16 </context-param> 17 18 <!-- 手工配置載入SpringMVC的支持庫和跳轉路徑 --> 19 <servlet> 20 <servlet-name>springmvc</servlet-name> 21 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 22 23 <init-param> 24 <param-name>contextConfigLocation</param-name> 25 <param-value>/WEB-INF/classes/applicationContext.xml</param-value> 26 </init-param> 27 <load-on-startup>1</load-on-startup> 28 </servlet> 29 <servlet-mapping> 30 <servlet-name>springmvc</servlet-name> 31 <url-pattern>*.do</url-pattern> 32 </servlet-mapping> 33 34 <!-- 添加過濾器 --> 35 <filter> 36 <filter-name>encoding</filter-name> 37 <filter-class>org.liky.sina.filter.EncodingFilter</filter-class> 38 </filter> 39 <filter-mapping> 40 <filter-name>encoding</filter-name> 41 <url-pattern>/*</url-pattern> 42 </filter-mapping> 43 </web-app>
5、添加log4j.properties文件至src根目錄
Log4j,Apache的一個開源項目,作用:
可以控制日誌信息輸送的目的地是控制台、文件、GUI組件、甚至是套介面服務器、NT的事件記錄器、UNIXSyslog守護進程等;
可以控制每一條日誌的輸出格式;
通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日志的生成過程。
此外,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。
1 ### direct log messages to stdout ### 2 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 3 log4j.appender.stdout.Target=System.out 4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 5 log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 6 7 ### direct messages to file hibernate.log ### 8 #log4j.appender.file=org.apache.log4j.FileAppender 9 #log4j.appender.file.File=hibernate.log 10 #log4j.appender.file.layout=org.apache.log4j.PatternLayout 11 #log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n 12 13 ### set log levels - for more verbose logging change 'info' to 'debug' ### 14 15 log4j.rootLogger=warn, stdout 16 17 log4j.logger.org.hibernate=info 18 #log4j.logger.org.hibernate=debug 19 20 ### log HQL query parser activity 21 #log4j.logger.org.hibernate.hql.ast.AST=debug 22 23 ### log just the SQL 24 #log4j.logger.org.hibernate.SQL=debug 25 26 ### log JDBC bind parameters ### 27 log4j.logger.org.hibernate.type=info 28 #log4j.logger.org.hibernate.type=debug 29 30 ### log schema export/update ### 31 log4j.logger.org.hibernate.tool.hbm2ddl=debug 32 33 ### log HQL parse trees 34 #log4j.logger.org.hibernate.hql=debug 35 36 ### log cache activity ### 37 #log4j.logger.org.hibernate.cache=debug 38 39 ### log transaction activity 40 #log4j.logger.org.hibernate.transaction=debug 41 42 ### log JDBC resource acquisition 43 #log4j.logger.org.hibernate.jdbc=debug 44 45 ### enable the following line if you want to track down connection ### 46 ### leakages when using DriverManagerConnectionProvider ### 47 #log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace
6、實現DAO操作類
之後啟動伺服器進行測試,如果啟動後沒有報錯,那就表示環境配置成功,可以正常使用。
下麵就開始編寫DAO操作。
對於DAO介面是沒有變化的,所以可以直接拷貝過來。
這裡不需要再編寫工廠類,因為Spring可以自動幫助創建對象。
實現類需要交給Spring來進行管理和控制,並且簡化jdbc代碼操作
代碼如下:
1 package org.liky.sina.dao.impl; 2 3 import java.sql.ResultSet; 4 import java.sql.SQLException; 5 import java.util.List; 6 7 import org.liky.sina.dao.INewsDAO; 8 import org.liky.sina.vo.News; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.jdbc.core.JdbcTemplate; 11 import org.springframework.jdbc.core.RowMapper; 12 import org.springframework.jdbc.core.support.JdbcDaoSupport; 13 import org.springframework.stereotype.Component; 14 15 @Component 16 public class NewsDAOImpl extends JdbcDaoSupport implements INewsDAO,RowMapper<News> { 17 18 @Autowired 19 public NewsDAOImpl(JdbcTemplate jdbcTemplate) { 20 super.setJdbcTemplate(jdbcTemplate); 21 } 22 23 24 public void doCreate(News news) throws Exception { 25 String sql="insert into news (id,title,description,url) values (?,?,?,?)"; 26 27 super.getJdbcTemplate().update(sql, news.getId(),news.getTitle(),news.getDescription(),news.getUrl()); 28 } 29 30 public News findById(int id) throws Exception { 31 String sql="select id,title,description,url from news where id=?"; 32 News news=super.getJdbcTemplate().queryForObject(sql,new Object[]{id},this); 33 34 return news; 35 } 36 37 public List<News> findByIds(Integer[] ids, int start, int pageSize) 38 throws Exception { 39 StringBuilder sql = new StringBuilder( 40 "SELECT id,title,description,url FROM new_news WHERE id IN ("); 41 if (ids != null && ids.length > 0) { 42 for (int id : ids) { 43 sql.append(id); 44 sql.append(","); 45 } 46 // 第一個 ? 表示開始的記錄數,第二個 ? 表示每頁顯示的記錄數。 47 String resultSQL = sql.substring(0, sql.length() - 1) 48 + ") LIMIT ?,?"; 49 List<News> allNews = super.getJdbcTemplate().query(resultSQL, 50 new Object[] { start, pageSize }, this); 51 return allNews; 52 } 53 return null; 54 } 55 56 57 public int getAllCount(Integer[] ids)throws Exception{ 58 StringBuilder sql=new StringBuilder("select count(id) from new_news where id in ("); 59 60 if(ids!=null&&ids.length>0){ 61 for(int id:ids){ 62 sql.append(id); 63 sql.append(","); 64 } 65 // 第一個 ? 表示開始的記錄數,第二個 ? 表示每頁顯示的記錄數。 66 String resultSQL = sql.substring(0, sql.length() - 1) + ")"; 67 int count = super.getJdbcTemplate().queryForInt(resultSQL); 68 return count; 69 } 70 return 0; 71 } 72 73 public News mapRow(ResultSet rs, int arg1) throws SQLException { 74 News news=new News(); 75 news.setId(rs.getInt(1)); 76 news.setTitle(rs.getString(2)); 77 news.setDescription(rs.getString(3)); 78 news.setUrl(rs.getString(4)); 79 return news; 80 81 } 82 83 }
7、修改頁面源碼
表單部分進行一些簡單的調整,加入了form標簽相關的配置。主要在action的值改為了search.do
【1】index.jsp修改
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <% 3 String path = request.getContextPath(); 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 5 %> 6 7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 8 <html> 9 <head> 10 <base href="<%=basePath%>"> 11 <title>新浪新聞熱詞搜索</title> 12 </head> 13 14 <body> 15 <center> 16 <form action="search.do" method="post"> 17 請輸入查詢關鍵字: 18 <input type="text" name="keyword"> 19 <input type="submit" value="查詢"> 20 </form> 21 </center> 22 </body> 23 </html>
【2】result.jsp修改
1 <%@page import="org.liky.sina.vo.News"%> 2 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 3 <% 4 String path = request.getContextPath(); 5 String basePath = request.getScheme() + "://" 6 + request.getServerName() + ":" + request.getServerPort() 7 + path + "/"; 8 %> 9 10 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 11 <html> 12 <head> 13 <base href="<%=basePath%>"> 14 15 <title>新浪新聞搜索</title> 16 </head> 17 18 <body> 19 <center> 20 <% 21 List<News> allNews = (List<News>)request.getAttribute("allNews"); 22 %> 23 <table width="80%"> 24 <% 25 for (News n : allNews) { 26 27 %> 28 <tr> 29 <td> 30 <a href="<%=n.getUrl() %>" target="_blank"><%=n.getTitle() %></a> <br> 31 <%=n.getDescription() %> 32 <hr/> 33 </td> 34 </tr> 35 <% 36 } 37 38 %> 39 </table> 40 41 <% 42 int cp = (Integer)request.getAttribute("currentPage"); 43 int allPages = (Integer)request.getAttribute("allPages"); 44 %> 45 <form id="split_page_form" action="search.do" method="post"> 46 <input type="hidden" name="currentPage" id="cp" value="<%=cp %>" /> 47 <input type="button" <%=cp == 1?"disabled":"" %> value="首頁" onclick="changeCp(1);"> 48 <input type="button" <%=cp == 1?"disabled":"" %> value="上一頁" onclick="changeCp(<%=cp - 1 %>);"> 49 <input type="button" <%=cp == allPages?"disabled":"" %> value="下一頁" onclick="changeCp(<%=cp + 1 %>);"> 50 <input type="button" <%=cp == allPages?"disabled":"" %> value="尾頁" onclick="changeCp(<%=allPages %>);"> 51 第 <%=cp %> 頁 / 共 <%=allPages %> 頁 52 <br> 53 請輸入查詢關鍵字:<input type="text" name="keyword" value="<%=request.getParameter("keyword")%>"> 54 <input type="submit" value="查詢"> 55 </form> 56 <script type="text/javascript"> 57 function changeCp(newcp) { 58 // 改變當前頁數 59 document.getElementById("cp").value = newcp; 60 // 提交表單 61 document.getElementById("split_page_form").submit(); 62 } 63 </script> 64 65 </center> 66 </body> 67 </html>
8、編寫控制器來自動接收參數,以及進行數據操作
之後需要編寫SpringMVC的控制器來自動接收參數,併進行數據操作。
代碼呈現:
1 package org.liky.sina.action; 2 3 import java.util.List; 4 5 import javax.annotation.Resource; 6 7 import org.liky.sina.dao.INewsDAO; 8 import org.liky.sina.utils.HDFSUtils; 9 import org.liky.sina.vo.News; 10 import org.springframework.stereotype.Controller; 11 import org.springframework.web.bind.annotation.RequestMapping; 12 import org.springframework.web.bind.annotation.RequestParam; 13 import org.springframework.web.servlet.ModelAndView; 14 15 16 @Controller 17 public class NewsAction { 18 private INewsDAO newsdao; 19 20 @RequestMapping(value="/search.do") 21 public ModelAndView search(@RequestParam String keyword,@RequestParam(defaultValue="1") int currentPage){ 22 ModelAndView mv= new ModelAndView(); 23 //一頁顯示10條數據 24 int pageSize=10; 25 26 try{ 27 Integer[] ids=HDFSUtils.getIdsByKeyword(keyword); 28 //根據這些id查詢相應的結果 29 List<News> allNews=newsdao.findByIds(ids, (currentPage-1), pageSize); 30 31 int count=newsdao.getAllCount(ids); 32 //計算一個關鍵詞讀取的數據顯示總頁數 33 int allPages=count/pageSize; 34 if(count%pageSize!=0){ 35 allPages++; 36 } 37 38 //結果傳遞迴頁面顯示 39 mv.addObject("allNews",allNews); 40 mv.addObject("allPages", allPages); 41 mv.addObject("currentPage", currentPage); 42 43 //切換到頁面上 44 mv.setViewName("/result.jsp"); 45 }catch(Exception e){ 46 e.printStackTrace(); 47 } 48 49 return mv; 50 } 51 52 @Resource(name="newsDAOImpl") 53 public void setNewsdao(INewsDAO newsdao){ 54 this.newsdao=newsdao; 55 56 } 57 }
9、過濾器解決亂碼問題
測試時會有亂碼出現,因此還要編寫一個過濾器來處理亂碼。
如果是MyEclipse的亂碼問題,只需修改:
【1】過濾器代碼呈現:
1 package org.liky.sina.filter; 2 /** 3 * 過濾器,解決網頁亂碼的問題 4 */ 5 import java.io.IOException; 6 7 import javax.servlet.Filter; 8 import javax.servlet.FilterChain; 9 import javax.servlet.FilterConfig; 10 import javax.servlet.ServletException; 11 import javax.servlet.ServletRequest; 12 import javax.servlet.ServletResponse; 13 14 public class EncodingFilter implements Filter { 15 16 @Override 17 public void destroy() { 18 } 19 20 @Override 21 public void doFilter(ServletRequest arg0, ServletResponse arg1, 22 FilterChain arg2) throws IOException, ServletException { 23 //修改編碼格式為UTF-8 24 arg0.setCharacterEncoding("UTF-8"); 25 arg2.doFilter(arg0, arg1); 26 } 27 28 @Override 29 public void init(FilterConfig arg0) throws ServletException { 30 // TODO Auto-generated method stub 31 32 } 33 34 }
【2】在web.xml中配置這個過濾器:
1 <filter> 2 <filter-name>encoding</filter-name> 3 <filter-class>org.liky.sina.filter.EncodingFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>encoding</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping>
10、結果顯示
關於其他的代碼在參考《Lucene搜索引擎+HDFS+MR完成垂直搜索》。
運行結果如下:
控制台顯示:
瀏覽器index.jsp顯示:
搜索結果 result.jsp 顯示,尾部實現了分頁功能,並且還可以進行搜索:
11、總結
首先上一篇結尾存在的亂碼問題在此處第九部分,給出了過濾器的方法,將編碼格式改為UTF-8,已經解決了。
關於垂直搜索引擎,是針對某一個行業的專業搜索引擎(本次項目是對新浪新聞的數據收集,爬取深度為5),是搜索引擎的細分和延伸,是對網頁庫中的某類專門的信息進行一次整合,定向分欄位抽取出需要的數據進行處理後再以某種形式返回給用戶。
本篇用SpringMVC框架的技術完成數據的垂直搜索,相比來說,結構更加清晰,難點在於兩個配置文件的修改,以及在代碼中使用配置文件。
思路流程:
1、 DAO操作類根據首頁搜索框關鍵詞的輸入,獲取HDFS的id,以便從資料庫提取一組信息;
2、 配置文件實現了資料庫的連接,並簡化了DAO的流程實現;
3、 SpringMVC的控制器自動接收參數,併進行數據操作;
4、 JSP文件接收控制器傳遞的數據,呈現在結果頁面。