-
Version01制作思路:
咱们这个登陆小项目,整体思路是先在register.jsp将form表单写好。然后,将用户名和密码等参数直接由form表单提交到registerDeal.jsp中,由register.jsp中写好的JDBC代码实现向DB中插入数据,以及从DB中拿到数据,进行非空唯一的判断,并借助response.sendRedirect("......")实现,当登陆成功以及登陆失败会跳转出对应的registerFail.jsp以及registerSuccess.jsp两个页面。
- 步骤一:新建web项目,先在webapps(有时候也叫WebContent)下的WEB-INF下新建所用到的前端.jsp文件,如下四个.jsp文件。当然,首先要进行编写的是register.jsp页面。
- Notes:如果前台页面中想要写中文,那么就得在配置中的JSP中将Encoding改为Chinese National Standard,然后就可以在前端页面中写中文了。
register.jsp的主要代码如下: <title>用户注册</title> </head> Form表单提交的一种写法如下: <body>//一般情况下,form请求都用post(无特殊要求时) <form method="post" action="registerDeal.jsp"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> 确认密码:<input type="password" name="password2"><br> <input type="submit" value="提交"/> </form> </body> //<form method="post" action="代表点击(form表单)提交之后应该到哪里去"></form> Form表单提交的另一种写法如下: <title>我的动态的web小项目,内瞅瞅</title> <!-- 代表你请求来的那个页面的标题栏上面的内容 --> </head> <body> <form action=HttpTest method="get"> <input type="text" name="get方式下的用来写文字的输入框"> <input type="submit" value="get method"> <!-- 代表form表单提交用了一个按钮,以及按钮中的内容是get method --> </form> <form action="HttpTest" method="post"> <input type="text" name=post_way> <input type="submit" value="post method"> <!-- 代表form表单提交用了一个按钮,以及按钮中的内容是post method --> </form> </body>
- Notes:
- 标签中的action中的内容和类名web.xml中的servlet-name\servlet-url等要保持一致,其实action的值必须与servlet-name一样,但一般我们此时会吧url-pattern也和他俩保持一样
- 标签下的中的name的值与类中的request.getParameter(“post方式下的用来写文字的”)的参数保持一致
- 步骤二:在第一个register.jsp中不是定义了一个form表单用于提交用户名和密码嘛,那随着form的执行,下一步就是编写你form中action跳转到的下一个页面进行逻辑处理或者说传到后台代码中进行逻辑处理。也就是register.jsp页面处理完了后咱们让他跳转到registerDeal.jsp页面中进行数据库连接(注册驱动、获取连接,写JDBC代码处理增删改查逻辑)。
- (此时这里一般有两种处理思路,一种是,也是常用的,就是传到后台中接收进行处理,这是咱们后端经常做的;另一种处理思路就是这个小项目中的,不太常用的,在下一个.jsp页面中进行接收处理)
-
如下两种处理方式:
- 1.可以像下面这个,直接由前台页面传到后台,在后台的dogGet()或者doPost中进行接收(其实主要的接收就request.XXX());
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /*response.getWriter().append("Served at: ").append(request.getContextPath());*/ PrintWriter writer = response.getWriter();//用一个打印流,这种打印流专门用于来给页面上打印出内容,你用其他输出流还得考虑出错,还得用try catch来抓异常等等,麻烦,这个打印流干净利索 /** * 前端不是写了一个form表单提交吗,就是弄出来了两个按钮,当点了那个get method的按钮后,网页上会弹出这一句咱们编写的代码里面的话:"this is get method. HTTPTestDemoVersion02="+ request.getParameter("HTTPTestDemoVersion02");request.getParameter("HTTPTestDemoVersion02")这一部分会被替换为你那个按钮对应的输入框里面输入的内容 */ writer.println("this is get method. get方式下的用来写文字的输入框=" + request.getParameter("get方式下的用来写文字的输入框")); System.out.println(writer); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //doGet(request, response); PrintWriter writer = response.getWriter(); writer.println("this is post method. post_way=" + request.getParameter("post_way")); }
- 2.也可以在另一个前端页面中用JSP代码接收,如下(其实主要的接收就request.XXX(),下面这个例子是用JSP语法来模拟JDBC做增删改查):
/*用JSP语法写了一个JDBC代码,用于给数据库中的某些列(request.getParameter("某些列名");)并插入某些值(代码如下三行: *String sql = "INSERT INTO user VALUES(null, ?, ?)"; *PreparedStatement preparedStatement = conn.prepareStatement(sql); *preparedStatement.setString(1, username); */ <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName(); String username = request.getParameter("username"); String password = request.getParameter("password"); String password2 = request.getParameter("password2"); //JDBC的官话嘛,直接背,注册数据库驱动并获取到连接 Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ocdb", "root", "root"); //用于给数据库中的某些列(request.getParameter("某些列名");)并插入某些值 String sql = "INSERT INTO user VALUES(null, ?, ?)"; PreparedStatement preparedStatement = conn.prepareStatement(sql);获取执行SQL语句的对象 preparedStatement.setString(1, username); preparedStatement.setString(2, password); preparedStatement.executeUpdate();//执行SQL语句 preparedStatement.close();//释放资源 conn.close();//释放资源 %>
- Notes:对于这种登陆类型的功能来说,经常要对程序中的常用几个参数进行甄别并做出响应。常见的有:(前端登陆框)非空验证、登陆用户名以及密码唯一验证(后台直到DB中不能重复)、登陆成功登陆失败会有什么样的提示吗?如下代码逻辑:
//先从数据库拿到用户名,代码如下: String sqlQuery = "SELECT COUNT(*) FROM user WHERE username = ?";//这样一写只要数据库的username有值,他就会拿出来一个 PreparedStatement preparedStatementQuery = conn.prepareStatement(sqlQuery); preparedStatementQuery.setString(1, username); ResultSet resultSet = preparedStatementQuery.executeQuery(); resultSet.next(); int count = resultSet.getInt(1);//拿到第一个字段的值,对于咱们在这写的user表,第一个字段就是username /** *上面count代表拿到的username的个数,进行验证“后台唯一 逻辑” */ if(count > 0){//count > 0代表username这个字段已经有值了 response.sendRedirect("registerFail.jsp"); preparedStatementQuery.close(); conn.close(); return;//当前页面执行到此处结束 } String sql = "INSERT INTO user VALUES(null, ?, ?)"; PreparedStatement preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, username); preparedStatement.setString(2, password); preparedStatement.executeUpdate(); preparedStatement.close(); conn.close(); /** *上面的if不是已经判断过了吗,count>0时说明这个用户名已经被人用过了就不能再用了,所以跳转到registeFail.jsp *现在当count=0时说明没人用过这个用户名,那就执行注册功能,把这个新的用户名给插入到数据库中,并返回registerSuccess.jsp页面 */ response.sendRedirect("registerSuccess.jsp");
- Notes:前台显示页面以及后台处理逻辑写好以后,可以在tomcat中跑项目了。注意以下几点:
- 四个前台.jsp文件一定要放在WebContent(有时候叫webapps)目录下,而不是他的两个子目录下,否则项目跑不出来。
- 其次,访问路径应该是完整的:http://localhost:8080/SSM_Registeration_Version01/register.jsp
- 记得将mysql-connector-java-5.1.28.jar驱动包放在tomcat的lib下
-
- (此时这里一般有两种处理思路,一种是,也是常用的,就是传到后台中接收进行处理,这是咱们后端经常做的;另一种处理思路就是这个小项目中的,不太常用的,在下一个.jsp页面中进行接收处理)
- Version01制作思路的缺点:用的是两层架构(现在最少都是三层了),现在都没人用这种两层架构了。
- 步骤一:新建web项目,先在webapps(有时候也叫WebContent)下的WEB-INF下新建所用到的前端.jsp文件,如下四个.jsp文件。当然,首先要进行编写的是register.jsp页面。
-
Version02制作思路:咱们这个登陆小项目版本二的整体思路是与版本一大体相同,只不过增加了一层entity,降低了一点耦合性,因为你面向对象的思维呀,你不得搞几个持久化对象实体出来,常用的就model\po\entity。然后将request.getParameter(“username”);这种语句的返回值给放set进实体的属性中,其他地用时直接调实体的get方法即可。但是呀,你这不也没咋简化代码嘛,只不过是倒了一下手而已。所以version02也得再进化。
-
Notes:
- 这里强调几个概念,(entity包或者model包或者po(persistentobject)包,都是一个意思)。以后进公司干业务五花八门,你要习惯不同的叫法。持久化对象po(persistentobject)和值对象vo(valueobject)不是一样的。
-
Version03制作思路:咱们这个登陆小项目版本三的整体思路是与之前版本大体相同,只不过增加了一层service,用来管理entity等逻辑。代码主要还是WebContent下面的四个jsp文件:register.jsp\registerDeal.jsp\registerFail.jsp\registerSuccess.jsp;以及src下的entity和service。
-
经过优化后的主要代码如下:可以与之前两个版本比较一下,看一下架构或者集成方面的不同。
-
register.jsp没有发生变化。依旧是一个form表单提交,将用户名、密码、确认密码向下一层递交。,action中写的就是递交的下一层是哪个。
-
registerDeal.jsp代码如下:
<%@ page language="java" import="java.util.*, java.sql.*" contentType="text/html; charset=UTF-8" pageEncoding="GB18030"%> <%@ page import="com.aiminminai.registeration.service.UserManagerService" %> <%@ page import="com.aiminminai.registeration.model.User" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName(); String username = request.getParameter("username"); String password = request.getParameter("password"); String password2 = request.getParameter("password2"); User user = new User(); user.setUsername(username); user.setPassword(password); Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ocdb", "root", "root"); UserManagerService userManagerService = new UserManagerService(); boolean exist = userManagerService.exists(user); if(exist){ response.sendRedirect("registerFail.jsp"); return; }else{ response.sendRedirect("registerSuccess.jsp"); } userManagerService.add(user); response.sendRedirect("registerSuccess.jsp"); %>
-
registerFail.jsp与registerSuccess.jsp的代码没有什么变化。
-
model包下的User就是一个实体,里面包含着与数据库对应的属性以及属性的set、get方法。
-
service包下的代码如下:
/** * Copyright (c) 2013-Now http://AIminminAI.com All rights reserved. */ package com.aiminminai.registeration.service; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import com.aiminminai.registeration.model.User; /** * * @author HuHongBo * @version 2021年11月1日 */ public class UserManagerService { public boolean exists(User user) throws Exception{ Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ocdb", "root", "root"); //先从数据库拿到用户名,代码如下: String sqlQuery = "SELECT COUNT(*) FROM user WHERE username = ?";//这样一写只要数据库的username有值,他就会拿出来一个 PreparedStatement preparedStatementQuery = conn.prepareStatement(sqlQuery); preparedStatementQuery.setString(1, user.getUsername()); ResultSet resultSet = preparedStatementQuery.executeQuery(); resultSet.next(); int count = resultSet.getInt(1);//拿到第一个字段的值,对于咱们在这写的user表,第一个字段就是username preparedStatementQuery.close(); conn.close(); /** *上面count代表拿到的username的个数,进行验证“后台唯一 逻辑” */ if(count > 0){//count > 0代表username这个字段已经有值了 return true;//当前页面执行到此处结束 } return false; } public void add(User user) throws Exception{ Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ocdb", "root", "root"); String sql = "INSERT INTO user VALUES(null, ?, ?)"; PreparedStatement preparedStatement = conn.prepareStatement(sql); preparedStatement.setString(1, user.getUsername()); preparedStatement.setString(2, user.getPassword()); preparedStatement.executeUpdate(); preparedStatement.close(); conn.close(); } }
-
回过头来看,代码是有问题的,Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/ocdb”, “root”, “root”);这句代码出现一次就说明开启了一个transaction,add(User user)和exists(User user)都出现了一次说明咱们用的不是一个事物,容易出现操作的非原子性。那有什么解决办法呢?办法就是我可以直接在add(User user)中进行原exists()方法原来的逻辑,也就是在add()方法中检查user存在不存在,可以自己定义一个UserAlreadyExistsException,然后在registerDeal.jsp中try…catch。User不存在的时候catch这个UserExistsException然后response.sendRedirect(“registerFail.jsp”);
- Notes:UserManagerService中的exists()方法和add()为什么不能直接写在User实体中呢,非要另写一个UserManagerService类然后在里面写呢?
- 原因:把add(User user)方法写在User类中的话。你想呀,当我要add(User)时,我是不是得先new一个user出来才能去存他,这就说不过去了,你做开发的管的屁事有点多,我就是让你存下user,没让你把我这个user实例化呀。但是exists()放在User类中是可以的。但是自己存自己也是可以的。自己添加自己也是可以用的,别把只是学死了,我还可以把方法改成静态的呀,是不是先实例化自己在存再添加就不是问题了呢。
- 这也就涉及到了对实体类的封装有两种模型,分别是贫血模型和充血模型。太多了。贫血模型就指的是你实体类中除了属性就是属性对应的get和set方法。充血模型就指的是实体类里面的逻辑,除了属性以及属性对应的get和set方法,还有其他很多的逻辑,比如咱们这个User实体类,代码如下:
public class User{ //成员变量...... //成员变量对应的set以及get方法...... public boolean exists(){ return new UserManagerService.exists(this); } }
- Notes:UserManagerService中的exists()方法和add()为什么不能直接写在User实体中呢,非要另写一个UserManagerService类然后在里面写呢?
-
-
- Version04制作思路:现在回过头来看,是不是每次都把对数据库的操作写道业务逻辑层中特不符合面向对象的思想呀…这就不引出了Mybatis了嘛,映射文件和核心配置文件玩起来(集成在Spring上面)。
- 现在的架构就是:UserManagerService通过Mybatis对DB实现CRUD,不够完善,升级版在下面。
- 现在的架构就是:UserManagerService通过Mybatis对DB实现CRUD,不够完善,升级版在下面。
- Version05制作思路:在版本04的基础上引入springMVC
-
Notes:SpringMVC是一个怎样的框架?
MVC三个角度的框架,SpringMVC主要解决的是其中的Controller
;View主要是我们自己实现的(JSP呀,FreeMarker呀,静态页面等),SpringMVC只是帮我们给这个View起了个名字
。 -
Notes:插播一个重点。咱们要想让一个WebApplication一启动就帮咱们加载各个配置文件并自动的把配置文件中的bean都给初始化了(配置文件中因为配置了诸如,组件扫描等,所以大部分的bean都在配置文件中包含着呢,不用担心漏谁),咱们就要在web.xml中加如下的配置信息。
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
这个监听器会监听Application的启动,你看监听器的名字叫ContextLoaderListener,其中的Context,就指得是我们的WebApplicationContext,也就是我们的项目名。相当于这一步完成了容器初始化。初始化的时候一般spring会去默认的位置找:/WEB-INF/applicationContext.xml,但是咱们要是非要把spring的配置文件放到resources目录下面让spring去找去初始化。那么咱们就要在web.xml中加如下的配置信息。
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
-
Notes:插播一个重点。DTO(Data Transfer Object)=VO(Value Object,用到他时让他临时帮我们保存一些值),这俩个都是用来装一些和数据库没有关系的数据。
-
-
巨人的肩膀:
lianglianglee老师的专栏:深挖 MyBatis 与 Spring 集成底层原理
B站各位老师