为了巩固之前所学Java Web基础,特做一个还算是比较完整的小项目——在线网上书店。
在线网上书店这个项目包含了两个部分,前台和后台。前台用于向用户显示商品(这里是书籍)信息,如下图所示。
后台由管理员进行管理,后台分为分类管理模块、图书管理模块、订单管理模块以及数据库管理模块等4个模块,如下图所示。
在Eclipse中新创建一个mybookstore的项目,导入项目所需要的开发包(jar包),创建项目所需要的包,在Java Web开发中,架构的层次是以包的形式体现出来的。
以上就是根据此项目的实际情况创建的包,可能还需要创建其他的包,这个得根据项目的需求来定了。
在WebRoot根目录下新建一个manager.jsp页面,这个页面代表后台首页,而且该页面是一个分帧页面,分帧的代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>在线网上书店后台管理页面title>
head>
<frameset rows="18%,*">
<frame src="${pageContext.request.contextPath }/manager/head.jsp" name="head" />
<frameset cols="15%,*">
<frame src="${pageContext.request.contextPath }/manager/left.jsp" name="left" />
<frame src="" name="right" />
frameset>
frameset>
html>
我们还要在WebRoot根目录下新建一个manager目录,用于保存后台相关的一系列jsp页面。由于后台首页包含有head.jsp和left.jsp这两个页面,所以,还要在manager目录中新建出这两个页面。
head.jsp页面的内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>在线网上书店后台管理页面的页头部分title>
head>
<body style="text-align: center;">
<h1>在线网上书店后台管理h1>
body>
html>
left.jsp页面的内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<title>后台左侧导航页面title>
<style type="text/css">
.dc {
display: none;
margin-left: 10px;
}
style>
<script language="javascript">
function test(e) {
var div = document.getElementById(e);
div.style.display = div.style.display == 'block' ? 'none' : 'block' ;
}
script>
head>
<body>
<ul>
<li>
<a href="#" onclick="test('div1')">分类管理
<div class="dc" id="div1">
<a href="#" target="right">添加分类a><br/>
<a href="#" target="right">查看分类a><br/>
div>
a>
li>
<br/><br/>
<li>
<a href="#" onclick="test('div2')">图书管理
<div class="dc" id="div2">
<a href="#" target="right">添加图书a><br/>
<a href="#" target="right">查看图书a>
div>
a>
li>
<br/><br/>
<li>
<a href="#" onclick="test('div3')">订单管理
<div class="dc" id="div3">
<a href="#" target="right">待处理订单a><br/>
<a href="#" target="right">已发货订单a><br/>
div>
a>
li>
<br/><br/>
<li>
<a href="#" onclick="test('div4')">数据库管理
<div class="dc" id="div4">
<a href="#" target="right">数据库备份a><br/>
<a href="#" target="right">数据库恢复a><br/>
div>
a>
li>
ul>
body>
html>
在WebRoot根目录下新建一个client目录,用于保存前台相关的一系列jsp页面。紧接着在client目录中创建前台首页——index.jsp。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>前台首页title>
head>
<body style="text-align: center;">
<div id="container">
<div id="head">
<%@include file="/client/head.jsp" %>
div>
<div id="main">
......
div>
div>
body>
html>
由于在前台首页中静态引入了head.jsp页面,所以,还要在client目录中新建出这个页面。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<h1>网上书店h1>
<br/>
<br/>
<hr>
这样,前台分帧页面的显示效果如下:
先把前台和后台分帧的页面架子给搭起来,后面在此基础上进行修修改改!
create database bookstore;
use bookstore;
在cn.liayun.utils包中创建一个WebUtils工具类,该工具类的功能就是封装客户端提交的表单数据到一个JavaBean中。
package cn.liayun.utils;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.beanutils.BeanUtils;
//主要把请求数据封装到一个JavaBean中
public class WebUtils {
public static <T> T request2Bean(HttpServletRequest request, Class<T> beanClass) {
try {
T bean = beanClass.newInstance();
Map<String, String[]> map = request.getParameterMap();
BeanUtils.populate(bean, map);
return bean;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
为了业务逻辑层和数据访问层之间的解耦,我们还应使用工厂设计模式来编写一下DaoFactory类来实现。于是,新建一个cn.liayun.factory包,在包中创建一个Dao工厂,即DaoFactory类,该类一般要设计成单例的。
package cn.liayun.factory;
import java.io.InputStream;
import java.util.Properties;
public class DaoFactory {
private static Properties prop = new Properties();
private DaoFactory() {
try {
InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("cn/liayun/factory/dao.properties");
prop.load(in);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static final DaoFactory instance = new DaoFactory();
public static DaoFactory getInstance() {
return instance;
}
public <T> T createDao(Class<T> interfaceClass) {
try {
String key = interfaceClass.getSimpleName();
String className = prop.getProperty(key);
return (T) Class.forName(className).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
紧接着,在cn.liayun.factory包下创建一个dao.properties文件,该配置文件的内容如下:
CategoryDao=cn.liayun.dao.impl.CategoryDaoImpl
BookDao=cn.liayun.dao.impl.BookDaoImpl
UserDao=cn.liayun.dao.impl.UserDaoImpl
OrderDao=cn.liayun.dao.impl.OrderDaoImpl
DbBackDao=cn.liayun.dao.impl.DbBackDaoImpl
接下来,我们要编写一个用于处理全站中文乱码的过滤器CharacterEncodingFilter。
package cn.liayun.web.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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
紧接着,就要在web.xml文件中配置CharacterEncodingFilter过滤器了。
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>cn.liayun.web.filter.CharacterEncodingFilterfilter-class>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
我们还要编写一个html转义过滤器(HtmlFilter),代码如下:
package cn.liayun.web.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.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
public class HtmlFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//request.getParameter("");
chain.doFilter(new MyRequest(request), response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
return filter(value);
}
public String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
}
紧接着,就要在web.xml文件中配置HtmlFilter过滤器了。
<filter>
<filter-name>HtmlFilterfilter-name>
<filter-class>cn.liayun.web.filter.HtmlFilterfilter-class>
filter>
<filter-mapping>
<filter-name>HtmlFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
在《Java Web基础入门第六十二讲 JDBC应用中的事务管理》这篇文章中,为了更加优雅地处理事务,我们使用了ThreadLocal类改造过数据库连接工具类JdbcUtils。所以,还得在cn.liayun.utils包中创建这样一个数据库连接工具类JdbcUtils。
package cn.liayun.utils;
import java.sql.Connection;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JdbcUtils {
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
private static DataSource ds;
static {
ds = new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return ds;
}
public static Connection getConnection() {
try {
//首先得到当前线程上绑定的连接
Connection conn = tl.get();
if (conn == null) {
conn = ds.getConnection();//如果当前线程上没有绑定一个连接,则从数据库连接池拿一个连接
}
tl.set(conn);//把连接绑定到当前线程上去
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void startTransaction() {
try {
Connection conn = getConnection();
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void commitTransaction() {
try {
Connection conn = getConnection();
if (conn != null) {
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void closeConn() {
Connection conn = null;
try {
conn = getConnection();
if (conn != null) {
conn.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
/*
if (conn != null) {
tl.remove();
}
*/
tl.remove();//千万要注意,解除当前线程上绑定的连接(从ThreadLocal容器中移除掉对应当前线程上的连接)
}
}
}
由于以上工具类使用了C3P0数据源,所以要在类目录下加入C3P0的配置文件,即c3p0-config.xml,该配置文件中的内容如下:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driverproperty>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/bookstoreproperty>
<property name="user">rootproperty>
<property name="password">liayunproperty>
<property name="initialPoolSize">10property>
<property name="maxIdleTime">30property>
<property name="maxPoolSize">20property>
<property name="minPoolSize">5property>
<property name="maxStatements">200property>
default-config>
<named-config name="mysql">
<property name="acquireIncrement">50property>
<property name="initialPoolSize">100property>
<property name="minPoolSize">50property>
<property name="maxPoolSize">1000property>
<property name="maxStatements">0property>
<property name="maxStatementsPerConnection">5property>
named-config>
c3p0-config>
也是在《Java Web基础入门第六十二讲 JDBC应用中的事务管理》这篇文章中,我们漏讲了一个事务过滤器,如果使用了事务过滤器,那么一次请求范围内的所有操作都将在一个事务里面了,如下图所示。
如果事务过滤器是像下面这样编写的,那么会有什么问题呢?
读者试着思考一下,如果访问的是网站首页——index.jsp,那么该事务过滤器拦截下来之后,势必也要获取连接并开启事务,然后把连接绑定到当前线程上。如此一来,效率势必要差很多。更加合适的做法就是把获取连接、开启事务的操作延迟到第一次访问数据库时,而不是说,我一上来就帮你获取连接、开启事务,这样子做,效率上要好一点。因此,该事务过滤器要修改为:
试想,当我们在后续编写dao层的代码时,一上来势必会调用JdbcUtils类的getConnection方法获取一个连接,而且这个方法要确保获取到的是一个开启事务的连接。因此,还要修改JdbcUtils类的getConnection方法。
千万不要忘了在web.xml文件中配置TransactionFilter事务过滤器。
<filter>
<filter-name>TransactionFilterfilter-name>
<filter-class>cn.liayun.web.filter.TransactionFilterfilter-class>
filter>
<filter-mapping>
<filter-name>TransactionFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
至此,在线网上书店项目的环境总算是搭建好了!