家居網購專案實現02
5.功能04-會員登入
5.1需求分析/圖解
需求如圖:
- 輸入使用者名稱、密碼後提交
- 判斷該使用者是否存在
- 如果存在,顯示登入成功頁面
- 否則返回登入頁面,要求重新登入
- 要求改進登入密碼為md5加密
5.2思路分析
5.3程式碼實現
根據上述分析圖,在對應的層新增方法
5.3.1dao層
-
修改MemberDAO介面,宣告queryMemberByUsernameAndPassword()方法
//提供一個透過使用者名稱和密碼返回對應的Member的方法 public Member queryMemberByUsernameAndPassword(String username,String password);
-
修改MemberDAOImpl實現類,實現queryMemberByUsernameAndPassword()方法
/** * 透過使用者名稱和密碼返回對應的Member物件 * * @param username 使用者名稱 * @param password 密碼 * @return 返回值為對應的Member物件,如果不存在則返回null */ @Override public Member queryMemberByUsernameAndPassword(String username, String password) { String sql = "SELECT * FROM `member` WHERE `username`=? AND `password`=MD5(?);"; return querySingle(sql, Member.class, username, password); }
-
在utils包中的MemberDAOImplTest類中增加測試方法
@Test public void queryMemberByUsernameAndPassword() { Member member = memberDAO.queryMemberByUsernameAndPassword ("king", "king"); System.out.println("member=" + member); }
程式碼測試透過
5.3.2service層
-
修改MemberService介面,宣告login方法
//登入使用者 //相比於直接傳遞使用者名稱和密碼,傳遞一個Member物件擴充性會比較好一些 public Member login(Member member);
-
修改MemberServiceImpl介面實現類,實現login方法
/** * 根據登入傳入的member資訊,返回對應的在資料庫中的member物件 * * @param member * @return 返回的是資料庫中的member物件,若不存在則返回null */ @Override public Member login(Member member) { return memberDAO.queryMemberByUsernameAndPassword (member.getUsername(), member.getPassword()); }
-
在utils包中的MemberServiceImplTest類中增加測試方法
@Test public void login() { Member member = memberService.login (new Member(null, "admin", "admin", null)); System.out.println("member=" + member); }
程式碼測試透過
5.3.3web層
-
配置loginServlet
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.li.furns.web.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/loginServlet</url-pattern> </servlet-mapping>
-
建立LoginServlet
package com.li.furns.web; import com.li.furns.entity.Member; import com.li.furns.service.MemberService; import com.li.furns.service.impl.MemberServiceImpl; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class LoginServlet extends HttpServlet { private MemberService memberService = new MemberServiceImpl(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.接收使用者名稱和密碼 //如果前端輸入的是null,後臺接收的資料為空串"" String username = request.getParameter("username"); String password = request.getParameter("password"); //構建一個member物件 Member member = new Member(null, username, password, null); //2.呼叫MemberServiceImpl的login方法 if (memberService.login(member) == null) {//資料庫中沒有該使用者,返回登入頁面 //注意路徑 request.getRequestDispatcher("/views/member/login.html") .forward(request, response); } else { //否則,跳轉到登入成功頁面 request.getRequestDispatcher("/views/member/login_ok.html") .forward(request, response); } } }
5.4完成測試
6.功能05-登入錯誤提示,表單回顯
6.1需求分析/圖解
- 輸入使用者名稱,密碼後提交
- 如果輸入有誤,則給出提示
- 在登入表單回顯使用者名稱
6.2思路分析
在5.2分析圖的基礎上修改如下兩處:
6.3程式碼實現
6.3.1web層
-
修改LoginServlet,將錯誤提示和使用者名稱放入request域中
package com.li.furns.web; import com.li.furns.entity.Member; import com.li.furns.service.MemberService; import com.li.furns.service.impl.MemberServiceImpl; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class LoginServlet extends HttpServlet { private MemberService memberService = new MemberServiceImpl(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.接收使用者名稱和密碼 //如果前端輸入的是null,後臺接收的資料為空串"" String username = request.getParameter("username"); String password = request.getParameter("password"); //構建一個member物件 Member member = new Member(null, username, password, null); //2.呼叫MemberServiceImpl的login方法 if (memberService.login(member) == null) {//資料庫中沒有該使用者,返回登入頁面 //登入失敗,將錯誤資訊和登入會員名放入request域中 request.setAttribute("errInfo", "登入失敗,使用者名稱或者密碼錯誤"); request.setAttribute("username", username); //注意路徑 request.getRequestDispatcher("/views/member/login.jsp") .forward(request, response); } else { //否則,跳轉到登入成功頁面 request.getRequestDispatcher("/views/member/login_ok.html") .forward(request, response); } } }
-
將login.html改為login.jsp(檔案右鍵Refactor-->Rename,在彈窗中點選Do Refactor,會把其他檔案引用login.html的資訊自動改為login.jsp)
部分程式碼,詳細程式碼請看 https://github.com/liyuelian/furniture_mall.git
<div class="login-register-form"> <%--提示錯誤資訊--%> <span class="errorMsg" style="float: right; font-weight: bold; font-size: 20pt; margin-left: 10px;"> ${requestScope.errInfo} </span> <form action="loginServlet" method="post"> <input type="text" name="username" placeholder="Username" value="${requestScope.username}"/> <input type="password" name="password" placeholder="Password"/> <div class="button-box"> <div class="login-toggle-btn"> <input type="checkbox"/> <a class="flote-none" href="javascript:void(0)">Remember me</a> <a href="#">Forgot Password?</a> </div> <button type="submit"><span>Login</span></button> </div> </form>
6.4完成測試
7.功能06-web層servlet減肥
7.1需求分析/圖解
- 如圖,一個請求對應一個Servlet,會造成Servlet太多,不利於管理
- 在專案開發中,同一個業務(模組),一般對應一個Servlet即可,比如LoginServlet和RegisterServlet都處理和會員相關的業務,應當合併
7.2方案一-if-else
前端頁面兩個表單login和register的action都提交到MemberServlet中
- 分別給兩個表單新增hidden元素,分別表示註冊和登入
- 當資訊提交到MemberServlet後,獲取action引數值
- 再根據不同的值來呼叫對應的方法即可(將原來的業務分別封裝到login方法和Register方法中)
7.3方案一程式碼實現
-
修改login.jsp,分別在login和register表單中新增hidden,兩個表單都提交到MemberServlet處理
-
在web.xml中配置MemberServlet
<servlet> <servlet-name>MemberServlet</servlet-name> <servlet-class>com.li.furns.web.MemberServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MemberServlet</servlet-name> <url-pattern>/memberServlet</url-pattern> </servlet-mapping>
-
實現MemberServlet
package com.li.furns.web; import com.li.furns.entity.Member; import com.li.furns.service.MemberService; import com.li.furns.service.impl.MemberServiceImpl; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class MemberServlet extends HttpServlet { private MemberService memberService = new MemberServiceImpl(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取提交表單的hidden元素值,判斷進行login還是register業務 String action = request.getParameter("action"); if ("login".equals(action)) { //進入登入業務 login(request, response); } else if ("register".equals(action)) { //進入註冊業務 register(request, response); } } public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.接收使用者名稱和密碼 //如果前端輸入的是null,後臺接收的資料為空串"" String username = request.getParameter("username"); String password = request.getParameter("password"); //構建一個member物件 Member member = new Member(null, username, password, null); //2.呼叫MemberServiceImpl的login方法 if (memberService.login(member) == null) {//資料庫中沒有該使用者,返回登入頁面 //登入失敗,將錯誤資訊和登入會員名放入request域中 request.setAttribute("errInfo", "登入失敗,使用者名稱或者密碼錯誤"); request.setAttribute("username", username); //注意路徑 request.getRequestDispatcher("/views/member/login.jsp") .forward(request, response); } else { //否則,跳轉到登入成功頁面 request.getRequestDispatcher("/views/member/login_ok.html") .forward(request, response); } } public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //接收使用者註冊資訊--引數名要以前端頁面的變數名為準 String username = request.getParameter("username"); String password = request.getParameter("password"); String email = request.getParameter("email"); //如果返回false,說明該使用者資訊可以註冊 if (!memberService.isExistsUsername(username)) { //構建一個member物件 Member member = new Member(null, username, password, email); if (memberService.registerMember(member)) { //如果註冊成功,請求轉發到register_ok.html request.getRequestDispatcher("/views/member/register_ok.html") .forward(request, response); } else { //註冊失敗,請求轉發到register_fail.html request.getRequestDispatcher("/views/member/register_fail.html") .forward(request, response); } } else {//否則不能進行註冊 //請求轉發到login.html //後面可以加入提示資訊 request.getRequestDispatcher("/views/member/login.jsp") .forward(request, response); } } }
7.4方案二-反射+模板設計模式+動態繫結
雖然方案一也可以實現業務需求,但是隨著業務的增加,if-else語句也會隨之增多,程式碼可讀性變差,因此這裡使用第二種方案實現,思想如下:
每一個業務Servlet類中都會有doPost和doGet方法,現在建立一個BasicServlet抽象類,其他的業務Servlet類都繼承BasicServlet抽象類。
將業務類中的doPost和doGet方法抽象到BasicServlet中,當http請求到業務類時,因為業務類中沒有重寫doPost和doGet,就會到父類BasicServlet中找並呼叫。
同時在父類BasicServlet的doPost()方法中使用動態繫結,透過反射去獲取到子類中的某個業務方法,然後呼叫。
7.5方案二程式碼實現
-
修改MemberServlet,將doPost方法抽象到父類BasicServlet中:
package com.li.furns.web; import com.li.furns.entity.Member; import com.li.furns.service.MemberService; import com.li.furns.service.impl.MemberServiceImpl; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; /** * 該Servlet處理和Member相關的請求 * * @author 李 * @version 1.0 */ public class MemberServlet extends BasicServlet { private MemberService memberService = new MemberServiceImpl(); /** * 處理會員登入業務 * * @param request * @param response * @throws ServletException * @throws IOException */ public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.接收使用者名稱和密碼 //如果前端輸入的是null,後臺接收的資料為空串"" String username = request.getParameter("username"); String password = request.getParameter("password"); //構建一個member物件 Member member = new Member(null, username, password, null); //2.呼叫MemberServiceImpl的login方法 if (memberService.login(member) == null) {//資料庫中沒有該使用者,返回登入頁面 //登入失敗,將錯誤資訊和登入會員名放入request域中 request.setAttribute("errInfo", "登入失敗,使用者名稱或者密碼錯誤"); request.setAttribute("username", username); //注意路徑 request.getRequestDispatcher("/views/member/login.jsp") .forward(request, response); } else { //否則,跳轉到登入成功頁面 request.getRequestDispatcher("/views/member/login_ok.html") .forward(request, response); } } /** * 處理會員註冊業務 * * @param request * @param response * @throws ServletException * @throws IOException */ public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //接收使用者註冊資訊--引數名要以前端頁面的變數名為準 String username = request.getParameter("username"); String password = request.getParameter("password"); String email = request.getParameter("email"); //如果返回false,說明該使用者資訊可以註冊 if (!memberService.isExistsUsername(username)) { //構建一個member物件 Member member = new Member(null, username, password, email); if (memberService.registerMember(member)) { //如果註冊成功,請求轉發到register_ok.html request.getRequestDispatcher("/views/member/register_ok.html") .forward(request, response); } else { //註冊失敗,請求轉發到register_fail.html request.getRequestDispatcher("/views/member/register_fail.html") .forward(request, response); } } else {//否則不能進行註冊 //請求轉發到login.html //後面可以加入提示資訊 request.getRequestDispatcher("/views/member/login.jsp") .forward(request, response); } } }
-
建立BasicServlet,在該抽象類中使用使用模板模式+反射+動態繫結
package com.li.furns.web; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; /** * 業務servlet的共同父類 * BasicServlet 是供子類去繼承的,不需要在web.xml中配置 * 使用模板模式+反射+動態繫結===>簡化了多個if-else的語句 * * @author 李 * @version 1.0 */ public abstract class BasicServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //獲取提交表單的隱藏域元素的值 //如果我們使用模板模式+反射+動態繫結,要滿足action的值要和方法名一致 String action = req.getParameter("action"); //使用反射,獲取到當前物件的方法 //1.this就是請求的業務Servlet,即執行型別 //2.declaredMethod 方法物件就是當前請求的業務servlet對應的action名稱的方法 try { /** * public Method getDeclaredMethod(){} * 該方法返回一個Method物件,它反射此Class物件所表示的類或介面的指定已宣告方法。 * 引數:此方法接受兩個引數: * -方法名稱,這是要獲取的方法。 * -引數型別 這是指定的方法的引數型別的陣列。 * 返回值:此方法以 Method 物件的形式返回此類的指定方法。 */ Method declaredMethod = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); //使用方法物件進行反射呼叫 //public Object invoke(Object obj, Object... args){} declaredMethod.invoke(this, req, resp); } catch (Exception e) { e.printStackTrace(); } } }
之後再去開發業務類,只需要繼承BasicServlet即可,推薦使用方案二
7.6完成測試
註冊業務:
登入業務: