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完成测试
注册业务:
登录业务: