1、三层架构:软件设计架构
软件设计的三层架构如下(参考视频20解析):注意三层架构与MVC开发模式的关系。
1. 界面层(表示层):用户看到的界面。用户可以通过界面上的组件和服务器进行交互
2. 业务逻辑层:处理业务逻辑的。
3. 数据访问层:操作数据存储文件(数据库、xml、properties等)。
1. M:Model,模型。JavaBean
* 完成具体的业务操作,如:查询数据库,封装对象
2. V:View,视图。JSP
* 展示数据。
3. C:Controller,控制器。Servlet(控制器就是一个中转)
* 获取用户的输入;
* 调用模型,使用模型封装的业务进行数据的查询与获取;
* 将模型查询出来的数据交给视图进行展示。
如下图,我们之前说的MVC的控制器(Servlet)以及视图(JSP)都在界面层。用户通过浏览器访问界面层,然后通过界面层向控制器(Servlet)发送请求,控制器(Servlet)接收用户的请求,并且获取用户提交的参数信息,将参数信息封装,接下来去调用业务逻辑层(service),业务逻辑层再去调用数据访问层(DAO),数据访问层查询数据库并将相应的数据返回给业务逻辑层,业务逻辑层再将数据返回给MVC的控制器(Servlet),控制器(Servlet)将数据发送给视图(JSP),用于展示数据,由这个JSP页面最终给客户端作出响应。
我们以前写代码的时候没有业务逻辑层,以前MVC模型中的model(JavaBean)就是在数据访问层,我们以前直接通过界面层的控制器(Servlet)直接调用数据访问层的模型中的业务逻辑(比如之前案例中的UserDao中的login()方法,我们直接使用Servlet调用login方法进行登录,login()方法会查询数据库有没有我们输入的参数并验证)。也就是说我们之前是没有编写中间的业务逻辑层的。
那为什么要定义业务逻辑层,这样反而更加复杂?我们在数据访问层中定义了对数据库最基本的CRUD操作,也就是说这些功能是十分单一的,但是这些单一功能的方法是无法完成一件具体的事情。业务逻辑层中将DAO中的简单方法组合成为复杂的功能(业务逻辑的操作),这样使得数据访问层的方法的复用性大大增强,我们的业务逻辑层要使用到哪一些功能,就将数据访问层中的功能取出来使用即可。我们在业务逻辑层(service)中会编写大量的代码来处理这些复杂的业务操作。而界面层(web)的功能是接收用户参数,封装数据,调用业务逻辑层完成数据的处理,最后将处理的数据转发给JSP页面,并将JSP页面返回给浏览器显示。
以后放置各部分代码的包起名:界面层(公司域名.项目名.web)、业务逻辑层(公司域名.项目名.service)、数据访问层(公司域名.项目名.dao)。以后项目也会这样分类代码,我们写代码的时候也需要按照规范来写代码。
我们将来会使用SSM三大框架对三层进行封装和简化,界面层(SpringMVC)、业务逻辑层(Spring)、数据访问层(MyBatis)
(这个案例结合自己文章看——Request案例:用户登录)
1. 技术选型:tomcat(服务器)+Servlet(控制器)+JSP(视图)+MySQL(数据库)+JDBCTemplate(JDBCTemplate对象简化JDBC的开发)+Duird(数据库连接池)+BeanUtilS(简化JavaBean的数据封装)
2. 数据库设计:
create database day17; -- 创建数据库
use day17; -- 使用数据库
create table user( -- 创建表
id int primary key auto_increment,
name varchar(20) not null,
gender varchar(5),
age int,
address varchar(32),
qq varchar(20),
email varchar(50)
);
1. 环境搭建
1) 创建数据库环境
2) 创建项目,导入需要的jar包
2. 编码
资料里面已经提供了各种页面的html文件,包括index.html(查询所有用户信息),提供index.html可以跳转到list.html(展示用户信息),在list.html可以对用户信息进行修改,跳转到update.html,可以添加用户信息,跳转到add.html。另外还有login.html的登录页面。其他文件就是对这些页面进行美化的js、css以及fonts文件。
确定当前需要的技术:tomcat(服务器)+Servlet(控制器)+JSP(视图,需要使用到EL表达式以及JSTL标签替代java代码)+MySQL(数据库)+JDBCTemplate(JDBCTemplate对象简化JDBC的开发)+Duird(数据库连接池)+BeanUtilS(简化JavaBean的数据封装)
设计数据库day09case(相关数据库在:MySQL数据目录:datadir=“C:\ProgramData\MySQL\MySQL Server 5.5\data” 下查看),注意,这里只是数据库的设计,并没有真正将数据库放在SQL的环境下运行。代码如下:
CREATE DATABASE day09case; -- 创建数据库day09case
SHOW DATABASES; -- 查看所有的数据库
USE day09case; -- 使用数据库,进入我们创建的数据库
SELECT DATABASE(); -- 确认当前正在使用的数据库
-- 在数据库中创建user表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT, -- id设置为主键,自动增长
NAME VARCHAR(20) NOT NULL, -- 姓名非空,字符类型
gender VARCHAR(5) NOT NULL, -- 性别非空,字符类型
age INT ,
address VARCHAR(32),
qq VARCHAR(20), -- qq设置为字符类型,否则数字太大可能超过int(32)位的范围,int最大也就是4294967296,qq号码很容易比这个大
email VARCHAR(50) -- email包括各种字符串,我们也使用字符串类型
);
首先,创建数据库环境。打开SQLyog软件,将之前设计的sql语句放到这个软件里面运行。可以在SQLyog里面右键打开表,查看当前表的内容,当前表没有任何数据。我们可以插入一些数据。
接下来,创建项目day09Case,将虚拟目录改为:/day09case,并将端口改为80,并根据之前的技术选型导入相应的jar包(放到web——WEB-INF——lib下),记得add as library。
数据库驱动jar包: mysql-connector-java-5.1.37-bin.jar
Druid jar包: druid-1.0.9.jar
c3p0(方便我们切换连接池) :c3p0-0.9.1.2.jar
JDBC Template jar包
commons-logging-1.2.jar、spring-beans-5.0.0.RELEASE.jar、spring-core-5.0.0.RELEASE.jar、spring-jdbc-5.0.0.RELEASE.jar、spring-tx-5.0.0.RELEASE.jar
JSTL标签:javax.servlet.jsp.jstl.jar、jstl-impl.jar
BeanUtils:commons-beanutils-1.8.0.jar、commons-logging-1.1.1.jar(这个是BeanUtils的依赖包)
随后,我们将资料提供的静态页面全部复制到web目录下(静态资源全部放到web目录),将来前端人员会将各类UI、UE效果图(流程跳转图),并将对应的静态页面编写好。我们拿到静态页面后,可以根据自己的需求,改造成为自己需要的文件(比如这里改造为jsp),然后将静态页面里面的假数据换成真实的动态的数据即可(通过Servlet控制器调用业务逻辑层获取数据,并将数据返回视图JSP替换假数据),页面是不需要我们编写的。
随后我们先不要编写代码,先启动服务器,看看静态页面之间的跳转能不能完成。访问:http://localhost/day09case/index.html,正常跳转,接下来我们就可以进行代码的编写。
案例有5个功能:列表查询展示、修改,删除、添加、登录。今天只做列表查询的功能。
分析参考视频23。
首先,在界面层,我们在浏览器访问index.jsp页面,点击“查询所有用户信息”按钮,跳转到list.jsp页面,这个页面展示所有用户的信息。目前静态页面里面的数据是假的,现在我们要将这些数据转变成为真是的数据库里面存储的数据。那么我们就不能跳转直接展示的页面list.jsp,而是跳转到一个Servlet(控制器):UserListServlet.java,通过这个Servlet去查询数据库,返回数据并将这些数据动态地展示回list.jsp。(我们后期会将html页面全部改为jsp)
这个UserListServlet.java不会从index.jsp接收数据,因此我们不需要设置request的编码。下面的步骤如下:
list.jsp接收到UserListServlet后,会使用el+jstl将数据展示到页面上。使用jstl的foreach标签遍历List集合,生成表格table展示数据。
在service层,我们会创建一个UserService.java接口以及该接口的实现类UserServiceImpl.java。这样设置的好处是,在界面层UserListServlet.java创建实现类的对象,如果我们实现类的功能有缺陷,我们只需要替换实现类就可以,不需要改变接口的功能,提高了程序的可扩展性与可维护性,这是面向接口的编程方式。在接口UserService.java里面有:public List findAll()方法,在实现类UserServiceImpl.java里面也会有:public List findAll()方法,这个方法调用dao.fianAll()完成查询,并将查询出来的数据返回给Servlet(UserListServlet)
在DAO层,我们也会创建接口UserDao.java以及实现类UserDaoImpl.java。在接口UserDao里面,有:public List findAll()方法,实现类UserDaoImpl.java里面也会有:public List findAll()方法,这个方法使用jdbc操作数据库。
用户访问index.jsp。跳转到UserListServlet.java,这个类调用UserServiceImpl.java的findAll()查询数据库,返回用户信息的List集合,同时UserServiceImpl.java也会调用UserDaoImpl.java的方法查询数据库。然后UserListServlet.java将list集合插入request域,并转发到list.jsp页面,这个页面遍历list集合并展示数据,这样用户在浏览器上就看到了list.jsp页面展示的数据。
我们先创建各类包:domain(数据库表对应的实现类)、web(界面层)、service(业务逻辑层)、dao(数据访问层)、util(工具类)。
首先,编写domain下的类User.java,这个类对应数据库的user表。代码如下
package lkj.domain;
public class User
{
private int id;
private String name;
private String gender;
private int age;
private String address;
private String qq;
private String email;
//空构造器
public User()
{
}
//带参构造器
public User(int id, String name, String gender, int age, String address, String qq, String email)
{
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.address = address;
this.qq = qq;
this.email = email;
}
//getter与setter方法
public int getId()
{
return id;
}
public String getName()
{
return name;
}
public String getGender()
{
return gender;
}
public int getAge()
{
return age;
}
public String getAddress()
{
return address;
}
public String getQq()
{
return qq;
}
public String getEmail()
{
return email;
}
public void setId(int id)
{
this.id = id;
}
public void setName(String name)
{
this.name = name;
}
public void setGender(String gender)
{
this.gender = gender;
}
public void setAge(int age)
{
this.age = age;
}
public void setAddress(String address)
{
this.address = address;
}
public void setQq(String qq)
{
this.qq = qq;
}
public void setEmail(String email)
{
this.email = email;
}
//toString方法
@Override
public String toString()
{
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", address='" + address + '\'' +
", qq='" + qq + '\'' +
", email='" + email + '\'' +
'}';
}
}
接下来,我们先编写客户访问的首页:index.jsp。创建一个新的index.jsp,并将前面提供的index.html的内容复制到index.jsp,index.jsp只留下配置的第一行。index.jsp只需要修改超链接跳转的路径即可,我们跳转到UserListServlet.java。index.jsp的代码如下
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>首页title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery-2.1.0.min.js">script>
<script src="js/bootstrap.min.js">script>
<script type="text/javascript">
script>
head>
<body>
<%--可以看出我们的页面的样式设计使用了BootStrap来设计--%>
<div align="center">
<%--
我们只需要修改超链接的href属性即可,将其跳转到UserListServlet。
注意这里的请求是浏览器发出的,因此需要加虚拟目录。
我们的虚拟目录通过EL表达式的隐式对象pageContext(用来获取JSP的内置对象)获取JSP的request对象,
再通过JSP对象request对应的java类Request,里面有getContextPath方法,request.contextPath的形式的获取虚拟目录。
EL表达式的pageContext对象,对应JSP的pageContext对象的获取JSP其他8个内置对象的功能,用来获取JSP其他8个内置对象;
EL表达式的pageScope对象,对应JSP的pageContext对象的共享数据功能,用来在当前的JSP页面进行数据共享。
既JSP的pageContext对象对应EL表达式内置对象pageScope与pageContext对象,但是对应的功能不一样。
--%>
<a
href="${pageContext.request.contextPath}/userListServlet" style="text-decoration:none;font-size:33px">查询所有用户信息
a>
div>
body>
html>
然后我们编写UserListServlet类,在web下面new一个Servlet包,下面放UserListServlet类(web包括Servlet与JSP,需要区分)。UserListServlet的代码如下:
package lkj.web.servlet;
import lkj.domain.User;
import lkj.service.impl.UserServiceImpl;
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.util.List;
//注意,只有界面层需要创建Servlet,而service层与dao层都只是创建java
@WebServlet("/userListServlet")
public class UserListServlet extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//首先,我们创建service中的的UserServiceImpl类的对象,并调用其中的findAll()方法,返回一个包含User对象的List集合
UserServiceImpl service = new UserServiceImpl();//以这种多态的形式创建对象
List<User> user = service.findAll();//返回一个List集合
//将获取到的List集合存储到JSP的request对象,便于在request域中进行数据共享
request.setAttribute("user" , user);
//将请求转发到list.jsp页面进行展示,请求转发是由服务器发出的,不需要加虚拟目录
request.getRequestDispatcher("/list.jsp").forward(request , response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
this.doPost(request, response);
}
}
接下来需要编写service包中的UserService接口与UserServiceImpl类,在service下放一个impl的包,下面放UserServiceImpl类。代码如下
//UserServiceImpl
package lkj.service;
import lkj.domain.User;
import java.util.List;
/**
* 用户管理的业务接口
*/
public interface UserService
{
/**
* 查询所有用户信息
* @return
*/
//创建一个抽象的finaALL()方法,注意加文档注释。注意加返回值为List
public abstract List<User> findAll();
}
//UserService
package lkj.service.impl;
import lkj.dao.UserDao;
import lkj.dao.impl.UserDaoImpl;
import lkj.domain.User;
import lkj.service.UserService;
import java.util.List;
//这个类实现UserService接口,并重写findAll()方法
public class UserServiceImpl implements UserService
{
//这个方法调用dao中的UserDaoImpl方法的findAll()方法。这个引用只被这个类使用,私有化
private UserDao dao = new UserDaoImpl();//以这种多态的方式创建对象!!!
@Override
public List<User> findAll()
{
return dao.findAll();//返回dao的findAll()方法返回的List
}
}
接下来需要编写dao包中的UserDao接口与UserDaoImpl类,在dao下放一个impl的包,下面放UserDaoImpl类。代码如下
//UserDaoImpl
package lkj.dao;
import lkj.domain.User;
import java.util.List;
/**
* 用户操作的DAO
*/
public interface UserDao
{
//创建一个抽象的findAll方法,同样返回一个List
public abstract List<User> findAll();
}
//UserDao
package lkj.dao.impl;
import lkj.dao.UserDao;
import lkj.domain.User;
import lkj.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class UserDaoImpl implements UserDao
{
private JdbcTemplate jt = new JdbcTemplate(JDBCUtils.getDataSource());
@Override
public List<User> findAll()
{
//使用JDBC操作数据库...
//1.定义sql
String sql = "select * from user";
//2、这里我们不需要传入查询参数,直接使用JDBCTemplate的query方法即可.
//这个方法查询数据库,查询出来的数据每一行构造成为一个User对象并将这些User对象放入List集合
List<User> user = jt.query(sql, new BeanPropertyRowMapper<User>(User.class));
//我们这里不需要接收用户提交的参数,既不需要使用到BeanUtils来封装用户提交的JavaBean类User的参数
return user;//将查询出来的List集合返回
}
}
接下来创建Util包中的JDBCUtils工具类,先将Druid连接池的配置文件放到src文件夹下。注意将druid.properties中的数据库与端口修改为我们当前的设定值。druid.properties的代码如下:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day09case
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
接下来创建JDBCUtils类,这个脸进行数据库的连接配置,并初始化连接池对象。提供连接池对象与数据库连接对象。之前写过,直接将代码放在util包下:
package lkj.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBC工具类 使用Durid连接池
*/
public class JDBCUtils
{
private static DataSource ds ;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 获取连接Connection对象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
最后,编写界面层UserListServlet跳转到的list.jsp页面。同样创建一个新的list.jsp,然后将list.html的内容复制到list.jsp上,list.jsp保留第一行配置。代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>用户信息管理系统title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery-2.1.0.min.js">script>
<script src="js/bootstrap.min.js">script>
<style type="text/css">
td, th {
text-align: center;
}
style>
head>
<body>
<div class="container">
<h3 style="text-align: center">用户信息列表h3>
<table border="1" class="table table-bordered table-hover">
<tr class="success">
<th>编号th>
<th>姓名th>
<th>性别th>
<th>年龄th>
<th>籍贯th>
<th>QQth>
<th>邮箱th>
<th>操作th>
tr>
<%--
我们将静态页面的虚假信息静态删除,使用JSTL标签遍历request域转发过来的List<User>集合。
request域存储的user键值对如下:
request.setAttribute("user" , user);
--%>
<c:forEach items="${requestScope.user}" var="user" varStatus="s">
<tr>
<td>${s.count}td> <%--用循环次数代表编号--%>
<td>${user.name}td> <%--用逻辑视图的方法,在EL表达式中使用User的getter取出属性的值--%>
<td>${user.gender}td>
<td>${user.age}td>
<td>${user.address}td>
<td>${user.qq}td>
<td>${user.email}td>
<td><a class="btn btn-default btn-sm" href="update.html">修改a> <a class="btn btn-default btn-sm" href="">删除a>td>
tr>
c:forEach>
<tr>
<td colspan="8" align="center"><a class="btn btn-primary" href="add.html">添加联系人a>td>
tr>
table>
div>
body>
html>
所有代码写为之后,我们启动服务器,访问:http://localhost/day09case/index.jsp。(我将WEB-INF文件夹写为WEB_INF,导致出错。注意不要犯这种低级错误),页面成功查询数据库展示: