家居網購項目實現02 5.功能04-會員登錄 5.1需求分析/圖解 需求如圖: 輸入用戶名、密碼後提交 判斷該用戶是否存在 如果存在,顯示登錄成功頁面 否則返回登錄頁面,要求重新登錄 要求改進登錄密碼為md5加密 5.2思路分析 5.3代碼實現 根據上述分析圖,在對應的層添加方法 5.3.1dao層 ...
家居網購項目實現02
5.功能04-會員登錄
5.1需求分析/圖解
需求如圖:
- 輸入用戶名、密碼後提交
- 判斷該用戶是否存在
- 如果存在,顯示登錄成功頁面
- 否則返回登錄頁面,要求重新登錄
- 要求改進登錄密碼為md5加密
5.2思路分析
![](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/%E5%AE%8C%E6%88%90%E4%BC%9A%E5%91%98%E7%99%BB%E5%BD%95%E7%9A%84%E7%A8%8B%E5%BA%8F%E6%A1%86%E6%9E%B6%E5%9B%BE.png)
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完成測試
![image-20221216192022479](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216192022479.png)
![image-20221216191948442](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216191948442.png)
6.功能05-登錄錯誤提示,表單回顯
6.1需求分析/圖解
![image-20221216191834383](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216191834383.png)
- 輸入用戶名,密碼後提交
- 如果輸入有誤,則給出提示
- 在登錄表單回顯用戶名
6.2思路分析
在5.2分析圖的基礎上修改如下兩處:
![image-20221216193057250](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216193057250.png)
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完成測試
![image-20221216201148932](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216201148932.png)
![image-20221216201221612](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216201221612.png)
7.功能06-web層servlet減肥
7.1需求分析/圖解
![image-20221216204810122](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216204810122.png)
- 如圖,一個請求對應一個Servlet,會造成Servlet太多,不利於管理
- 在項目開發中,同一個業務(模塊),一般對應一個Servlet即可,比如LoginServlet和RegisterServlet都處理和會員相關的業務,應當合併
7.2方案一-if-else
![image-20221216210835186](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216210835186.png)
前端頁面兩個表單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語句也會隨之增多,代碼可讀性變差,因此這裡使用第二種方案實現,思想如下:
![image-20221216220943007](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216220943007.png)
![image-20221216220928145](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216220928145.png)
每一個業務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完成測試
註冊業務:
![image-20221216212552315](https://liyuelian.oss-cn-shenzhen.aliyuncs.com/imgs/image-20221216212552315.png)
登錄業務: