在Web开发模式中,有两个主要的开发结构,称为模式一(Mode I)和模式二(Mode II).html
首先咱们来理清一些概念吧:java
模式一指的就是在开发中将显示层、控制层、数据层的操做统一交给JSP或者JavaBean来进行处理!web
模式一有两种状况:数据库
彻底使用JSP作开发设计模式
优势:服务器
缺点:微信
使用JSP+JavaBean作开发dom
优势:jsp
缺点:ide
咱们使用JavaBean+JSP开发一个简易的计算器吧,效果如图下:
public class Calculator { private double firstNum; private double secondNum; private char Operator = '+'; private double result; //JavaBean提供了计算的功能 public void calculate() { switch (this.Operator) { case '+': this.result = this.firstNum + this.secondNum; break; case '-': this.result = this.firstNum - this.secondNum; break; case '*': this.result = this.firstNum * this.secondNum; break; case '/': if (this.secondNum == 0) { throw new RuntimeException("除数不能为0"); } this.result = this.firstNum / this.secondNum; break; default: throw new RuntimeException("传入的字符非法!"); } } public double getFirstNum() { return firstNum; } public void setFirstNum(double firstNum) { this.firstNum = firstNum; } public double getSecondNum() { return secondNum; } public void setSecondNum(double secondNum) { this.secondNum = secondNum; } public char getOperator() { return Operator; } public void setOperator(char operator) { Operator = operator; } public double getResult() { return result; } public void setResult(double result) { this.result = result; } }
<%--开发用户界面--%> <form action="/zhongfucheng/1.jsp" method="post"> <table border="1"> <tr> <td colspan="2">简单计数器</td> <td></td> </tr> <tr> <td>第一个参数:</td> <td><input type="text" name="firstNum"></td> </tr> <tr> <td>运算符</td> <td> <select name="operator"> <option value="+">+</option> <option value="-">-</option> <option value="*">*</option> <option value="/">/</option> </select> </td> </tr> <tr> <td>第二个参数:</td> <td><input type="text " name="secondNum"></td> </tr> <tr> <td colspan="2"><input type="submit" value="提交"></td> <td></td> </tr> </table> </form>
<%--获取获得Bean对象--%> <jsp:useBean id="calculator" class="domain.Calculator" scope="page"/> <%--设置Bean对象的数据--%> <jsp:setProperty name="calculator" property="*"/> <%--调用Caculator的方法计算出值--%> <jsp:scriptlet> calculator.calculate(); </jsp:scriptlet> <%--得出的结果:--%> <c:out value="计算得出的结果是:"/> <jsp:getProperty name="calculator" property="firstNum"/> <jsp:getProperty name="calculator" property="operator"/> <jsp:getProperty name="calculator" property="secondNum"/> <c:out value="="/> <jsp:getProperty name="calculator" property="result"/>
开发这个简易的计算器,只用了一个JSP页面和一个JavaBean完成!
总的来讲,Mode I 适合小型的开发,复杂程序低的开发,由于Mode I 的特色就是开发速度快,但在进行维护的时候就要付出更大的代价!
Mode II 中全部的开发都是以Servlet为主体展开的,由Servlet接收全部的客户端请求,而后根据请求调用相对应的JavaBean,并全部的显示结果交给JSP完成!,也就是俗称的MVC设计模式!
MVC设计模式:
咱们使用MVC模式开发一个简单的用户登录注册的案例吧!做为一个简单的用户登录注册,这里就直接使用XML文档看成小型数据库吧!
private int id; private String username; private String password; private String email; private Date birthday; //....各类setter、getter
//外界传递用户名和密码进来,我要在XML文档中查找是否有该条记录 public User find(String username, String password) { //获得XML文档的流对象 InputStream inputStream = UserImplXML.class.getClassLoader().getResourceAsStream("user.xml"); //获得dom4j的解析器对象 SAXReader saxReader = new SAXReader(); try { //解析XML文档 Document document = saxReader.read(path); //使用XPATH技术,查找XML文档中是否有传递进来的username和password Element element = (Element) document.selectSingleNode("//user[@username='" + username + "' and@password='" + password + "']"); if (element == null) { return null; } //若是有,就把XML查出来的节点信息封装到User对象,返回出去 User user = new User(); user.setId(Integer.parseInt(element.attributeValue("id"))); user.setUsername(element.attributeValue("username")); user.setPassword(element.attributeValue("password")); user.setEmail(element.attributeValue("email")); //生日就须要转换一下了,XML文档保存的是字符串,User对象须要的是Date类型 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd"); Date birthday = simpleDateFormat.parse(element.attributeValue("birthday")); user.setBirthday(birthday); //返回User对象出去 return user; } catch (DocumentException e) { e.printStackTrace(); throw new RuntimeException("初始化时候出错啦!"); } catch (ParseException e) { e.printStackTrace(); throw new RuntimeException("查询的时候出错啦!"); } }
private String username = "zhongfucheng"; private String password = "123"; @Test public void testLogin() { UserImplXML userImplXML = new UserImplXML(); User user = userImplXML.find(username, password); System.out.println(user.getBirthday()); System.out.println(user.getEmail()); System.out.println(user.getId()); System.out.println(user.getUsername()); System.out.println(user.getPassword()); }
3.2注册功能
//注册功能,外界传递一个User对象进来。我就在XML文档中添加一条信息 public void register(User user) { //获取XML文档路径! String path = UserImplXML.class.getClassLoader().getResource("user.xml").getPath(); try { //获取dom4j的解析器,解析XML文档 SAXReader saxReader = new SAXReader(); Document document = saxReader.read(path); //在XML文档中建立新的节点 Element newElement = DocumentHelper.createElement("user"); newElement.addAttribute("id", String.valueOf(user.getId())); newElement.addAttribute("username", user.getUsername()); newElement.addAttribute("email", user.getEmail()); newElement.addAttribute("password", user.getPassword()); //日期返回的是指定格式的日期 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd"); String date = simpleDateFormat.format(user.getBirthday()); newElement.addAttribute("birthday",date); //把新建立的节点增长到父节点上 document.getRootElement().add(newElement); //把XML内容中文档的内容写到硬盘文件上 OutputFormat outputFormat = OutputFormat.createPrettyPrint(); outputFormat.setEncoding("UTF-8"); XMLWriter xmlWriter = new XMLWriter(new FileWriter(path),outputFormat); xmlWriter.write(document); xmlWriter.close(); } catch (DocumentException e) { e.printStackTrace(); throw new RuntimeException("注册的时候出错了!!!"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("注册的时候出错了!!!"); } }
@Test public void testRegister() { UserImplXML userImplXML = new UserImplXML(); //这里我为了测试的方便,就添加一个带5个参数的构造函数了! User user = new User(10, "nihao", "123", "sina@qq.com", new Date()); userImplXML.register(user); }
service层的开发就很是简单了!上面已经说了,service层就是:将多个原子性的DAO操做进行组合,组合成一个完整的业务逻辑。简单来讲:对web层提供全部的业务服务的!
在逻辑代码不是很是复杂的状况下,咱们能够没有service层的,这里仍是演示一下吧!
public class UserServiceXML { //Service层就是调用Dao层的方法,咱们就直接在类中建立Dao层的对象了 UserDao userImplXML = new UserImplXML(); public void register(User user) { userImplXML.register(user); } public void login(String username, String password) { userImplXML.find(username, password); } }
public class RegisterUIServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //直接跳转到显示注册界面的JSP request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doPost(request, response); } }
<h1>欢迎来到注册界面!</h1> <%--提交给处理注册的处理Servlet--%> <form method="post" action="${pageContext.request.contextPath}/RegisterServlet"> <table> <%--对于id来说,是服务器分配的!不须要用户本身输入--%> <tr> <td>用户名</td> <td> <input type="text " name="username"> </td> </tr> <tr> <td>密码</td> <td> <input type="text" name="password"> </td> </tr> <tr> <td>确认密码</td> <td> <input type="text" name="password"> </td> </tr> <tr> <td>邮箱</td> <td> <input type="text" name="email"> </td> </tr> <tr> <td>生日</td> <td> <input type="text " name="birethday"> </td> </tr> <tr> <td> <input type="submit" value="提交"> </td> <td> <input type="reset" value="重置!"> </td> </tr> </table> </form>
//首先要接受Parameter的参数,封装到User里面去 String username = request.getParameter("username"); String password = request.getParameter("password"); //......若是参数过多,咱们就要写好多好多相似的代码了...
/* * 将Parameter参数的数据封装到Bean中,为了外边不用强转,这里就使用泛型了! * * @request 因为要获取的是Parameter参数的信息,因此须要有request对象 * @tClass 自己是不知道封装什么对象的,因此用class * * */ public static <T> T request2Bean(HttpServletRequest httpServletRequest, Class<T> tClass) { try { //建立tClass的对象 T bean = tClass.newInstance(); //获取获得Parameter中所有的参数的名字 Enumeration enumeration = httpServletRequest.getParameterNames(); //遍历上边获取获得的集合 while (enumeration.hasMoreElements()) { //获取获得每个带过来参数的名字 String name = (String) enumeration.nextElement(); //获取获得值 String value = httpServletRequest.getParameter(name); //把数据封装到Bean对象中 BeanUtils.setProperty(bean, name, value); } return bean; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("封装数据到Bean对象中出错了!"); } }
//日期转换器 ConvertUtils.register(new DateLocaleConverter(), Date.class);
/*生成ID*/ public static int makeId() { return Integer.parseInt(UUID.randomUUID().toString()); }
User user = WebUtils.request2Bean(request, User.class); user.setId(WebUtils.makeId()); //调用service层的注册方法,实现注册 ServiceBussiness serviceBussiness = new UserServiceXML(); serviceBussiness.register(user);
上面的代码是不够完善的(没有校验用户输入的信息、注册成功或失败都没有给出提示..等等)
public class FormBean { //表单提交过来的数据全都是String类型的,birthday也不例外! private String username; private String password; private String password2; private String email; private String birthday; /*用于判断表单提交过来的数据是否合法*/ public boolean validate() { return false; } //......各类setter、getter方法 }
public boolean validate() { //用户名不能为空,而且要是3-8的字符 abcdABcd if (this.username == null || this.username.trim().equals("")) { return false; } else { if (!this.username.matches("[a-zA-Z]{3,8}")) { return false; } } //密码不能为空,而且要是3-8的数字 if (this.password == null || this.password.trim().equals("")) { return false; } else { if (!this.password.matches("\\d{3,8}")) { return false; } } //两次密码要一致 if (this.password2 != null && !this.password2.trim().equals("")) { if (!this.password2.equals(this.password)) { return false; } } //邮箱能够为空,若是为空就必须合法 if (this.email != null && !this.email.trim().equals("")) { if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { System.out.println("邮箱错误了!"); return false; } } //日期能够为空,若是为空就必须合法 if (this.birthday != null && !this.birthday.trim().equals("")) { try { DateLocaleConverter dateLocaleConverter = new DateLocaleConverter(); dateLocaleConverter.convert(this.birthday); } catch (Exception e) { System.out.println("日期错误了!"); return false; } } //若是上面都没有执行,那么就是合法的了,返回true return true; }
//将表单的数据封装到formBean中 FormBean formBean = WebUtils.request2Bean(request, FormBean.class); //验证表单的数据是否合法,若是不合法就跳转回去注册的页面 if(formBean.validate()==false){ request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); return; } try { //将表单的数据封装到User对象中 User user = WebUtils.request2Bean(request, User.class); user.setId(WebUtils.makeId()); //调用service层的注册方法,实现注册 ServiceBussiness serviceBussiness = new UserServiceXML(); serviceBussiness.register(user); } catch (Exception e) { e.printStackTrace(); }
它抛出了错误!缘由也很是简单:表单数据提交给Servlet,Servlet将表单的数据(Parameter中的数据)用BeanUtils封装到User对象中,当封装到日期的时候,发现日期为null,没法转换成日期对象!
那咱们如今要怎么解决呢?
首先咱们要明确:由于咱们在设定的时候,已经容许了email和birthday能够为空,那么在DAO层就应该有相应的逻辑判断email和birthday是否为空!
if (user.getEmail() == null) { newElement.addAttribute("email", ""); } else { newElement.addAttribute("email", user.getEmail()); } //若是不是空才格式化信息 if (user.getBirthday() != null) { //日期返回的是指定格式的日期 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); String date = simpleDateFormat.format(user.getBirthday()); newElement.addAttribute("birthday", date); } else { newElement.addAttribute("birthday", ""); }
解决办法:
public static <T> T request2Bean(HttpServletRequest httpServletRequest, Class<T> tClass) { try { //建立tClass的对象 T bean = tClass.newInstance(); //获取获得Parameter中所有的参数的名字 Enumeration enumeration = httpServletRequest.getParameterNames(); //日期转换器 ConvertUtils.register(new DateLocaleConverter(), Date.class); //遍历上边获取获得的集合 while (enumeration.hasMoreElements()) { //获取获得每个带过来参数的名字 String name = (String) enumeration.nextElement(); //获取获得值 String value = httpServletRequest.getParameter(name); //若是Parameter中的数据为"",那么我就不封装到User对象里边去!执行下一次循环 if (value == "") { continue; } else { //把数据封装到Bean对象中 BeanUtils.setProperty(bean, name, value); } } return bean; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("封装数据到Bean对象中出错了!"); } }
将数据封装到User对象中还有另一个办法:
//将表单的数据封装到formBean中 FormBean formBean = WebUtils.request2Bean(request, FormBean.class); //验证表单的数据是否合法,若是不合法就跳转回去注册的页面 if(formBean.validate()==false){ request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); return; } try { //这是第一种-------------------------- /*User user = new User(); user.setId(WebUtils.makeId()); BeanUtils.copyProperties(user,formBean);*/ //------------------------------------------ //这是第二种 User user1 = WebUtils.request2Bean(request,User.class); user1.setId(WebUtils.makeId()); //----------------------------------- //调用service层的注册方法,实现注册 ServiceBussiness serviceBussiness = new UserServiceXML(); serviceBussiness.register(user1); } catch (Exception e) { e.printStackTrace(); }
如今还有问题,若是我填写信息不合法,提交给服务器验证之后,服务器应该告诉用户哪一个信息不合法,而不是直接把跳转回注册界面,把全部的信息所有清空,让用户从新填写!
咱们应该这样作:当发现用户输入的信息不合法时,把错误的信息记录下来,等到返回注册页面,就提示用户哪里出错了!
//表单提交过来的数据全都是String类型的,birthday也不例外! private String username; private String password; private String password2; private String email; private String birthday; //记录错误的信息 private HashMap<String, String> error = new HashMap<>(); /*用于判断表单提交过来的数据是否合法*/ public boolean validate() { //用户名不能为空,而且要是3-8的字符 abcdABcd if (this.username == null || this.username.trim().equals("")) { error.put("username", "用户名不能为空,而且要是3-8的字符"); return false; } else { if (!this.username.matches("[a-zA-Z]{3,8}")) { error.put("username", "用户名不能为空,而且要是3-8的字符"); return false; } } //密码不能为空,而且要是3-8的数字 if (this.password == null || this.password.trim().equals("")) { error.put("password", "密码不能为空,而且要是3-8的数字"); return false; } else { if (!this.password.matches("\\d{3,8}")) { error.put("password", "密码不能为空,而且要是3-8的数字"); return false; } } //两次密码要一致 if (this.password2 != null && !this.password2.trim().equals("")) { if (!this.password2.equals(this.password)) { error.put("password2", "两次密码要一致"); return false; } } //邮箱能够为空,若是为空就必须合法 if (this.email != null && !this.email.trim().equals("")) { if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { error.put("email", "邮箱不合法!"); return false; } } //日期能够为空,若是为空就必须合法 if (this.birthday != null && !this.birthday.trim().equals("")) { try { DateLocaleConverter dateLocaleConverter = new DateLocaleConverter(); dateLocaleConverter.convert(this.birthday); } catch (Exception e) { error.put("birthday", "日期不合法!"); return false; } } //若是上面都没有执行,那么就是合法的了,返回true return true; } //.....各类的setter和getter
//验证表单的数据是否合法,若是不合法就跳转回去注册的页面 if(formBean.validate()==false){ //在跳转以前,把formbean对象传递给注册页面 request.setAttribute("formbean", formBean); request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); return; }
作到这里,仍是有丢丢的问题,咱们不该该把用户输入的数据所有清空的!你想一想,若是用户注册须要输入多个信息,仅仅一个出错了,就把所有信息清空,要他从新填写,这样是不合理的!
尚未完善,细心的朋友能够发现,上面图的日期也是错误的,可是没一次性标记出来给用户!要改也十分简单:在验证的时候,不要先急着return false 用一个布尔型变量记住,最后返回布尔型的变量便可
不管注册成功仍是失败都须要给用户一个友好界面的!
登录和注册是相似的,咱们按着注册的步骤来写就对了!
首先写一个提供登录界面的Servlet
//直接跳转到登录界面 request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
<h1>这是登录界面</h1> <form action="${pageContext.request.contextPath}/LoginServlet" method="post"> <table> <tr> <td>用户名</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码</td> <td><input type="password" name="password"></td> </tr> <tr> <td><input type="submit" value="提交"></td> <td><input type="reset" name="重置"></td> </tr> </table> </form>
//获取提交过来的数据 String username = request.getParameter("username"); String password = request.getParameter("password"); //调用service层的方法,去查询数据库(XML)是否有该条记录 try { ServiceBussiness serviceBussiness = new UserServiceXML(); User user = serviceBussiness.login(username, password); if (user == null) { request.setAttribute("message", "用户名或密码是错的"); } else { request.setAttribute("message","登录成功"); } } catch (Exception e) { e.printStackTrace(); request.setAttribute("message","登录失败咯"); } request.getRequestDispatcher("/message.jsp").forward(request, response);
<h1>这是首页!</h1> <a href="${pageContext.request.contextPath}/LoginUIServlet">登录</a> <a href="${pageContext.request.contextPath}/RegisterUIServlet">注册</a> </body>
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章的同窗,能够关注微信公众号: Java3y