架构其实就是项目的结构,只是因为架构是一个更大的词,通常来形容比较大规模事物的结构。
单一架构也叫all-in-one的结构,就是所有的代码、配置文件、各种资源都在同一个工程
【1】 到哪找?
依赖信息网站
【2】怎么选择?
tips:
确定技术选型、组件依赖列表,项目划分模块等操作都是属于架构设计的范围。
首先确保自己本机已经安装了mysql数据库,安装过程不再赘述。
mysql依赖
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.28version>
dependency>
druid依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>commons-dbutilsgroupId>
<artifactId>commons-dbutilsartifactId>
<version>1.7version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleafartifactId>
<version>3.0.11.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
<scope>testscope>
dependency>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.28version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
<dependency>
<groupId>commons-dbutilsgroupId>
<artifactId>commons-dbutilsartifactId>
<version>1.7version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleafartifactId>
<version>3.0.11.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
dependencies>
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;
}
}
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=10000
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;
/**
* 功能1:从数据源获取数据库链接
* 功能2:将数据源获取到数据库连接绑定到本地线程 ThreadLocal
* 功能3:释放线程时和本地超线程解除绑定
*/
public class JDBCUtils {
//数据源成员变量,设置为静态资源,保证大对象的单例性,同时保证静态方法中可以访问
private static DataSource dataSource;
//由于ThreadLocal对象需要作为绑定数据时K-v对中的key,所以需要保证唯一性
//加static声明为静态资源可保证唯一性
private static ThreadLocal<Connection> 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);
}
}
}
}
//数据源成员变量,设置为静态资源,保证大对象的单例性,同时保证静态方法中可以访问
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);
}
}
【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对象的功能
方法名 | 功能 |
---|---|
set(T value) | 将数据绑定到当前线程 |
get() | 从当前线程获取已绑定的数据 |
remove | 将数据从当前线程移除 |
【3】Java代码
//由于ThreadLocal对象需要作为绑定数据时K-v对中的key,所以需要保证唯一性
//加static声明为静态资源可保证唯一性
private static ThreadLocal<Connection> 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);
}
}
/**
* BaseDao类:所有Dao类实现类的基类
* @param 实体类的类型
*/
public class BaseDao<T> {
//DBUtils 工具包提供的数据操作对象
private QueryRunner runner = new QueryRunner();
/**
* 查询单个对象
* @param sql sql语句
* @param entityBean 实体类对象
* @param parameters 传给sql语句的参数
* @return 查询到的实体类对象
*/
public T getSingleBean(String sql ,Class<T> 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);
}
}
/**
* 查询多个对象
* @param sql sql语句
* @param entityBean 实体类对象
* @param parameters 传给sql语句的参数
* @return 查询到的实体类对象
*/
public List<T> getBeanList(String sql , Class<T> 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<Emp> 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<Emp> 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);
}
}
public interface EmpDao {
}
EmpDaoImpl
public class EmpDaoImpl extends BaseDao<Emp> implements EmpDao {
}
MemorialsDao
public interface MemorialsDao {
}
MemorialsDaoImpl
public class MemorialsDaoImpl extends BaseDao<Memorials> 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<String> 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() {
}
}
<filter>
<filter-name>txFilterfilter-name>
<filter-class>com.sr.maven.filter.TransactionFilterfilter-class>
filter>
<filter-mapping>
<filter-name>txFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
【1】确保异常回滚
在程序执行的过程中,必须让所有的catch块把编译时异常转成运行时异常抛出,否则,Filter捕获不到就无法回滚。
【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());
}
}
<context-param>
<param-name>view-prefixparam-name>
<param-value>/WEB-INF/pages/param-value>
context-param>
<context-param>
<param-name>view-suffixparam-name>
<param-value>.html/param-value>
context-param>
详细去看官网文档就可以
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);
}
}
}
package com.sr.maven.util;
public class ImperialCourtConst {
private static final String LOGIN_FAIL_MESSAGE = "帐号、密码错误,不可进宫!";
private static final String ACCESS_DENIED_MESSAGE = "宫闱禁地,不可擅入!";
}
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();
}
}
<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%d{HH:mm:ss.SSS}][%-5level][%thread][%logger][%msg]%npattern>
<charset>UTF-8charset>
encoder>
appender>
<root level = "INFO">
<appender-ref ref="STDOUT"/>
root>
<logger name="com.sr" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
logger>
configuration>
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);
}
}
<servlet>
<servlet-name>portalServletservlet-name>
<servlet-class>com.sr.maven.servlet.module.PortalServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>portalServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>乾清宫title>
head>
<body>
<form th:action="@{auth}" method="post">
<input type="hidden" name="method" value="login"/>
<p th:text="${message}">p>
<p th:text="${systemMessage}">p>
帐号:<input type="text" name="loginAccount"/><br/>
帐号:<input type="password" name="loginPassword"/><br/>
<button type="submit">进宫button>
form>
body>
html>
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<Emp> 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);
}
}
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);
}
}
}
}
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>temptitle>
head>
<body>
<p th:text="${session.loginInfo}">p>
body>
html>
<servlet>
<servlet-name>authServletservlet-name>
<servlet-class>com.sr.maven.servlet.module.AuthServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>authServletservlet-name>
<url-pattern>/authurl-pattern>
servlet-mapping>
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>temptitle>
head>
<body>
<p th:text="${session.loginInfo}">p>
<a th:href="@{/auth?method=logout}">退朝a>
body>
html>
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);
}
}
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<Memorials> 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<Memorials> 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<Memorials> getAllMemorialsDigest() {
return memorialsDao.selectAllMemorialsDigest();
}
}
package com.sr.maven.dao.api;
import com.sr.maven.entity.Memorials;
import java.util.List;
public interface MemorialsDao {
List<Memorials> 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<Memorials> implements MemorialsDao {
@Override
public List<Memorials> selectAllMemorialsDigest() {
String sql = "SELECT\n" +
"\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 memorialsStatus\n" +
"FROM\n" +
"\tt_memorials m\n" +
"\tleft join t_emp e on m.memorials_emp=e.emp_id";
return super.getBeanList(sql,Memorials.class);
}
}
<servlet>
<servlet-name>workServletservlet-name>
<servlet-class>com.sr.maven.servlet.module.WorkServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>workServletservlet-name>
<url-pattern>/workurl-pattern>
servlet-mapping>
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>temptitle>
<style type="text/css">
table{
border-collapse: collapse;
margin: 0px auto 0px auto;
}
table th ,td{
border: 1px solid black;
text-align: center;
}
div{
text-align: right;
}
style>
head>
<body>
<div>
<span th:if="${session.loginInfo.empPosition == 'emperor'}">恭请皇上圣安span>
<span th:if="${session.loginInfo.empPosition == 'minister'}">给<span th:text="${session.loginInfo.empName}">span>大人请安span>
<a th:href="@{/auth?method=logout}">退朝a>
div>
<table>
<thead>
<th>奏折标题th>
<th>内容摘要th>
<th>上疏大臣th>
<th>上疏时间th>
<th>奏折状态th>
<th>奏折详情th>
thead>
<tbody th:if="${#lists.isEmpty(memorialsList)}">
<tr>
<td colspan="6">没有人上过折子td>
tr>
tbody>
<tbody th:if="${not #lists.isEmpty(memorialsList)}">
<tr th:each="memorials : ${memorialsList}">
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsTitle}" th:case="0" style="color: red">奏折标题span>
<span th:text="${memorials.memorialsTitle}" th:case="1" style="color: blue">奏折标题span>
<span th:text="${memorials.memorialsTitle}" th:case="2" >奏折标题span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsContentDigest}" th:case="0" style="color: red">内容摘要span>
<span th:text="${memorials.memorialsContentDigest}" th:case="1" style="color: blue">内容摘要span>
<span th:text="${memorials.memorialsContentDigest}" th:case="2" >内容摘要span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsEmpEmpName}" th:case="0" style="color: red">上疏大臣span>
<span th:text="${memorials.memorialsEmpEmpName}" th:case="1" style="color: blue">上疏大臣span>
<span th:text="${memorials.memorialsEmpEmpName}" th:case="2" >上疏大臣span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:text="${memorials.memorialsCreateTime}" th:case="0" style="color: red">上疏时间span>
<span th:text="${memorials.memorialsCreateTime}" th:case="1" style="color: blue">上疏时间span>
<span th:text="${memorials.memorialsCreateTime}" th:case="2" >上疏时间span>
td>
<td th:switch="${memorials.memorialsStatus}">
<span th:case="0" style="color: red">未读span>
<span th:case="1" style="color: blue">已读span>
<span th:case="2" >已批示span>
td>
<td>
<a th:href="@{/work?(method='memorialsDetail',memorialsId=${memorials.memorialsId})}">奏折详情a>
td>
tr>
tbody>
table>
body>
html>
将authServlet中登录跳转temp的代码替换为刚才做的列表页
resp.sendRedirect(req.getContextPath()+"/work?method=showMemorialsDigestList");
DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>temptitle>
<style type="text/css">
table {
border-collapse: collapse;
margin: 0px auto 0px auto;
}
table th, td {
border: 1px solid black;
text-align: center;
}
div {
text-align: center;
}
style>
head>
<body>
<div>
<span th:if="${session.loginInfo.empPosition == 'emperor'}">恭请皇上圣安span>
<span th:if="${session.loginInfo.empPosition == 'minister'}">给<span th:text="${session.loginInfo.empName}">span>大人请安span>
<a th:href="@{/auth?method=logout}">退朝a>
div>
<table>
<tr>
<td>奏折标题td>
<td th:text="${memorials.memorialsTitle}">td>
tr>
<tr>
<td>上疏大臣td>
<td th:text="${memorials.memorialsEmpEmpName}">td>
tr>
<tr>
<td>上疏时间td>
<td th:text="${memorials.memorialsCreateTime}">td>
tr>
<tr>
<td>奏折内容td>
<td th:text="${memorials.memorialsContent}">td>
tr>
<tr th:if="${memorials.memorialsStatus == 2}">
<td>批复时间td>
<td th:text="${memorials.feedbackTime}">td>
tr>
<tr th:if="${memorials.memorialsStatus == 2}">
<td>批复内容td>
<td th:text="${memorials.feedbackContent}">td>
tr>
table>
<div th:if="${memorials.memorialsStatus != 2}">
<form th:action="@{/work}" method="post">
<input type="hidden" name="method" value="feedback">
<input type="hidden" name="memorialsId" th:value="${memorials.memorialsId}">
<textarea name="feedbackContent">
textarea>
<button type="submit">御批button>
form>
div>
<a th:href="@{/work?method=showMemorialsDigestList}">返回列表a>
body>
html>
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);
}
Memorials getMemorialsById(String memorialsId);
void updateMemorialsStatus(String memorialsId, int i);
@Override
public Memorials getMemorialsById(String memorialsId) {
return memorialsDao.selectMemorialsById(memorialsId);
}
@Override
public void updateMemorialsStatus(String memorialsId, int i) {
memorialsDao.updateMemorialsStatus(memorialsId,i);
}
Memorials selectMemorialsById(String memorialsId);
void updateMemorialsStatus(String memorialsId, int i);
@Override
public Memorials selectMemorialsById(String memorialsId) {
String sql = "SELECT\n" +
"\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 memorialsStatus\n" +
"FROM\n" +
"\tt_memorials m\n" +
"\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);
}
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");
}
void updateFeedback(String memorialsId, String content);
@Override
public void updateFeedback(String memorialsId, String content) {
memorialsDao.updateFeedback(memorialsId,content);
}
void updateFeedback(String memorialsId, String content);
@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);
}
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() {
}
}
把LoginFilter放在txFilter前面,一个过滤链的操作,代表前后顺序,节省性能
<filter>
<filter-name>loginFilterfilter-name>
<filter-class>com.sr.maven.filter.LoginFilterfilter-class>
filter>
<filter-mapping>
<filter-name>loginFilterfilter-name>
<url-pattern>/workurl-pattern>
filter-mapping>
记得修改对应的配置文件信息,比如数据库地址等,跟环境有关的信息
mvn clean package -Dmaven.test.skip=true
自己选择上传到Linux或者Windows等,Tomcat对应的webapp包内
Linux
tomcat目录/bin/startup.sh
Windows
tomcat目录/bin/startup.bat双击
自行浏览器地址测试