一、创建工程,引入依赖
1.架构
①架构的概念②单一架构 2.创建工程3. 引入依赖
①搜索依赖信息的网站②持久化层所需依赖③表示层所需依赖④辅助功能所需依赖⑤最终完整依赖信息 4.建包 二、搭建环境:持久化层
1.数据建模
①物理建模②逻辑建模 2.数据库连接信息3.获取数据库连接
①创建JDBCUtils工具类②创建javax.sql.DataSource对象③创建ThreadLocal对象④声明方法:获取数据库连接⑤声明方法:释放数据库连接⑥初步测试 4.baseDao
①泛型的说明②测试baseDao 5.子类Dao
①创建接口和实现类 三、搭建环境:事务控制
1.总体思路2.TransactionFilter
①创建Filter类②完整代码③配置web.xml④注意点 四、搭建环境:表示层
1.视图模板技术Thymeleaf
①服务器端渲染③逻辑视图与物理视图④ViewbaseServlet完整代码⑤增加前缀后缀的web.xml配置⑥Thymeleaf页面语法 2.ModelbaseServlet
①提出问题②解决方案③完整代码④继承关系 五、搭建环境:辅助功能
1.常量类2.MD5加密工具类
3.日志配置文件 六、业务功能:登录
1.显示首页
① 流程图②创建PortalServlet③在index.html中编写登录页面 2.登录操作
①流程图②编写EmpDao的方法③创建EmpService类④创建登录失败异常⑤增加一个常量⑥创建AuthServlet类⑦temp页面⑧注册,在web.xml中增加信息⑨运行结果 3.退出登录
①在临时页面编写超链接②在AuthServlet编写退出的逻辑 七、业务功能:显示列表
1.流程图2.创建组件
①创建Java类② 注册,在web.xml中增加信息 3.页面显示4.和登录对接 八、业务功能:显示详情
1.详情页代码2.workServlet方法3.MemorialsService方法4.MemorialsServiceImpl方法增加5.MemorialsDao方法5.MemorialsDaoImpl方法6. 结果图展示 九、业务功能:回复
1.WorkServlet方法2.MemorialsService方法3.MemorialsServiceImpl方法4.MemorialsDao方法5.MemorialsDaoImpl方法 十、业务功能:登录检查
1.逻辑2.创建LoginFilter3.配置web.xml 十一、打包部署
1.适配环境信息2.跳过测试打包3.上传war包4.启动Tomcat5.访问测试
本章节将以创建一个单一架构的业务系统,从搭建到部署的过程,演示Maven项目
完整代码gitee地址:https://gitee.com/reportories/maven_study_all_in_one.git
架构其实就是项目的结构,只是因为架构是一个更大的词,通常来形容比较大规模事物的结构。
②单一架构单一架构也叫all-in-one的结构,就是所有的代码、配置文件、各种资源都在同一个工程
一个项目包含一个工程导出一个war包放在一个Tomcat上运行 2.创建工程
【1】 到哪找?
依赖信息网站
【2】怎么选择?
确定技术选型:确定我们项目中要使用哪些技术到网站去搜索具体技术的依赖信息确定这个技术使用哪个版本的依赖
考虑因素1:看是否有别的技术要求这里必须使用某一个版本考虑因素2:如果没有硬性要求,那么选择较高版本或者下载量的版本 在实际使用中检验所有依赖信息是否都正常可用
tips:
确定技术选型、组件依赖列表,项目划分模块等操作都是属于架构设计的范围。
项目本身所属行业的基本特点项目具体的功能需求项目预计访问压力程度项目预计将来需要扩展的功能 ②持久化层所需依赖
首先确保自己本机已经安装了mysql数据库,安装过程不再赘述。
mysql依赖
mysql mysql-connector-java 8.0.28
druid依赖
com.alibaba druid 1.2.8
③表示层所需依赖commons-dbutils commons-dbutils 1.7
javax.servlet javax.servlet-api 3.1.0 provided
④辅助功能所需依赖org.thymeleaf thymeleaf 3.0.11.RELEASE
junit junit 4.12 test
⑤最终完整依赖信息ch.qos.logback logback-classic 1.2.3 test
4.建包mysql mysql-connector-java 8.0.28 com.alibaba druid 1.2.8 commons-dbutils commons-dbutils 1.7 javax.servlet javax.servlet-api 3.1.0 provided org.thymeleaf thymeleaf 3.0.11.RELEASE junit junit 4.12 test ch.qos.logback logback-classic 1.2.3
| package功能 | package名称 |
|---|---|
| 主包 | com.sr.maven |
| 子包【实体类】 | com.sr.maven.entity |
| 子包 【servlet基类包】 | com.sr.maven.servlet.base |
| 子包 【servelet模块包】 | com.sr.maven.servlet.module |
| 子包 【servlet接口包】 | com.sr.maven.servlet.api |
| 子包 【servlet实现类包】 | com.sr.maven.servlet.imp |
| 子包 【Dao接口包】 | com.sr.maven.dao.api |
| 子包 【Dao实现类包】 | com.sr.maven.dao.imp |
| 子包【Filter过滤器包】 | com.sr.maven.filter |
| 子包 【异常类包】 | com.sr.maven.exception |
| 子包 【工具类包】 | com.sr.maven.util |
| 子包 【测试类包】 | com.sr.maven.test |
在mysql数据库运行sql语句,进行物理建模,建表
CREATE DATAbase db_imperial_court; USE db_imperial_court; CREATE TABLE t_emp ( emp_id INT PRIMARY KEY auto_increment, emp_name CHAR ( 100 ) NOT NULL, emp_position CHAR ( 100 ) NOT NULL, login_account CHAR ( 100 ) NOT NULL UNIQUE, login_password CHAR ( 100 ) NOT NULL ); INSERT INTO t_emp ( emp_name, emp_position, login_account, login_password ) VALUES# 16540504 ( '爱新觉罗.玄烨', 'emperor', 'xiaoxuanzi1654', '25325c896624D444B2E241807DCAC988' ), ( '纳兰明珠', 'minister', 'brightba771635', '25325c896624D444B2E241807DCAC988' ), ( '赫舍里.索额图', 'minister', 'tutu1636', '25325c896624D444B2E241807DCAC988' ); CREATE TABLE t_memorials ( memorials_id INT PRIMARY KEY auto_increment, memorials_title CHAR ( 100 ) NOT NULL, memorials_content VARCHAR ( 5000 ) NOT NULL, memorials_emp INT NOT NULL, memorials_create_time CHAR ( 100 ), feedback_time CHAR ( 100 ), feedback_content VARCHAR ( 1000 ), memorials_status INT NOT NULL ); INSERT INTO t_memorials ( memorials_title, memorials_content, memorials_emp, memorials_create_time, feedback_time, feedback_content, memorials_status ) VALUES# 16540504 ( '浙江巡抚奏钱塘江堤决口疏', '皇上啊,不好了!钱塘江发大水啦!堤坝冲毁啦!您看咋弄啊!', 2, '1690-05-07', NULL, NULL, 0 ), ( '左都御史参鳌拜圈地口疏', '皇上啊,鳌拜这厮不是东西啊!占老百姓的地呀,还打人呀!您看咋弄啊', 3, '1690-04-14', NULL, NULL, 0 ), ( '督察员参吴三桂不臣疏', '皇上啊,不好了!吴三桂那孙子想造反!', 2, '1693-11-18', NULL, NULL, 0 ), ( '兵部奏准噶尔犯境疏', '皇上啊,不好了!噶尔丹要打过来了!', 3, '1693-11-18', NULL, NULL, 0 );②逻辑建模
【1】Emp实体类
package com.sr.maven.entity;
public class Emp {
private Integer empId;
private String empName;
private String empPosition;
private String loginAccount;
private String loginPassword;
public Emp() {
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpPosition() {
return empPosition;
}
public void setEmpPosition(String empPosition) {
this.empPosition = empPosition;
}
public String getLoginAccount() {
return loginAccount;
}
public void setLoginAccount(String loginAccount) {
this.loginAccount = loginAccount;
}
public String getLoginPassword() {
return loginPassword;
}
public void setLoginPassword(String loginPassword) {
this.loginPassword = loginPassword;
}
}
【2】Memorials实体类
public class Memorials {
private Integer memorialsId;
private String memorialsTitle;
private String memorialsContent;
private String memorialsContentDigest;
private Integer memorialsEmp;
private String memorialsEmpEmpName;
private String memorialsCreateTime;
private String feedbackTime;
private String feedbackContent;
private Integer memorialsStatus;
public Memorials() {
}
public Integer getMemorialsId() {
return memorialsId;
}
public void setMemorialsId(Integer memorialsId) {
this.memorialsId = memorialsId;
}
public String getMemorialsTitle() {
return memorialsTitle;
}
public void setMemorialsTitle(String memorialsTitle) {
this.memorialsTitle = memorialsTitle;
}
public String getMemorialsContent() {
return memorialsContent;
}
public void setMemorialsContent(String memorialsContent) {
this.memorialsContent = memorialsContent;
}
public String getMemorialsContentDigest() {
return memorialsContentDigest;
}
public void setMemorialsContentDigest(String memorialsContentDigest) {
this.memorialsContentDigest = memorialsContentDigest;
}
public Integer getMemorialsEmp() {
return memorialsEmp;
}
public void setMemorialsEmp(Integer memorialsEmp) {
this.memorialsEmp = memorialsEmp;
}
public String getMemorialsEmpEmpName() {
return memorialsEmpEmpName;
}
public void setMemorialsEmpEmpName(String memorialsEmpEmpName) {
this.memorialsEmpEmpName = memorialsEmpEmpName;
}
public String getMemorialsCreateTime() {
return memorialsCreateTime;
}
public void setMemorialsCreateTime(String memorialsCreateTime) {
this.memorialsCreateTime = memorialsCreateTime;
}
public String getFeedbackTime() {
return feedbackTime;
}
public void setFeedbackTime(String feedbackTime) {
this.feedbackTime = feedbackTime;
}
public String getFeedbackContent() {
return feedbackContent;
}
public void setFeedbackContent(String feedbackContent) {
this.feedbackContent = feedbackContent;
}
public Integer getMemorialsStatus() {
return memorialsStatus;
}
public void setMemorialsStatus(Integer memorialsStatus) {
this.memorialsStatus = memorialsStatus;
}
}
2.数据库连接信息
编写自己的数据库配置
driverClassName=com.mysql.cj.jdbc.Driver url= jdbc:mysql://localhost:3306/db_imperial_court?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username=自己的账户 password=自己的密码 initialSize=10 maxActive=20 maxWait=100003.获取数据库连接 ①创建JDBCUtils工具类
首先先放出完整的工具类内容,下面进行分步骤的添加解释
package com.sr.maven.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;
public class JDBCUtils {
//数据源成员变量,设置为静态资源,保证大对象的单例性,同时保证静态方法中可以访问
private static DataSource dataSource;
//由于ThreadLocal对象需要作为绑定数据时K-v对中的key,所以需要保证唯一性
//加static声明为静态资源可保证唯一性
private static ThreadLocal threadLocal = new ThreadLocal<>();
//在静态代码块中初始化数据源
static {
try {
//从jdbc.properties中读取链接数据库的信息
//为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件
//确定的基准,类路径的根目录,resource目录下的内容经过构建后打包操作会确定放在 WEB-INF/classes目录下。
//WEB-INF/classes 目录存放编译好的*.class字节码文件,所以这个目录我们就称之为类路径
//类路径无论是在本地运行还是在服务器端运行都是一个确定的路径
//具体操作代码
//1.获取当前类的类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
//2.通过类加载器对象从类路径根目录下读取文件
InputStream stream = classLoader.getResourceAsStream("jdbc.properties");
//3.使用Properties类封装属性文件中的数据
Properties properties = new Properties();
properties.load(stream);
//4.根据Properties对象(已经封装了数据库连接信息)来创建数据源对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
//为了避免在真正抛出异常后,catch块捕捉获取到异常掩盖问题
//这里将所捕获的异常封装为运行时异常继续抛出去
throw new RuntimeException(e);
}
}
public static Connection getConnection() {
Connection connection;
try {
//1.尝试从当前线程检查是否已存在绑定的Connection对象
connection = threadLocal.get();
//2.检查Connection对象是否为null
if (connection == null) {
connection = dataSource.getConnection();
//绑定到当前线程
threadLocal.set(connection);
}
}catch (SQLException e){
e.printStackTrace();
throw new RuntimeException(e);
}
return connection;
}
public static void releaseConnection(Connection connection){
if(connection!=null){
try {
connection.close();
threadLocal.remove();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
②创建javax.sql.DataSource对象
//数据源成员变量,设置为静态资源,保证大对象的单例性,同时保证静态方法中可以访问
private static DataSource dataSource;
//在静态代码块中初始化数据源
static {
try {
//从jdbc.properties中读取链接数据库的信息
//为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件
//确定的基准,类路径的根目录,resource目录下的内容经过构建后打包操作会确定放在 WEB-INF/classes目录下。
//WEB-INF/classes 目录存放编译好的*.class字节码文件,所以这个目录我们就称之为类路径
//类路径无论是在本地运行还是在服务器端运行都是一个确定的路径
//具体操作代码
//1.获取当前类的类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
//2.通过类加载器对象从类路径根目录下读取文件
InputStream stream = classLoader.getResourceAsStream("jdbc.properties");
//3.使用Properties类封装属性文件中的数据
Properties properties = new Properties();
properties.load(stream);
//4.根据Properties对象(已经封装了数据库连接信息)来创建数据源对象
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
//为了避免在真正抛出异常后,catch块捕捉获取到异常掩盖问题
//这里将所捕获的异常封装为运行时异常继续抛出去
throw new RuntimeException(e);
}
}
③创建ThreadLocal对象
【1】提出需求
(1)在一个方法内控制事务
如果在每一个Service方法中都写下面的代码,那么代码重复性就太高了
try{
//1.获取数据连接
//重要:要保证参与事务的多个数据库操作(SQL语句)使用的是同一个数据库连接
Connection conn = JDBCUtils.getConnection();
//2.核心操作
//....省略
//3.核心操作陈工结束,可以提交事务
conn.commit();
}catch(Exception e){
//4 核心操作抛出异常,必须回滚事务
conn.rollback();
}finally{
//5.释放数据库连接
JDBCUtils.releaseConnection(conn);
}
(2)将重复的代码抽取到Filter中
所谓当前请求覆盖的Servlet方法、Service方法、Dao方法就是chain.doFilter间接调用的方法。
public void doFilter(ServletRequest request,ServlertResponse response ,FilterChain chain){
try{
//1.获取数据连接
//重要:要保证参与事务的多个数据库操作(SQL语句)使用的是同一个数据库连接
Connection conn = JDBCUtils.getConnection();
//关闭自动提交
connection.setAutoCommit(false);
//2.核心操作:通过chain对象放行当前请求
//这样就可以保证当前请求覆盖的Servlet方法、Service方法、Dao方法都在同一个事务中
//同时各个请求都经过这个Filter,所以当前事务控制的代码这里只写了一遍
//避免了代码的冗余
chain.doFilter(request,response);
//3.核心操作陈工结束,可以提交事务
conn.commit();
}catch(Exception e){
//4 核心操作抛出异常,必须回滚事务
conn.rollback();
}finally{
//5.释放数据库连接
JDBCUtils.releaseConnection(conn);
}
}
(3)数据的跨方法传递
但是,通过(2),我们还是会遇到一个难题,如图所示,为了保证使用同一个数据库连接对象,但是chain.doFilter方法又无法传参,我们该怎么办?
通过JDBCUtils工具类获取的Connection对象需要传递给Dao方法,让事务涉及到的所有Dao方法用的都是同一个Connection对象。
但是Connection对象无法通过chain.doFilter()方法以参数的形式传递。以下所有方法调用都是在同一线程内。
【2】ThreadLocal对象的功能
全类名:java.lang.ThreadLocal泛型T:要绑定到当前线程的类型三个主要的方法
| 方法名 | 功能 |
|---|---|
| set(T value) | 将数据绑定到当前线程 |
| get() | 从当前线程获取已绑定的数据 |
| remove | 将数据从当前线程移除 |
【3】Java代码
//由于ThreadLocal对象需要作为绑定数据时K-v对中的key,所以需要保证唯一性 //加static声明为静态资源可保证唯一性 private static ThreadLocal④声明方法:获取数据库连接threadLocal = new ThreadLocal<>();
public static Connection getConnection() {
Connection connection;
try {
//1.尝试从当前线程检查是否已存在绑定的Connection对象
connection = threadLocal.get();
//2.检查Connection对象是否为null
if (connection == null) {
connection = dataSource.getConnection();
//绑定到当前线程
threadLocal.set(connection);
}
}catch (SQLException e){
e.printStackTrace();
throw new RuntimeException(e);
}
return connection;
}
⑤声明方法:释放数据库连接
public void releaseConnection(Connection connection){
if(connection!=null){
try {
connection.close();
threadLocal.remove();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
⑥初步测试
我们创建一个测试类,看能否正常获取到连接,如下图所示,结果显示正常。
public class ImperialCourtTest {
@Test
public void testGetConnection(){
Connection connection = JDBCUtils.getConnection();
System.out.println("connection = "+connection);
JDBCUtils.releaseConnection(connection);
}
}
4.baseDao
①泛型的说明
public class baseDao②测试baseDao{ //DBUtils 工具包提供的数据操作对象 private QueryRunner runner = new QueryRunner(); public T getSingleBean(String sql ,Class entityBean,Object ... parameters){ //获取数据库连接 Connection connection = JDBCUtils.getConnection(); try { return runner.query(connection,sql,new BeanHandler<>(entityBean),parameters); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } public List getBeanList(String sql , Class entityBean, Object ... parameters){ //获取数据库连接 Connection connection = JDBCUtils.getConnection(); try { return runner.query(connection,sql,new BeanListHandler<>(entityBean),parameters); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } public int update (String sql ,Object ... parameters){ try{ Connection connection = JDBCUtils.getConnection(); return runner.update(connection,sql,parameters); }catch (SQLException e){ e.printStackTrace(); throw new RuntimeException(e); } } }
运行显示,结果正常。
public class ImperialCourtTest {
private baseDao baseDao = new baseDao<>();
@Test
public void testGetConnection(){
Connection connection = JDBCUtils.getConnection();
System.out.println("connection = "+connection);
JDBCUtils.releaseConnection(connection);
}
@Test
public void testGetSingleBean(){
String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp where emp_id = ?";
Emp singleBean = baseDao.getSingleBean(sql, Emp.class, 1);
System.out.println(singleBean);
}
@Test
public void testGeteBeanList(){
String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp ";
List beanList = baseDao.getBeanList(sql, Emp.class);
beanList.forEach(i->
System.out.println(i.toString())
);
}
@Test
public void testUpdate(){
String sql = "update t_emp set emp_position = ? where emp_id = ?";
String empPosition = "emperor";
String empId = "3";
int affectRow = baseDao.update(sql,empPosition,empId);
System.out.println("affectRow = "+affectRow);
}
}
5.子类Dao
①创建接口和实现类
EmpDao
public interface EmpDao {
}
EmpDaoImpl
public class EmpDaoImpl extends baseDaoimplements EmpDao { }
MemorialsDao
public interface MemorialsDao {
}
MemorialsDaoImpl
public class MemorialsDaoImpl extends baseDao三、搭建环境:事务控制 1.总体思路implements MemorialsDao { }
按照先前操作的代码和思路,总结如下:
package com.sr.maven.filter;
import com.sr.maven.util.JDBCUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
public class TransactionFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
//声明一个集合保存静态资源扩展名,非操作sql的 请求进入到这个过滤器 会浪费性能
private static Set staticResourceExNameSet;
static {
staticResourceExNameSet = new HashSet<>();
staticResourceExNameSet.add(".png");
staticResourceExNameSet.add(".jpg");
staticResourceExNameSet.add(".js");
staticResourceExNameSet.add(".css");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//前置操作,排除静态资源
HttpServletRequest request = (HttpServletRequest)servletRequest;
String servletPath = request.getServletPath();
if(servletPath.contains(".")){
String extName = servletPath.substring(servletPath.lastIndexOf("."));
if(staticResourceExNameSet.contains(extName)){
//如果检测到当前请求确实是静态资源,则直接方形,不做事务操作
filterChain.doFilter(servletRequest,servletResponse);
return;
}
}
//1.获取数据库连接
Connection connection =null;
//
try {
connection = JDBCUtils.getConnection();
//关闭自动提交
connection.setAutoCommit(false);
//2.核心操作
filterChain.doFilter(servletRequest,servletResponse);
//3.提交事务
connection.commit();
}catch (Exception e){
try {
//4.回滚事务
assert connection != null;
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
//页面显示:将这里捕获到的异常发送到指定页面
String message = e.getMessage();
request.setAttribute("message",message);
request.getRequestDispatcher("/").forward(request,servletResponse);
}
finally {
JDBCUtils.releaseConnection(connection);
}
}
@Override
public void destroy() {
}
}
③配置web.xml
web.xml中增加过滤器配置
④注意点txFilter com.sr.maven.filter.TransactionFilter txFilter /*
【1】确保异常回滚
在程序执行的过程中,必须让所有的catch块把编译时异常转成运行时异常抛出,否则,Filter捕获不到就无法回滚。
【2】谨防数据库连接提前释放
由于诸多操作都是在使用同一个数据库连接,中间任何一个环节释放数据库连接都会导致后续操作无法正常完成。
②简要的工作机制
【1】初始化阶段
目标:创建TemplateEngine对象封装:因为对每一个请求来说,TemplateEngine对象使用的都是同一个,所以在初始化阶段准备好。
【2】请求处理阶段
假设有如下页面地址:
/WEB-INF/pages/apple.html /WEB-INF/pages/banana.html /WEB-INF/pages/orange.html /WEB-INF/pages/grape.html /WEB-INF/pages/egg.html
这样的地址可以直接访问到页面本身,我们称之为:物理视图。而将物理视图中前面,后面的固定内容抽取出来,让每次请求指定中间变化部分即可,那么中间变化部分就叫:逻辑视图。
为了简化视图页面的处理过程,封装一个基类,以后具体业务直接继承就好。
package com.sr.maven.servlet.base;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ViewbaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
//1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
//2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
//给解析器对象设置参数
templateResolver.setTemplateMode(TemplateMode.HTML);
//设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
//设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
//设置缓存过期时间
templateResolver.setCacheTTLMs(600000L);
templateResolver.setCacheable(true);
//设置服务器编码格式
templateResolver.setCharacterEncoding("utf-8");
//创建模板引擎对象
templateEngine = new TemplateEngine();
//给模板引擎设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
public void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse res) throws IOException {
//1.设置响应体内容类型和字符集
res.setContentType("text/html;charset=UTF-8");
//2.创建WebContext对象
WebContext webContext = new WebContext(req,res,getServletContext());
//3.处理模板数据
templateEngine.process(templateName,webContext,res.getWriter());
}
}
⑤增加前缀后缀的web.xml配置
view-prefix
/WEB-INF/pages/
view-suffix
.html/
⑥Thymeleaf页面语法
详细去看官网文档就可以
2.ModelbaseServlet ①提出问题【1】我们的需求
【2】HttpServlet的局限
doGet()方法:处理Get请求doPost()方法:处理Post请求 ②解决方案
每个请求附带一个请求参数,表明自己要调用的目标方法Servlet根据目标方法通过反射调用目标方法 ③完整代码
package com.sr.maven.servlet.base;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class ModelbaseServlet extends ViewbaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.在所有的request.getParameter()前面设置解析请求体的字符集
req.setCharacterEncoding("UTF-8");
//2.从请求参数中获取method对应的数据
String method = req.getParameter("method");
//3。通过反射调用method对应的方法
//这里使用this. 是获取的子类的类型,因为真正执行的时候 是走的子类
Class extends ModelbaseServlet> clazz = this.getClass();
try{
//获取method对应的Method对象
Method methodObject = clazz.getDeclaredMethod(method,HttpServletRequest.class,HttpServletResponse.class);
//打开访问权限
methodObject.setAccessible(true);
//通过method调用方法
methodObject.invoke(this,req,resp);
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
④继承关系
五、搭建环境:辅助功能
1.常量类
package com.sr.maven.util;
public class ImperialCourtConst {
private static final String LOGIN_FAIL_MESSAGE = "帐号、密码错误,不可进宫!";
private static final String ACCESS_DENIED_MESSAGE = "宫闱禁地,不可擅入!";
}
2.MD5加密工具类
package com.sr.maven.util;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Util {
public static String encode(String source) {
//1.判断明文字符串是否有效
if (source == null || "".equals(source)) {
throw new RuntimeException("用于加密的明文不可为空");
}
//2.声明算法名称
String algorithm = "md5";
//3.获取MessageDigest对象
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//4. 获取明文字符串对应的字节数组
byte[] input = source.getBytes();
//5.执行加密
byte[] output = messageDigest.digest(input);
//6.创建BigInter对象
int signum =1 ;
BigInteger bigInteger = new BigInteger(signum,output);
//7.按照16进制将BigInteger的值转换为字符串
int radix = 16;
return bigInteger.toString(radix).toUpperCase();
}
}
3.日志配置文件
六、业务功能:登录 1.显示首页 ① 流程图 ②创建PortalServlet[%d{HH:mm:ss.SSS}][%-5level][%thread][%logger][%msg]%n UTF-8
【1】创建Java类
package com.sr.maven.servlet.module;
import com.sr.maven.servlet.base.ViewbaseServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class PortalServlet extends ViewbaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//声明要访问的首页的逻辑视图
String templateName = "index";
//调用父类的方法根据逻辑视图名称渲染视图
processTemplate(templateName,req,resp);
}
}
【2】注册,添加到web.xml中
③在index.html中编写登录页面portalServlet com.sr.maven.servlet.module.PortalServlet portalServlet /
乾清宫
2.登录操作
①流程图
②编写EmpDao的方法
package com.sr.maven.dao.api;
import com.sr.maven.entity.Emp;
public interface EmpDao {
Emp selectEmpByLoginAccount(String loginAccount, String loginPassword);
}
package com.sr.maven.dao.impl; import com.sr.maven.dao.baseDao; import com.sr.maven.dao.api.EmpDao; import com.sr.maven.entity.Emp; public class EmpDaoImpl extends baseDao③创建EmpService类implements EmpDao { @Override public Emp selectEmpByLoginAccount(String loginAccount, String loginPassword) { //1.编写sql语句 String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp where login_account=? and login_password=?"; //2.调用父类方法查询单个对象 return super.getSingleBean(sql, Emp.class, loginAccount,loginPassword); } }
package com.sr.maven.service;
import com.sr.maven.entity.Emp;
public interface EmpService {
Emp getEmpByLoginAccount(String loginAccount, String loginPassword);
}
package com.sr.maven.service.impl;
import com.sr.maven.dao.api.EmpDao;
import com.sr.maven.dao.impl.EmpDaoImpl;
import com.sr.maven.entity.Emp;
import com.sr.maven.exception.LoginFailException;
import com.sr.maven.service.EmpService;
import com.sr.maven.util.ImperialCourtConst;
import com.sr.maven.util.MD5Util;
public class EmpServiceImpl implements EmpService {
private EmpDao empDao = new EmpDaoImpl();
@Override
public Emp getEmpByLoginAccount(String loginAccount, String loginPassword) {
//1.对密码进行加密
String encodeLoginPassword = MD5Util.encode(loginPassword);
//2.根据账户和密码加密密码查询数据库
Emp emp = empDao.selectEmpByLoginAccount(loginAccount,loginPassword);
//3.检查Emp对象是否为空
if(emp !=null ){
// ①不为null,返回emp
return emp;
}else
{
//抛出登录异常失败
throw new LoginFailException(ImperialCourtConst.LOGIN_FAIL_MESSAGE);
}
}
}
④创建登录失败异常
package com.sr.maven.exception;
public class LoginFailException extends RuntimeException{
public LoginFailException() {
super();
}
public LoginFailException(String message) {
super(message);
}
public LoginFailException(String message, Throwable cause) {
super(message, cause);
}
public LoginFailException(Throwable cause) {
super(cause);
}
protected LoginFailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
⑤增加一个常量
⑥创建AuthServlet类
package com.sr.maven.servlet.module;
import com.sr.maven.entity.Emp;
import com.sr.maven.exception.LoginFailException;
import com.sr.maven.service.EmpService;
import com.sr.maven.service.impl.EmpServiceImpl;
import com.sr.maven.servlet.base.ModelbaseServlet;
import com.sr.maven.util.ImperialCourtConst;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class AuthServlet extends ModelbaseServlet {
private EmpService empService = new EmpServiceImpl();
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//1.先获取请求参数
String loginAccount = req.getParameter("loginAccount");
String loginPassword = req.getParameter("loginPassword");
//2.调用EmpService 的方法,执行登录的逻辑
Emp emp = empService.getEmpByLoginAccount(loginAccount,loginPassword);
//3. 通过requet获取HTTPSession
HttpSession session = req.getSession();
//4.将查询的Emp对象存入Session域
session.setAttribute(ImperialCourtConst.LOGIN_EMP_ATTR_NAME,emp);
//5.前往指定的页面视图
String templateName = "temp";
processTemplate(templateName,req,resp);
}catch (Exception e){
e.printStackTrace();
//判断此处是否是 登录失败异常
if(e instanceof LoginFailException){
//如果是登录失败异常跳转回登录页面
//存入请求域
req.setAttribute("message",e.getMessage());
//处理视图:index
processTemplate("index",req,resp);
}else{
//如果不是登录异常则封装为运行时异常继续抛出
throw new RuntimeException(e);
}
}
}
}
⑦temp页面
temp
⑧注册,在web.xml中增加信息
⑨运行结果authServlet com.sr.maven.servlet.module.AuthServlet authServlet /auth
帐号密码: xiaoxuanzi1654 16540504
当前完整代码
temp
退朝
②在AuthServlet编写退出的逻辑
protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.通过request 对象获取 HttpSession对象
HttpSession session = req.getSession();
//2.将HttpSession设置失效
session.invalidate();
//3.回到index页面
processTemplate("index",req,resp);
}
}
七、业务功能:显示列表
1.流程图
2.创建组件
①创建Java类
package com.sr.maven.servlet.module;
import com.sr.maven.entity.Memorials;
import com.sr.maven.service.MemorialsService;
import com.sr.maven.service.impl.MemorialsServiceImpl;
import com.sr.maven.servlet.base.ModelbaseServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class WorkServlet extends ModelbaseServlet {
private MemorialsService memorialsService = new MemorialsServiceImpl();
protected void showMemorialsDigestList(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//. 1.调用Service方法查询数据
List list = memorialsService.getAllMemorialsDigest();
//将查询到的数据存入请求域
String templateName = "memorials-list";
processTemplate(templateName,req,resp);
}
}
package com.sr.maven.service;
import com.sr.maven.entity.Memorials;
import java.util.List;
public interface MemorialsService {
List getAllMemorialsDigest();
}
package com.sr.maven.service.impl;
import com.sr.maven.dao.api.MemorialsDao;
import com.sr.maven.dao.impl.MemorialsDaoImpl;
import com.sr.maven.entity.Memorials;
import com.sr.maven.service.MemorialsService;
import java.util.List;
public class MemorialsServiceImpl implements MemorialsService {
private MemorialsDao memorialsDao = new MemorialsDaoImpl();
@Override
public List getAllMemorialsDigest() {
return memorialsDao.selectAllMemorialsDigest();
}
}
package com.sr.maven.dao.api;
import com.sr.maven.entity.Memorials;
import java.util.List;
public interface MemorialsDao {
List selectAllMemorialsDigest();
}
package com.sr.maven.dao.impl; import com.sr.maven.dao.baseDao; import com.sr.maven.dao.api.MemorialsDao; import com.sr.maven.entity.Memorials; import java.util.List; public class MemorialsDaoImpl extends baseDao② 注册,在web.xml中增加信息implements MemorialsDao { @Override public List selectAllMemorialsDigest() { String sql = "SELECTn" + "tmemorials_id memorialsId,n" + "tmemorials_title memorialsTitle,n" + "tconcat(left(memorials_content,10),'...') as memorialsContentDigest,n" + "tmemorials_content as memorialsContent,n" + "tmemorials_emp as memorialsEmp,n" + "temp_name as memorialsEmpEmpName,n" + "tmemorials_create_time as memorialsCreateTime,n" + "tfeedback_time as feedbackTime,n" + "tfeedback_content as feedbackContent,n" + "tmemorials_status as memorialsStatusn" + "FROMn" + "tt_memorials mn" + "tleft join t_emp e on m.memorials_emp=e.emp_id"; return super.getBeanList(sql,Memorials.class); } }
workServlet
com.sr.maven.servlet.module.WorkServlet
workServlet
/work
3.页面显示
temp
恭请皇上圣安
给大人请安
退朝
| 奏折标题 | 内容摘要 | 上疏大臣 | 上疏时间 | 奏折状态 | 奏折详情 |
|---|---|---|---|---|---|
| 没有人上过折子 | |||||
| 奏折标题 奏折标题 奏折标题 | 内容摘要 内容摘要 内容摘要 | 上疏大臣 上疏大臣 上疏大臣 | 上疏时间 上疏时间 上疏时间 | 未读 已读 已批示 | 奏折详情 |
将authServlet中登录跳转temp的代码替换为刚才做的列表页
resp.sendRedirect(req.getContextPath()+"/work?method=showMemorialsDigestList");八、业务功能:显示详情 1.详情页代码
temp
恭请皇上圣安
给大人请安
退朝
| 奏折标题 | |
| 上疏大臣 | |
| 上疏时间 | |
| 奏折内容 | |
| 批复时间 | |
| 批复内容 |
protected void memorialsDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1。获取请求参数读取 memorialsId
String memorialsId = req.getParameter("memorialsId");
//2.根据memorialsId查询 对象
Memorials memorials = memorialsService.getMemorialsById(memorialsId);
//更新为 已读
if (memorials.getMemorialsStatus() == 0) {
memorialsService.updateMemorialsStatus(memorialsId, 1);
}
//3.放入请求域
req.setAttribute("memorials", memorials);
//解析渲染页面
String templateName = "memorials-detail";
processTemplate(templateName, req, resp);
}
3.MemorialsService方法
Memorials getMemorialsById(String memorialsId); void updateMemorialsStatus(String memorialsId, int i);4.MemorialsServiceImpl方法增加
@Override
public Memorials getMemorialsById(String memorialsId) {
return memorialsDao.selectMemorialsById(memorialsId);
}
@Override
public void updateMemorialsStatus(String memorialsId, int i) {
memorialsDao.updateMemorialsStatus(memorialsId,i);
}
5.MemorialsDao方法
Memorials selectMemorialsById(String memorialsId);
void updateMemorialsStatus(String memorialsId, int i);
5.MemorialsDaoImpl方法
@Override
public Memorials selectMemorialsById(String memorialsId) {
String sql = "SELECTn" +
"tmemorials_id memorialsId,n" +
"tmemorials_title memorialsTitle,n" +
"tconcat(left(memorials_content,10),'...') as memorialsContentDigest,n" +
"tmemorials_content as memorialsContent,n" +
"tmemorials_emp as memorialsEmp,n" +
"temp_name as memorialsEmpEmpName,n" +
"tmemorials_create_time as memorialsCreateTime,n" +
"tfeedback_time as feedbackTime,n" +
"tfeedback_content as feedbackContent,n" +
"tmemorials_status as memorialsStatusn" +
"FROMn" +
"tt_memorials mn" +
"tleft join t_emp e on m.memorials_emp=e.emp_id where m.memorials_id = ?";
return super.getSingleBean(sql,Memorials.class,memorialsId);
}
@Override
public void updateMemorialsStatus(String memorialsId, int i) {
String sql = "update t_memorials set memorials_status = ? where memorials_id = ?";
super.update(sql,i,memorialsId);
}
6. 结果图展示
九、业务功能:回复
1.WorkServlet方法
protected void feedback(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取回复的内容参数
String content = req.getParameter("feedbackContent");
String memorialsId = req.getParameter("memorialsId");
//更新内容
memorialsService.updateFeedback(memorialsId,content);
//重定向到列表页
resp.sendRedirect(req.getContextPath()+"/work?method=showMemorialsDigestList");
}
2.MemorialsService方法
void updateFeedback(String memorialsId, String content);3.MemorialsServiceImpl方法
@Override
public void updateFeedback(String memorialsId, String content) {
memorialsDao.updateFeedback(memorialsId,content);
}
4.MemorialsDao方法
void updateFeedback(String memorialsId, String content);5.MemorialsDaoImpl方法
@Override
public void updateFeedback(String memorialsId, String content) {
String sql = "update t_memorials set memorials_status = ? ,feedback_content = ?, feedback_time =? where memorials_id = ?";
String currentTime = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
super.update(sql,2,content,currentTime,memorialsId);
}
十、业务功能:登录检查
1.逻辑
2.创建LoginFilter
package com.sr.maven.filter;
import com.sr.maven.util.ImperialCourtConst;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//1.获取HttpSession
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpSession session = request.getSession();
//尝试从Session域获取已登录的对象
Object loginEmp = session.getAttribute(ImperialCourtConst.LOGIN_EMP_ATTR_NAME);
//判断loginEmp是否为空
if(loginEmp != null){
filterChain.doFilter(request,servletResponse);
return;
}
request.setAttribute("systemMessage",ImperialCourtConst.ACCESS_DENIED_MESSAGE);
request.getRequestDispatcher("/").forward(request,servletResponse);
}
@Override
public void destroy() {
}
}
3.配置web.xml
把LoginFilter放在txFilter前面,一个过滤链的操作,代表前后顺序,节省性能
loginFilter
com.sr.maven.filter.LoginFilter
loginFilter
/work
十一、打包部署
1.适配环境信息
记得修改对应的配置文件信息,比如数据库地址等,跟环境有关的信息
2.跳过测试打包mvn clean package -Dmaven.test.skip=true3.上传war包
自己选择上传到Linux或者Windows等,Tomcat对应的webapp包内
4.启动TomcatLinux
tomcat目录/bin/startup.sh
Windows
tomcat目录/bin/startup.bat双击
自行浏览器地址测试



