2.1.开发环境检查
- 开发工具:SpringToolSuite(STS)、MySql6.0、MySQL Workbench
- 检查STS的jdk配置:jdk8
- 检查STS的tomcat配置:tomcat8.5
-
2.2.搭建javaWeb工程总体架构
2.2.1.工程类型
2.2.2.导入jar包
mysql-connector-java-bin.jar
- jackson-core.jar
- jackson-annotations.jar
-
2.2.3.工程目录结构
2.2.4.MVC架构解决方案
本工程采用:基于Servlet的简易MVC架构。
- 本工程采用:约定优于配置的原则来搭建简易MVC框架。
- 本工程中规定:
请求url与Controller方法映射示例: http://localhost:8080/elm/Controller类名/Controller方法名 - 本工程中不需要任何配置文件。
附录:DispatcherServlet代码:
package com.neusoft.elm.framework;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
/**
* 自定义前端控制器 拦截url格式要求: /控制器类名/控制器方法名
*/
@WebServlet("/")
public class DispatcherServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//中文编码处理
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
//获取客户端请求路径(/HelloController/say)
String path = request.getServletPath();
//根据请求路径,将Controller的类名和方法名解析出来
String className = path.substring(1,path.lastIndexOf("/"));
String methodName = path.substring(path.lastIndexOf("/")+1);
PrintWriter out = null;
//判断请求路径,根据不同的请求,分发给不同的业务处理器
try{
//通过Controller类全名获取此类的所有信息
Class clazz = Class.forName("com.neusoft.elm.controller."+className);
//创建Controller类的对象
Object controller = clazz.newInstance();
//获取Controller类对象中的方法
Method method = clazz.getMethod(methodName,new Class[]{HttpServletRequest.class});
//调用上面获取的方法
Object result = method.invoke(controller,new Object[]{request});
//获取向客户端响应的输出流
out = response.getWriter();
ObjectMapper om = new ObjectMapper();
//向客户端响应json数据
out.print(om.writeValueAsString(result));
}catch(Exception e){
e.printStackTrace();
System.out.println("DispatcherServlet信息:请求url:"+path);
System.out.println("DispatcherServlet信息:类名:"+className+"\t方法名:"+methodName);
}finally {
out.close();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
2.2.5.跨域问题解决方案
本工程使用CORS解决前后端分离开发模式时的跨域问题。 CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
附录:CORS过滤器代码:
package com.neusoft.elm.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
@WebFilter("/*")
public class CorsFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
//注意:这里设置只允许http://localhost:8081进行跨域访问
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3628800");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() { }
}
2.2.6.事务处理解决方案
首先要明确一点:事务处理放在service层。所以:
- Connection的创建与销毁要放在service层。
- 为了保证在同一次请求处理的线程中,service层和dao层都共用同一个Connection对象,需要将Connection对象放入ThreadLocal中。service层和dao层使用的Connection对象一律从ThreadLocal获取。
- dao层不再处理异常,dao层产生的异常将直接抛给service层进行处理。
- dao层负责关闭PreparedStatement和ResultSet,service层负责关闭Connection。
附录: ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
DBUtil类代码:
package com.neusoft.elm.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DBUtil {
private static final ThreadLocal<Connection> TL = new ThreadLocal<Connection>();
private static final String URL="jdbc:mysql://localhost:3306/elm?characterEncoding=utf-8";
private static final String DRIVER="com.mysql.jdbc.Driver";
private static final String USERNAME="root";
private static final String PASSWORD="123";
// 获取Connection
public static Connection getConnection() {
Connection con = null;
con = TL.get();
if (con==null) {
con = createConnection();
TL.set(con);
}
return con;
}
// 开启一个事务
public static void beginTransaction() throws Exception {
Connection con = null;
con = TL.get();
if (con == null) {
con = createConnection();
TL.set(con);
}
con.setAutoCommit(false);
}
// 提交一个事务
public static void commitTransaction() throws Exception {
Connection con = TL.get();
con.commit();
}
// 回滚一个事务
public static void rollbackTransaction() throws Exception {
Connection con = TL.get();
con.rollback();
}
// 关闭各种资源
public static void close(ResultSet rs, PreparedStatement pst) {
try {
if (rs != null) {
rs.close();
}
if (pst != null) {
pst.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭各种资源
public static void close(PreparedStatement pst) {
try {
if (pst != null) {
pst.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 关闭各种资源
public static void close() {
Connection con = TL.get();
try {
if (con != null) {
con.close();
}
//至关重要,否则容易造成内存溢出等问题。
TL.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
private static Connection createConnection() {
Connection con = null;
if (con == null) {
try {
Class.forName(DRIVER);
con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
}
return con;
}
}
dao层代码示例:
@Override
//必须要有: throws Exception
public int saveCart(Cart cart) throws Exception{
int result = 0;
String sql = "insert into cart values(null,?,?,?,1)";
try {
//Connection从ThreadLocal中获取
con = DBUtil.getConnection();
pst = con.prepareStatement(sql);
pst.setInt(1, cart.getFoodId());
pst.setInt(2, cart.getBusinessId());
pst.setString(3, cart.getUserId());
result = pst.executeUpdate();
}finally { //这里不能处理异常,也就是没有catch,只有finally
DBUtil.close(pst); //这里负责关闭PreparedStatement和ResultSet
}
return result;
}
service层代码示例:
@Override
public int createOrders(String userId,Integer businessId,Integer daId) {
try {
DBUtil.beginTransaction(); //开启一个事务
//调用多个Dao进行数据处理
//... ...
//... ...
DBUtil.commitTransaction(); //提交一个事务
} catch (Exception e) {
try {
DBUtil.rollbackTransaction(); //回滚一个事务
} catch (Exception e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
DBUtil.close(); // 关闭Connection
}
return orderId;
}
2.2.7.Servlet返回JSON解决方案
Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。 Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC 的默认 json 解析器便是 Jackson。
本项目中,Servlet中使用Jackson将java对象或集合转换为json对象或数组后,返回前端。
ObjectMapper om = new ObjectMapper();
out.print(om.writeValueAsString(result)); //result就是响应数据
2.2.8.AJAX解决方案
由于本项目前端使用Vue框架,所有AJAX采用axios框架(这是尤雨溪推荐使用的)。
this.$axios.post('DeliveryAddressController/saveDeliveryAddress', this.$qs.stringify(
this.deliveryAddress //josn对象类型的提交参数
)).then(response => {
//做响应处理
}).catch(error => {
console.error(error);
});
2.2.9.图片解决方案
对于购物系统来说,商家信息、食品信息中必须要使用大量的图片。
在本工程中,由于使用的图片都比较小,所以采用Base64编码方式来存储图片信息。
Base64是一种用64个字符来表示任意二进制数据的方法。 我们知道,图片就是二进制数据。而Base64就可以将图片的二进制数据转换成字符串形式。这样能让我们方便的在数据库中,对图片进行存储和读取。
<img src="data:image/jpeg;base64,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">
[