前言
因为课程需要我们做一个项目,没有要求是什么程序,我们小组暂定商议为一个安卓程序,又鉴于我没有学过安卓开发,又感受到后台开发的内容自己基本没有接触到过,所以自告奋勇来写后端的内容,这个教程主要参考了b站上的某个视频,发布视频的大佬关于整个项目的代码github在此
引入
- 关于后台开发,我自己的理解是,对于某个APP来说,在服务器一侧处理客户端发来的信息和数据并返回客户端想要的内容的过程。
- 对于后台开发,不可避免的应该有计算机网络、数据库相关的计算机基础知识,比如说客户端与服务器之间的连接,是用HTTP协议还是tcp或udp协议呢?使用协议传输的同时,自己也要与前端商议好某些状态码,以便后面的对接。
- 用到数据库更是不可避免的,只要是APP就肯定会产生数据,而这些数据除了某些缓存之外,其他的都应该存放在服务器一端,等到客户端需要时,可以从服务器拿取。
准备
- 首先我们要明确我们的产品需求,在这里用作示例的是一个二手书转卖APP,教程仅仅给出的是关于注册和登录的部分,其余部分大家自己再摸索。
- 这个项目中,我们选择的就是HTTP协议,为了可以便于测试HTTP请求返回的内容,建议大家使用Chrome的一个插件——postman,Chrome插件商店好像是要翻墙,不过网上也到处都有postman的压缩包下载。
- 使用的语言为JavaEE,这个是目前比较主流的服务端使用语言,在写这篇博客的时候,我也还没有仔细学过Java,但是相信如果仅仅只是学到能用的地步也不会太难
- 服务端的数据库选择,现在很流行NoSql,但是相信大部分在校学生都没有使用过,包括我自己,所以我们这个项目首先使用MySQL入门,当然为了便捷使用,我们不要用命令行版的MySQL,自己去下载一个可视化工具,我用的是SQLyog,也是因为之前看过一个视频,他用的就是这个,当然选择还有很多,自己搜搜
- 服务器环境搭建——首先是JDK和Tomcat的下载安装,注意,这两个的版本要一致,(比如你下载的是JDK7那么Tomcat也要下Tomcat7,否则可能玄学)
- 项目框架选择,有SSH一说即spring,Struts,hibernate三大框架,教学视频内说spring比较适合大的项目,对于小项目来说有点臃肿,所以我们这里选择Struts2和hibernate
- Struts2的作用是什么呢?一是路由,当我们输入某个HTTP请求后,要路由到某个类中的方法;二是取代servlet;三他是MVC模式中的controller
- hibernate的作用是什么呢?一是ORM(object - relation - mapping)对象关系映射,这是很重要的一个点,我们在建好数据库后,使用hibernate的反向工程可以直接生成类和类中某些属性的方法;二是数据持久化(不是太明白);三是取代jdbc(增删改查);四是MVC模式中的model
- 关于代码编辑器视频中推荐的是MyEclipse,这个是Eclipse的改进版,是要收费的,直接下载有5天的免费体验时间,大家可以自行体验一下,有很多地方可以帮助你更省心的完成。
至此我们的前期思想准备工作已经完成了,总结一下,到这步,我们应该完成的有:
- MyEclipse的下载安装
- MySQL和它的可视化工具的下载安装
- JDK 和 Tomcat 的下载安装(注意大版本号相同)
- Chrome插件postman的下载安装
开发环境搭建
分析需求
根据客户端的需求,我们应撰写需求报告
在这个示范中,我们客户端的需求仅仅是用户注册和登录功能,需求主要是:
1.用户注册要求,分配给新用户一个独一无二的id,用户自己填写的账户名要求不能重复,密码要求6位以上,用户注册的手机号为11位
2.用户登录,要求账号密码正确,即返回登录成功的状态码
设计建立数据库
根据上面的分析,显然我们只需要一张表记录下所有的客户的注册信息即可,首先我们建立一个名为bookseller的数据库,并建一张名为USER的表,里面的字段有:
- user_id int primary key auto_increment
- user_account varchar(45)
- user_password varchar(45)
- user_phone varchar(15)
注意我在建表的时候,属性字段用了 “ 表名_属性 ” 的写法,一定要遵循这个规则,有利于后面hibernate反向工程生成规范的类名。
新建项目
打开MyEclipse,在项目栏右键new一个other,自己输入dy,选择dynamic web project,可能有时输入dy后发现没有这个选项,那么√一下下面的show all wizards就有了。
然后next,给自己的项目取个名字,我们这里叫bookseller,target runtime记得选到Tomcat
然后next next ,记得要勾选Generate web.xml deployment descriptor选项,然后finish就行了
到这里就新建了一个项目了。
初始化项目结构
项目结构一般分为四个包:
- action包:放置Struts接口类
- dao包:data access object ,数据访问接口,用来对entity(数据库)进行增删改查
- entity包:实体类包,放hibernate反向工程生成的实体类
- util包:工具类包,放一些json工具类或者hibernate工厂类
如何创建包呢?
在src文件夹右击new–>package,注意,package的name一般具有固定格式,即action的name是com.bookseller.action 如果学过计网这是容易理解的,代表的是服务器的一层层往下。
其他三个包也是类似的名称。
安装Struts2
右键你刚建好的项目,移到configure facets,然后会有很多框架可以装,我们选到Struts2,
next,然后URL pattern,我们可以选*.action或者 */,而.do一般是servlet使用的,这是代表我们路由操作的一种正则格式匹配,在这里我们就选action作为示范;
再选next,默认核心包即可,点击finish完成。
这时候出现了一个Struts.xml 这是配置路由的文件
连接数据库
点击右上角的标志
选择database explorer;
进入页面后点击右键new,driver template选择MySQL,name自己取一个;
connection URL 的hostname是localhost,端口继续3306,dbname为我们上面创建的数据库bookseller,然后输入我们数据库的账号密码;
注意,下面的地方要求我们加入一个jar包,如果用MySQL是叫mysql-connector-java.jar,自己百度去下载(这里遇到一个坑,有些jar包加入后test发现连接不上,如果确认其他地方无误的话建议重下一个jar包使得driver class name 是 ‘com.mysql.jdbc.driver’)
然后test成功后,点击finish完成。
安装hibernate
同样的方法,在configure facets里面找到install hibernate:
1.configuration folder放在src即可,configuration file name 直接放根目录下(即保持默认位置不用改动),sessionFactory class防在util package下,next
2. DBdriver选上面刚建好的数据库连接,next
3. finish就行了,问你是否前往hibernate视图,可以暂时不用,选no
4. 这时候我们打开hibernate.cf.xml,查看source源码
5. 注意一下,connection URL需要更改,防止中文乱码(xml文件中&的表示是&)
新建实体类
- 在database explorer界面找到我们的user表,右击有一个选项“ hibernate reverse engineering ”,即前面一直说的反向工程;
- Java package选到entity包,下面的‘creat POJO’和‘create data object’都要勾选,但是我们不选生成abstract类的选项,next
- ID generator选择native ,下面的四个选项都勾上,next
- 勾选A->B 和 B->A ,finish
- 这时去看entity包,就建好了实体类user和一些属性方法,xml是映射连接文件,可以打开看看如何映射的
我们去百度搜下载一个gson.jar包,拷贝放入项目的webroot -> web-inf -> lib下,会自动配置到环境中,这个包是用于处理json文件。
到这里,其实我们的项目已经可以运行了,右击webroot下的index.jsp文件,run as MyEclipse server application,然后跑完后弹出页面,显示“this is my JSP page”,代表我们这个项目配置成功了,已经可以实现最基本的访问。
开发过程
开发顺序
设计dao包;
实现action类;
配置路由;
我们现在回去看开发结构中4个基本的包:
- action包:放置Struts接口类
- dao包:data access object ,数据访问接口,用来对entity(数据库)进行增删改查
- entity包:实体类包,放hibernate反向工程生成的实体类
- util包:工具类包,放一些json工具类或者hibernate工厂类
发现我们的entity包里面的对象已经有了,util包里面也暂时有了hibernate工厂类,而action和dao包还没有内容,所以从这里入手。
Dao层
一般根据entity来进行设计,每个entity对应一个dao,dao其实就是对entity对象进行增删改查的方法。
一般的设计规范是先设计一个xxxDao的interface,再用一个xxxDaoImpl的类来实现接口功能。
一般关于增删改查的操作都要用到hibernate工厂类中的session对象去操作,其中增删改三个操作要用到hibernate中的事务(Transaction对象)。
下面我们进行示范,针对user的实体对象,我们先在dao包内new一个名为userDao的interface(new–>other–>interface),因为我们只有登录和注册的功能,所以声明两个基本方法方法login和saveUser用来登录和注册,还声明了一个根据用户ID获得用户类数据的函数和一个根据用户account获得ID的函数:
package com.BookSeller.dao;
import com.BookSeller.entity.User;
public interface UserDao {
public User getUser(int userId);
public int getUserIdByUserAccount(String userAccount);
public boolean login(String userAccount,String userPassword);
public void saveUser(User user);
}
完成后我们再新建一个实体类userDaoImpl类,在new的时候要记得add interface“userDao”,用来实现interface中的函数:
package com.BookSeller.dao;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.BookSeller.entity.User;
import com.BookSeller.util.HibernateSessionFactory;
public class UserDaoImpl implements UserDao {
private Transaction transaction;
@Override
public boolean login(String userAccount, String userPassword) {
if(userAccount != null && userPassword != null)
{
Session session = HibernateSessionFactory.getSession();
String hql = "from User as a where a.userAccount= '"+ userAccount +"' and a.userPassword= '"+ userPassword +"' ";
Query query = (Query)session.createQuery(hql);
User user = (User)query.uniqueResult();
if(user != null)
{
HibernateSessionFactory.closeSession();
return true;
}
HibernateSessionFactory.closeSession();
}
return false;
}
@Override
public void saveUser(User user) {
// TODO Auto-generated method stub
Session session = HibernateSessionFactory.getSession();
try{
transaction = session.beginTransaction();
session.save(user);
transaction.commit();
}catch(Exception e){
transaction.rollback();
e.printStackTrace();
}finally{
HibernateSessionFactory.closeSession();
}
}
@Override
public User getUser(int userId) {
Session session = HibernateSessionFactory.getSession();
User user = (User)session.load(User.class, userId);
return user;
}
@Override
public int getUserIdByUserAccount(String userAccount) {
Session session = HibernateSessionFactory.getSession();
String hql = " from User as a where a.userAccount= '"+userAccount+"' ";
Query query = (Query)session.createQuery(hql);
User user = (User)query.uniqueResult();
if(user != null){
HibernateSessionFactory.closeSession();
return user.getUserId();
}
HibernateSessionFactory.closeSession();
return user.getUserId();
}
}
实现action类
使用Javabean来接收参数;
必须实现返回值为string的execute()方法;
execute方法实现
其实方法比较固定,有5行代码是必须有的:
ActionContext ctx = ActionContext.getContext();
HttpServletResponse response = (HttpServletResponse)ctx.get(ServletActionContext.HTTP_RESPONSE);
response.setContentType("text/json");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
这五行代码是把我们的response变成json文件的一个固定套路,首先拿到上下文对象,然后拿到response,然后设置一下response的属性,最后把response打印出来。
下面我们开始针对注册操作实现action类,首先考虑到我们需要用到jsonResponse的状态码回应,首先我们在util包里,新建一个JavaResponse的类,代码如下:
package com.BookSeller.util;
public class JsonResponse {
private int status;
private int userId;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}
然后我们开始实现注册的action类,在action包里面新建class名为RegisterAction,
实现代码为:
package com.BookSeller.action;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.BookSeller.dao.UserDao;
import com.BookSeller.dao.UserDaoImpl;
import com.BookSeller.entity.User;
import com.BookSeller.util.JsonResponse;
import com.google.gson.Gson;
import com.opensymphony.xwork2.ActionContext;
public class RegisterAction {
private JsonResponse jsonResponse = new JsonResponse();
private String userAccount;
private String userPassword;
private String userPhone;
public String execute() throws IOException{
Gson gson = new Gson();
ActionContext ctx = ActionContext.getContext();
HttpServletResponse response = (HttpServletResponse)ctx.get(ServletActionContext.HTTP_RESPONSE);
response.setContentType("text/json");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
UserDao dao = new UserDaoImpl();
User user = new User();
user.setUserAccount(userAccount);
user.setUserPassword(userPassword);
user.setUserPhone(userPhone);
dao.saveUser(user);
jsonResponse.setStatus(200);
out.print(gson.toJson(jsonResponse));
return null;
}
public String getUserAccount() {
return userAccount;
}
public void setUserAccount(String userAccount) {
this.userAccount = userAccount;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
}
到这里我们就已经完成了注册行为的action类,关于登录的action代码在此不做赘述,有代码需求的同学可以去看看博客最开始的github仓库,那么我们怎么样能够让一个HTTP请求调用到这个action类呢?
那就需要我们下面的工作了。
配置路由
打开Struts.xml文件,这个路由文件的作用主要是把一个HTTP请求路由到一个具体的action类,写法也比较固定,先看示例
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="default" extends="struts-default">
<action name="register" class="com.BookSeller.action.RegisterAction"></action>
<action name="login" class="com.BookSeller.action.LoginAction"></action>
</package>
</struts>
上面的package标签是固定的写法,后面的action标签大家应该一看就懂了,当我们发出服务器IP+register.action?userAccount=xxx&userPassword=xxx&userPhone=xxx的HTTP请求时,Struts配置就会路由调用这个对应的action类。
至此,我们的基本工作就完成了,可以来用上面的HTTP请求测试一下了。
测试
首先提示大家一下,修改了代码之后,最好可以重启一下Tomcat,否则有可能Tomcat上部署的仍是之前的内容。
重启后,我们右击index.jsp,选择run as myeclipse server application,在web browser导航栏输入自己的请求:
可以看到返回了200,也就是我们前面设定的注册成功的状态码,我们打开数据库GUI工具查看之前创建的user表,发现,有一个新用户添加进来了:(我们注册的时候并没有返回userID,所以默认是0,并不是返回错误了)
神奇!
部署
对于一个可以使用了的项目我们部署的方法有很多,比如:
1.租用阿里云,华为云等服务器,把自己的项目传到服务器上去,就可以随时随地访问;
2.用自己的电脑做服务器(不是特别推荐,因为需要将电脑24小时开机才可实现随时访问);
3.使用docker;
我在写这篇博客的时候自己也还没有完成这个项目,暂时没有到部署的那一步,所以在此不做详细展开了,后面如果觉得部署需要整理一下,会再专门写一篇博客的。
万字长文不易,感谢您的阅读!