1. 程式人生 > >JavaWeb開發知識總結(網上商城專案總結)

JavaWeb開發知識總結(網上商城專案總結)

JavaWeb開發知識總結(網上商城專案小結)

1. 資料庫設計

表的關係的設計如下:

資料庫設計

2. 使用技術總結

網上商城前臺業務流程

2.1 BaseServlet的設計

實現處理請求的方式1:

* 針對每一個請求均建立一個Servlet的實現類進行處理,弊端是:當業務較為複雜和請求較多時,會使得Servlet類過多。
// 請求方式:
// http://localhost:8080/website/UserLoginServlet -- 使用者登陸的Servlet
public class UserLoginServlet extends HttpServlet {
  protected void
doGet(HttpServletRequest req, HttpServletResponse resp){ ... } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { ... } } // http://localhost:8080/website/UserLogoutServlet -- 使用者退出的Servlet public class UserLogoutServlet extends HttpServlet { protected
void doGet(HttpServletRequest req, HttpServletResponse resp){ ... } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { ... } }

實現處理請求的方式2:

* 每一個模組建立一個Servlet類,然後通過在請求的引數內傳遞要執行處理的方法名的方式進行整合,弊端:需要在Servlet中的get或post方法中使用if做多層判斷,使得邏輯較為混亂。
// 請求方式:
// http://localhost:8080/website/UserServlet?method=login -- 訪問使用者登陸 public class UserServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp){ // 獲取要執行的方法的名稱,通過方法名稱執行對應的方法 String methodname = request.getParameter("method"); if("login".equals(methodname)) { login(req,resp); } else if () { } .... } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { this.doGet(req,resp); } public void login() { ... } }

實現處理請求的方式3:較為常用

* 編寫一個統一的繼承HttpServlet類的Servlet類,重寫service方法,在service方法中獲取請求中method引數,根據該引數通過反射執行對應的方法。並且所有的請求都會經過Servlet,方便控制,不直接訪問jsp頁面
/**
 * 所有的Servlet的基類,該類不在web.xml中配置
 * 所有的Servlet均繼承至該類
 * 該類封裝方法的執行及資源的轉發(封裝的方式根據需要可以自定義)
 */
public class BaseServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;
  @Override
  // 重寫service方法,封裝所有的Servlet的請求處理方法
  protected void service(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException {
      // 接收引數,獲取方法的名稱
      String methodName = req.getParameter("method");
      // 傳遞的執行的方法為空或者為空字串時,直接返回
      if(methodName == null || "".equals(methodName)) {
          resp.setContentType("text/html;charset=UTF-8");
          resp.getWriter().println("執行的method方法為null");
          return;
      }
      // 獲取當前物件的class物件,通過反射的方式執行對於該請求的對應的方法
      Class<? extends BaseServlet> clas = this.getClass();
      try {
          // 獲取傳遞過來的方法名的方法
          Method method = clas.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
          // 執行引數中的方法,返回值表示要轉發的資源路徑
          String path = (String) method.invoke(this, req, resp);
          if(path != null) {
              // 轉發資源
              req.getRequestDispatcher(path).forward(req, resp);
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
}
// 使用者的Servlet繼承至BaseServlet
// 訪問http://localhost:8080/website/UserServlet?method=userRegistUI   -- 訪問使用者註冊頁面
public class UserServlet extends BaseServlet {
  private static final long serialVersionUID = 1L;
      // 跳轉使用者註冊的頁面
  public String userRegistUI(HttpServletRequest request, HttpServletResponse response){
      return "/jsp/regist.jsp";
    }
}

2.2 IOC方式實現介面和實現類的解耦合

Java程式設計提倡面向介面程式設計,並且要遵循OCP(open-close-principle)原則(對程式擴充套件開放,對程式原始碼修改關閉)。


# 1. 面向介面程式設計:

  在設計中,提供dao層和service的介面,並提供實現類,在需要使用dao層處理資料時,直接建立dao層實現類的物件進行處理,這種方式使得介面和實現類出現了高度的耦合,當dao層實現類變化時,需要修改原始碼建立實現類物件,違背了OCP原則.

# 2.使用工廠設計模式:

  工廠模式是在工廠類中提供建立dao層和service介面的實現類物件的方法,在需要使用dao層或service介面的實現類時,工廠類提供建立介面實現類的物件,通過工廠類進行獲取,但當介面的實現類發生變化時,需要在工廠類中進行改變,這種方式實際上是將介面和實現類的耦合轉變為了介面和工廠類的耦合,並且也需要更改原始碼。

# 3.IOC模式實現程式間接耦合:IOC-控制反轉 --> 工廠模式+反射+配置檔案

  在工廠方法中通過讀取配置檔案中配置介面的實現類的完全限定名,通過反射建立介面的實現類物件,當需求改變時,只需改變配置檔案中介面對應的實現類即可。
  XML配置檔案:配置介面和實現類的對應關係;
  dom4j:實現讀取配置檔案(使用XPath技術輔助讀取)
  反射:使用反射獲取讀取介面對應的實現類的物件
/**
 * dao層和業務層介面的工廠類
 */
public class BeanFactory {
  // 獲取業務層和dao層的介面的實現類
  public static Object getBean(String beanid) {
      try {
          // 讀取配置檔案,從xml配置檔案中獲取介面的實現類
          // 建立讀取xml檔案的物件
          SAXReader reader = new SAXReader();
          // 獲取檔案的Document物件
          Document document = reader.read(BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml"));
          // 獲取指定id名稱的子元素,通過XPath獲取元素
          Element element = (Element) document.selectSingleNode("//bean[@id='"+beanid+"']");
          // 獲取子元素中class屬性的值
          String classname = element.attributeValue("class");
          // 通過反射獲取class物件
          Class<?> class1 = Class.forName(classname);
          // 建立例項
          return class1.newInstance();
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
  }
}
<!--applicationContext.xml   介面和實現類的配置檔案,放置在專案的src目錄下-->
<?xml version="1.0" encoding="UTF-8"?>
<beans>
  <!-- dao層的實現類配置 -->
  <!-- UserDao的配置實現類 -->
  <bean id="UserDao" class="com.itheima.store.dao.impl.UserDaoImpl"/>
  <!-- CategoryDao的配置實現類 -->
  <bean id="CategoryDao" class="com.itheima.store.dao.impl.CategoryDaoImpl"/>
  <!-- ProductDao的配置實現類 -->
  <bean id="ProductDao" class="com.itheima.store.dao.impl.ProductDaoImpl"/>
  <!-- OrderDao的配置實現類 -->
  <bean id="OrderDao" class="com.itheima.store.dao.impl.OrderDaoImpl"/>

  <!-- 業務層層的實現類配置 -->
  <!-- UserService的配置實現類 -->
  <bean id="UserService" class="com.itheima.store.service.impl.UserServiceImpl"/>
  <!-- CategoryService的配置實現類 -->
  <bean id="CategoryService" class="com.itheima.store.service.impl.CategoryServiceImpl"/>
  <!-- ProductService的配置實現類 -->
  <bean id="ProductService" class="com.itheima.store.service.impl.ProductServiceImpl"/>
  <!-- OrderService的配置實現類 -->
  <bean id="OrderService" class="com.itheima.store.service.impl.OrderServiceImpl"/>
</beans>

2.3 分頁資料工具類的封裝

分頁資料的顯示:分頁顯示有5個必要元素:當前頁,每頁顯示條數,總記錄數,總頁數,要顯示的資料.

將這5個必須元素封裝到javabean類中,並且其中的總記錄數和顯示的資料需要通過查詢獲取,其他的可以直接計算,則需要定義兩個方法獲取總記錄數和顯示的資料的集合。

// 分頁的javabean類封裝,封裝時資料集合採用泛型形式,可以進行重用
public class PageBean<T> {
  private Integer currPage; // 當前頁
  private Integer totalPage; // 總頁數
  private Integer totalCount; // 記錄的總條數
  private Integer pageSize; // 每頁顯示的條數
  private List<T> list; // 查詢的資料的集合
  ... get和set方法
}
/**
 * 生成分頁資料的工具類
 */
public class PageUtils {
  // 定義每頁顯示的資料條數,預設值
  public static Integer pageSize = 12;
  /**
   * 分頁資料封裝
   * @param: classname 要執行Dao層實現類
   * @param: countmethod 計算總條數的方法名
   * @param: pagemethod 計算總頁數的方法名
   * @param: param 查詢資料的條件
   * @param: currPage 當前頁
   * @return: PageBean<T>   返回PageBean物件
   */
  public static <T> PageBean<T> getPagebean(Object obj, String countmethod,String pagemethod, String param, Integer currPage, Integer spageSize) {
      // 每頁顯示記錄預設是12條,當傳入的引數為不為null是就顯示定製顯示的條數
      if(spageSize != null) {
          pageSize = spageSize;
      }
      PageBean<T> pageBean = null;
      try {
              // 獲取class物件
          Class<?> c = obj.getClass();
              // 定義分頁封裝物件
          pageBean = new PageBean<T>();
          // 1.設定當前頁
          pageBean.setCurrPage(currPage);
          // 2.設定查詢的資料的總條數
          // 呼叫dao層查詢資料的總條數
          Method method_count = c.getMethod(countmethod, String.class);
          Integer totalCount = (Integer) method_count.invoke(obj, param);
          pageBean.setTotalCount(totalCount);
          // 3.設定每頁顯示的條數
          pageBean.setPageSize(pageSize);
          // 4.設定總頁數
          Double ceil = Math.ceil((totalCount*1.0 / pageSize));
          int totalPage = ceil.intValue();
          pageBean.setTotalPage(totalPage);
          // 5.查詢資料
          // 呼叫業務層查詢資料
          Method method_page = c.getMethod(pagemethod, String.class, Integer.class, Integer.class);
          int begin = (currPage - 1) * pageSize;
          List<T> list  = (List<T>) method_page.invoke(obj, param, begin, pageSize);
              //  將資料封裝到PageBean物件中
          pageBean.setList(list);
      } catch (Exception e) {
          e.printStackTrace();
      }
      // 將每頁顯示的記錄條資料設為初始值
      pageSize = 12;
      return pageBean;
  }
}

2.4 全域性字符集編碼過濾器

為解決通過request獲取引數時中文亂碼問題,可以設定統一字符集過濾器:

/**
 * 通過動態代理的方式,建立request物件的動態代理物件
 */
public class CharacterEncodingFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
          FilterChain chain) throws IOException, ServletException {
          // 將ServletRequest的物件轉換為HttpServletRequest物件
      final HttpServletRequest hsrequest = (HttpServletRequest) request;
          // 通過動態代理的方式,在原始的request的獲取引數之前設定編碼格式,防止中文亂碼
      HttpServletRequest myrequest = (HttpServletRequest) Proxy.newProxyInstance(hsrequest.getClass().getClassLoader(), hsrequest.getClass().getInterfaces(), new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args)
                  throws Throwable {
              // 獲取請求的方式
              String reqmethod = hsrequest.getMethod();
              // get方式只增強getParameter方法
              // 獲取方法的名稱
              String methodName = method.getName();
              if("get".equalsIgnoreCase(reqmethod)) {
                  // 當方法名為getParameter改變編碼
                  if("getParameter".equals(methodName)) {
                      // 獲取引數的值,並進行轉換編碼
                      String value = (String) method.invoke(hsrequest, args);
                      return new String(value.getBytes("ISO-8859-1"), "UTF-8");
                  }
              // post方式只增強getParameter和getParameterMap方法
              } else if("post".equalsIgnoreCase(reqmethod)) {
                  if("getParameter".equals(methodName) || "getParameterMap".equals(methodName)) {
                      // 設定request域中的編碼
                      hsrequest.setCharacterEncoding("UTF-8");
                  }
              }
              return method.invoke(hsrequest, args);
          }
      });
      chain.doFilter(myrequest, response);
  }
  @Override
  public void destroy() {
  }
}
// 在專案的web.xml檔案中配置過濾器
<!-- 統一字符集編碼過濾器 -->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>com.store.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern> // 過濾所有的請求
</filter-mapping>

2.5 使用者模組:

2.5.1 使用者註冊

非同步校驗註冊的使用者名稱是否可用:

通過Ajax的技術,在使用者輸入註冊的使用者名稱時進行向後臺校驗輸入的使用者名稱是否可用。

/**
 * 使用者模組的Servlet
 */
public class UserServlet extends BaseServlet {
  private static final long serialVersionUID = 1L;
  /**
   * 使用者名稱的非同步校驗是否可用
   */
  public String checkUsername(HttpServletRequest request, HttpServletResponse response){
      try {
          // 接收引數
          String username = request.getParameter("username");
          // 呼叫業務層進行查詢
          // UserService userService = new UserServiceImpl();
          UserService userService = (UserService) BeanFactory.getBean("UserService");
          // 查詢使用者
          User existUser = userService.findByUsername(username);
          response.setContentType("text/html;charset=UTF-8");
          if(existUser == null) {
              // 使用者名稱可以使用,返回ok
              // json表示式需要加雙引號,否則在jsp頁面中無法識別為json資料
              response.getWriter().println("{\"msg\":\"ok\"}");
          } else {
              // 使用者名稱已被佔用,返回no
              response.getWriter().println("{\"msg\":\"no\"}");
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null; // Ajax的請求不進行轉發
  }
$.post(path+"/UserServlet",{method:"checkUsername",username:$("#username").val()},
       function(data){
      var state = data.msg;
      // msg為ok代表使用者名稱可用
      if(state == "ok") {
          // 提示資訊
          layer.tips('使用者名稱可用', '#username', {
                tips: [2, '#038E09'] //還可配置顏色 綠色:#00FF00  紅色:FF0000
          });
          $("#subbtn").attr("disabled", false);
      // msg為no代表使用者名稱不可用
      } else {
          layer.tips('使用者名稱已被佔用', '#username', {
                tips: [2, '#FF0000'] //還可配置顏色 綠色:#00FF00  紅色:FF0000
          });
          $("#subbtn").attr("disabled", true);
      }
  },"json");

使用者註冊:使用BeanUtils封裝資料時,封裝時間型別的資料,需要自定義轉換類,然後在Beanutils封裝資料時進行註冊。

// 註冊的方法
public String registUser(HttpServletRequest request, HttpServletResponse response){
  try {

      // 獲取令牌口口令的字串,防止表單的重複提交
      // 獲取session域中的口令
      String stoken = (String) request.getSession().getAttribute("token");
      // 當session中的口令資料為null,代表表單是重複提交,直接返回到msg.jsp頁面進行提示
      if(stoken == null) {
          request.setAttribute("msg", "親,您註冊請求已提交,請您不要重複提交!");
          return "/jsp/msg.jsp";
      }
      // 當session中的口令資料不為空時,表示該表單是第一次提交
      // 獲取表單中隱藏欄位的口令資料
      String ftoken = request.getParameter("token");
      // 當session域中的口令資料和表單中的口令資料不一致時,表示表單中的口令被篡改,直接返回提示
      if(!stoken.equals(ftoken)) {
          request.setAttribute("msg", "親,您的註冊資料被篡改,請您重新註冊!");
          return "/jsp/msg.jsp";
      }

      // 當session中的口令資料和表單中的口令資料一致時進行使用者的註冊
      // 接收引數,封裝資料
      Map<String, String[]> parameterMap = request.getParameterMap();
      User user = new User();

      // 定義字串轉換為date型別
      ConvertUtils.register(new DateConver(), Date.class);
      // 呼叫beanutils進行封裝資料
      BeanUtils.populate(user, parameterMap);
      // 呼叫業務層進行註冊使用者
      // UserService userService = new UserServiceImpl();
      UserService userService = (UserService) BeanFactory.getBean("UserService");
      int status = userService.registUser(user);
      // 當status不為0時表示註冊成功
      if(status != 0) {
          request.setAttribute("msg", "歡迎您的註冊,請前往您的 "+user.getEmail()+" 郵箱進行啟用!");
      } else {
          request.setAttribute("msg", "對不起,註冊失敗,請重新註冊!");
      }

      // 使用者註冊完畢後需要將session域中的令牌口令資料清除
      request.getSession().removeAttribute("token");

  } catch (Exception e) {
      e.printStackTrace();
  }
  return "/jsp/msg.jsp";
}
/**
 * 日期和字串轉換的工具類
 */
public class DateConver implements Converter {
  @Override
  /**
   * 將value 轉換 c 對應型別
   * 存在Class引數目的編寫通用轉換器,如果轉換目標型別是確定的,可以不使用c 引數
   */
  public Object convert(Class c, Object value) {
      // 將String轉換為Date --- 需要使用日期格式化
      DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
      try {
          Date date = dateFormat.parse((String)value);
          return date;
      } catch (ParseException e) {
          e.printStackTrace();
      }
      return null;
  }
}

2.5.2 郵箱連結啟用使用者

郵箱相關內容:

郵件傳送的相關的概念:
* 郵箱伺服器   :如果一臺電腦安裝了郵箱伺服器的軟體,這臺電腦稱為是郵箱伺服器.
* 電子郵箱:其實就是郵箱伺服器上的一塊空間,通過電子郵箱賬號訪問這塊空間的資料.
* 收發郵件的協議:
    * 發郵件:SMTP協議:SMTPSimple Mail Transfer Protocol)即簡單郵件傳輸協議,它是一組用於由源地址到目的地址傳送郵件的規則,由它來控制信件的中轉方式。 25預設埠號
    * 收郵件:POP3協議:POP3,全名為“Post Office Protocol - Version 3”,即"郵局協議版本3"。是TCP/IP協議族中的一員。預設埠是110
    * 收郵件:IMAP協議:IMAPInternet Mail Access ProtocolInternet郵件訪問協議)以前稱作互動郵件訪問協議(Interactive Mail Access Protocol)。IMAP是斯坦福大學在1986年開發的一種郵件獲取協議。
* 收發郵件的過程:收郵件都要經過有郵件的POP3伺服器,傳送郵件都要經過SMTP伺服器,兩個郵箱間不是點對點通訊.

java中傳送郵件:需要jar包:mail.jar

/**
 * 郵箱工具類
 */
public class MailUtils {
  public static void sendMail(String to, String code) {
      try {
          // 獲得連線:
          Properties props = new Properties();
          Session session = Session.getInstance(props, new Authenticator() {
              @Override
              protected PasswordAuthentication getPasswordAuthentication() {
                  return new PasswordAuthentication("[email protected]",
                          "somnus");
              }
          });
          // 構建郵件:
          Message message = new MimeMessage(session);
          message.setFrom(new InternetAddress("[email protected]"));
          // 設定收件人:
          // TO:收件人 CC:抄送 BCC:暗送,密送.
          message.addRecipient(RecipientType.TO, new InternetAddress(to));
          // 主題:
          message.setSubject("來自商城的啟用郵件!");
          // 正文:
          message.setContent(
                  "<h1>來自購物天堂商城的啟用郵件:請點選下面連結啟用!</h1><h3><a href='http://localhost:8080/store_v2.0/UserServlet?method=active&code="
                          + code
                          + "'>http://localhost:8080/store_v2.0/UserServlet?method=active&code="
                          + code + "</a></h3>", "text/html;charset=UTF-8");
          // 傳送郵件:
          Transport.send(message);
      } catch (MessagingException e) {
          e.printStackTrace();
      }
  }
}

2.5.3 使用者登入模組

登陸的非同步校驗及自動登陸和記住使用者名稱

登陸資訊的非同步校驗:點選登陸時進行Ajax請求校驗使用者名稱和密碼及驗證碼是否正確,返回校驗的標識。

自動登陸:使用者登陸成功後將使用者資訊儲存在session中,並將使用者名稱和密碼儲存到客戶端的Cookie中;再次訪問時,通過配置的過濾器攔截,先檢視session中含有使用者的資訊(瀏覽器未關閉,重新請求頁面),當session中含有使用者資訊則直接放行;當session中沒有使用者資訊時,檢查使用者請求帶過來的Cookie中是否有儲存的使用者資訊,如果沒有使用者資訊則直接放行;如果有使用者的資訊則查詢資料庫校驗Cookie中的使用者名稱和密碼是否正確(防止惡意更改資料),如果校驗成功,則將使用者的資訊儲存在session中並放行,如果校驗失敗(使用者資訊被篡改)直接放行;使用者點選安全退出時,需要將Cookie和session中的儲存的使用者的資訊進行清除。

記住使用者名稱:使用者登入成功後,將使用者名稱寫到客戶端的Cookie中並設定儲存時間,使用者訪問登陸頁面時,在跳轉之前,獲取使用者帶來的Cookie中的使用者名稱資訊,如果存在,則將使用者名稱取出儲存在request域中,在登陸的jsp頁面取出作為使用者名稱輸入框的value值即可;使用者取消記住使用者名稱時將使用者端的Cookie中的使用者名稱清除即可。

public class UserServlet extends BaseServlet {
 ...
/**
 * 使用者登陸頁面
 */
public String userLoginUI(HttpServletRequest request, HttpServletResponse response){
  // 獲取客戶端請求中的Cookie  實現記住使用者名稱功能
  Cookie[] cookies = request.getCookies();
  // 查詢是否記住使用者名稱的Cookie
  Cookie findCookie = CookieUtils.findCookie(cookies, "remember");
  // 當Cookie有記住使用者名稱時,將使用者名稱儲存在request域中
  if(findCookie != null) {
      request.setAttribute("remember", findCookie.getValue());
  }
  return "/jsp/login.jsp";
}

/**
 * 使用者登陸的校驗
 */
public String checkLogin(HttpServletRequest request, HttpServletResponse response){
  // 獲取表單中的資料
  String code = request.getParameter("code");
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  // 校驗表單中的資料
  response.setContentType("text/html;charset=UTF-8");
  try {
      if(code == null || "".equals(code) || username == null || "".equals(username) || password == null || "".equals(password)) {
          response.getWriter().println("{\"msg\":\"null\"}");
          return null;
      }
  } catch (IOException e) {
      e.printStackTrace();
  }

  // 獲取session中的驗證碼
  String incode = (String) request.getSession().getAttribute("iconCode");
  // 清除session中本次的驗證碼
  request.getSession().removeAttribute("iconCode");

  // 校驗驗證碼是否正確
  if(!incode.equalsIgnoreCase(code)) {
      try {
          response.getWriter().println("{\"msg\":\"no\"}");
      } catch (IOException e) {
          e.printStackTrace();
      }
      return null;
  }

  // 封裝資料
  User user = new User();
  user.setUsername(username);
  user.setPassword(password);
  // 呼叫業務層進行查詢使用者是否存在
  // UserService userService = new UserServiceImpl();
  UserService userService = (UserService) BeanFactory.getBean("UserService");
  try {
      User existUser  = userService.checkUser(user);
      // 判斷使用者是否存在
      if(existUser == null) {
          response.getWriter().println("{\"msg\":\"no\"}");
      } else {
          // 使用者登陸成功
          // 將使用者資訊儲存在session中

          // 判斷是否勾選自動登陸,如果自動登陸勾選,則將使用者的登陸資訊儲存在cookie中
          String autoLogin = request.getParameter("autoLogin");
          if("true".equals(autoLogin)) {
              // 將使用者名稱和密碼以  username敏password形式儲存在Cookie中
              String username_password = existUser.getUsername()+"敏"+existUser.getPassword();
              // 將使用者名稱和密碼進行加密,將加密後的字串儲存在Cookie中
              String encrypt = DesUtils.encrypt(username_password);
              Cookie cookie  = new Cookie("autoLogin",encrypt);
              // 設定Cookie的有效路徑
              cookie.setPath(request.getContextPath()); // 有效路徑是當前專案的路徑
              // 設定Cookie的有效時間,7天
              cookie.setMaxAge(60 * 60 * 24 * 7);
              // 將Cookie寫到客戶端
              response.addCookie(cookie);
          }
          // 判斷是否勾選記住使用者名稱,勾選時,則將使用者名稱儲存在Cookie中
          String remember = request.getParameter("remember");
          if("true".equals(remember)) {
              Cookie rcookie = new Cookie("remember", existUser.getUsername());
              // 設定Cookie的有效路徑
              rcookie.setPath(request.getContextPath()); // 有效路徑是當前專案的路徑
              // 設定Cookie的有效時間,7天
              rcookie.setMaxAge(60 * 60 * 24 * 7);
              // 將Cookie寫到客戶端
              response.addCookie(rcookie);
          } else {
              Cookie[] cookies = request.getCookies();
              Cookie findCookie = CookieUtils.findCookie(cookies, "remember");
              if(findCookie != null) {
                  // 設定Cookie的有效路徑
                  findCookie.setPath(request.getContextPath()); // 有效路徑是當前專案的路徑
                  // 設定Cookie的時間為0
                  findCookie.setMaxAge(0);
                  // 將Cookie寫到客戶端
                  response.addCookie(findCookie);
              }
          }

          // 將用登陸的資訊儲存在session中
          request.getSession().setAttribute("existUser", existUser);
          response.getWriter().println("{\"msg\":\"ok\"}");
      }
      return null;
  } catch (Exception e) {
      e.printStackTrace();
  }
  return null;
}

/**
 * 使用者安全退出的方法
 * @Title: userLogOut
 * @Description: TODO(安全退出)
 * @param: @param request
 * @param: @param response
 * @param: @return   
 * @return: String   
 */
public String userLogOut(HttpServletRequest request, HttpServletResponse response){
  // 1. 清除session中的資訊
  request.getSession().removeAttribute("existUser");
  // 2. 清除Cookie中儲存的使用者的資訊
  Cookie[] cookies = request.getCookies();
  Cookie findCookie = CookieUtils.findCookie(cookies, "autoLogin");
  if(findCookie != null) {
      // 設定Cookie的有效路徑
      findCookie.setPath(request.getContextPath()); // 有效路徑是當前專案的路徑
      // 設定Cookie的時間為0
      findCookie.setMaxAge(0);
      // 將Cookie寫到客戶端
      response.addCookie(findCookie);
  }
  // 跳轉到主頁面
  return "/index.jsp";
}
...
/**
 * 使用者自動登入的過濾器
 */
public class AutoLoginFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {}
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
          FilterChain chain) throws IOException, ServletException {
      HttpServletRequest hsrequest = (HttpServletRequest) request;
      // 1.校驗session中是否含有使用者資訊
      User existUser = (User) hsrequest.getSession().getAttribute("existUser");
      // 1.1 session中含有使用者的登陸資訊,直接放行
      if(existUser != null) {
          chain.doFilter(hsrequest, response);
          return;
      }
      // 1.2 session中不含有使用者的登陸資訊,則查詢Cookie中是否有使用者的登陸資訊
      Cookie[] cookies = hsrequest.getCookies();
      Cookie findCookie = CookieUtils.findCookie(cookies,"autoLogin");
      // 2. Cookie中不含有使用者的資訊時,直接放行
      if(findCookie == null) {
          chain.doFilter(hsrequest, response);
          return;
      }
      // 3. Cookie中含有使用者的資訊,則取出Cookie中的使用者名稱和密碼,到資料庫中進行校驗
      String cookiValue = findCookie.getValue();
      // 進行解密
      try {
          String username_password = DesUtils.decrypt(cookiValue);
          User user = new User();
          user.setUsername(username_password.split("敏")[0]);
          user.setPassword(username_password.split("敏")[1]);

          // 呼叫業務層進行查詢
          // UserService userService = new UserServiceImpl();
          UserService userService = (UserService) BeanFactory.getBean("UserService");
          User checkUser = userService.checkUser(user);
          // 3.1 如果使用者資訊校驗不通過,直接放行
          if(checkUser == null) {
              chain.doFilter(hsrequest, response);
              return;
          }
          // 3.2 如果使用者資訊校驗成功,則將使用者的資訊儲存在session域中,然後放行
          hsrequest.getSession().setAttribute("existUser", checkUser);
          chain.doFilter(hsrequest, response);
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
  @Override
  public void destroy() {
  }
}
<!-- 自動登入過濾器  web.xml檔案配置 -->
<filter>
  <filter-name>AutoLoginFilter</filter-name>
  <filter-class>com.store.web.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>AutoLoginFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2.6分類模組

使用快取技術實現分類資料的載入:首頁載入需要顯示分類資料,在首頁載入時使用Ajax非同步請求分類資料,由於分類資料是很少變動,可以將分類資料儲存在快取中,再次請求分類資料時可以不用從資料庫查詢,以提高效率。


# 常用的快取技術:常用的有EHCache,Memcached,Redis等快取技術,本次使用EHCache技術

 * EHCache:Hibernate框架二級快取使用,使用時如果配置了overflowToDisk="true" 則需要將需要序列化的javabean實現序列化介面

EHCache的配置檔案:

<!--ehcache.xml配置檔案,放置在專案的src目錄下-->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
  <!-- 快取超出限制的數量時序列化到硬碟的路徑 -->
  <diskStore path="E:/develop/apache-tomcat-7.0.69/webapps/store_v2.0/ehcache" />
  <!-- cache可以配置多個,通過配置的name屬性進行區分 -->
  <!-- 
      name:代表緩衝的名稱
      maxElementsInMemory:記憶體中能夠緩衝資料的條數
      timeToLiveSeconds:緩衝的存活時間
      overflowToDisk:當超出記憶體中快取數量時是否寫到本地硬碟
   -->
      <!--categoryCache:分類資料的快取名稱-->
  <cache 
      name="categoryCache" 
      maxElementsInMemory="10000"
      eternal="false"
      timeToIdleSeconds="120" 
      timeToLiveSeconds="120" 
      overflowToDisk="true"
      maxElementsOnDisk="10000000" 
      diskPersistent="false"
      diskExpiryThreadIntervalSeconds="120" 
      memoryStoreEvictionPolicy="LRU" />
</ehcache>

快取的使用:在分類模組的業務層進行使用

// 分類模組的Servlet
public class CategoryServlet extends BaseServlet {
  private static final long serialVersionUID = 1L;
  /**
   * 查詢所有的分類資料
   */
  public String findAllCategory(HttpServletRequest request, HttpServletResponse response) {
      // 呼叫業務層進行查詢所有的分類資料
      // CategoryService categoryService = new CategoryServiceImpl();
      CategoryService categoryService = (CategoryService) BeanFactory.getBean("CategoryService");
      try {
          List<Category> list = categoryService.findAllCategory();
          // 將資料轉換為json格式,傳送給主頁面
          JSONArray jsonArray = JSONArray.fromObject(list);
          // 將資料傳遞給主頁面
          response.setContentType("text/html;charset=UTF-8");
          response.getWriter().println(jsonArray.toString());
      } catch (Exception e) {
          e.printStackTrace();
      }
      return null;
  }
}

/**
 * 分類的業務層的實現類Cat