第三节 搭建环境:事务控制

1、总体思路

单层架构系统概要设计_java

2、TransactionFilter

①创建 Filter 类

单层架构系统概要设计_java_02

②TransactionFilter 完整代码
public class TransactionFilter implements Filter {
    // 声明集合保存静态资源扩展名
    private static Set<String> staticResourceExtNameSet;
    static {
        staticResourceExtNameSet = new HashSet<>();
        staticResourceExtNameSet.add(".png");
        staticResourceExtNameSet.add(".jpg");
        staticResourceExtNameSet.add(".css");
        staticResourceExtNameSet.add(".js");
    }
    @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 (staticResourceExtNameSet.contains(extName)) {
                // 如果检测到当前请求确实是静态资源,则直接放行,不做事务操作
                filterChain.doFilter(servletRequest, servletResponse);
                // 当前方法立即返回
                return ;
            }
        }

        Connection connection = null;

        try{

            // 1、获取数据库连接
            connection = JDBCUtils.getConnection();

            // 重要操作:关闭自动提交功能
            connection.setAutoCommit(false);

            // 2、核心操作
            filterChain.doFilter(servletRequest, servletResponse);

            // 3、提交事务
            connection.commit();

        }catch (Exception e) {

            try {
                // 4、回滚事务
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }

            // 页面显示:将这里捕获到的异常发送到指定页面显示
            // 获取异常信息
            String message = e.getMessage();

            // 将异常信息存入请求域
            request.setAttribute("systemMessage", message);

            // 将请求转发到指定页面
            request.getRequestDispatcher("/").forward(request, servletResponse);

        }finally {

            // 5、释放数据库连接
            JDBCUtils.releaseConnection(connection);
        }
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void destroy() {}
}
③配置 web.xmlx

注意:需要首先将当前工程改成 Web 工程。

单层架构系统概要设计_html_03

<filter>
    <filter-name>txFilter</filter-name>
    <filter-class>com.atguigu.imperial.court.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>txFilter</filter-name>
    <url-pattern>/*</url-pattern> 
</filter-mapping>
④注意点
[1]确保异常回滚

在程序执行的过程中,必须让所有 catch 块都把编译时异常转换为运行时异常抛出;如果不这么做,在 TransactionFilter 中 catch 就无法捕获到底层抛出的异常,那么该回滚的时候就无法回滚。

[2]谨防数据库连接提前释放

由于诸多操作都是在使用同一个数据库连接,那么中间任何一个环节释放数据库连接都会导致后续操作无法正常完成。

第四节 搭建环境:表述层

1、视图模板技术 Thymeleaf

①服务器端渲染

参考资料(opens new window)

单层架构系统概要设计_架构_04

②Thymeleaf 简要工作机制
[1]初始化阶段
  • 目标:创建 TemplateEngine 对象
  • 封装:因为对每一个请求来说,TemplateEngine 对象使用的都是同一个,所以在初始化阶段准备好
[2]请求处理阶段

单层架构系统概要设计_单层架构系统概要设计_05

③逻辑视图与物理视图

假设有下列页面地址:

/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
这样的地址可以直接访问到页面本身,我们称之为:物理视图。而将物理视图中前面、后面的固定内容抽取出来,让每次请求指定中间变化部分即可,那么中间变化部分就叫:逻辑视图

单层架构系统概要设计_html_06

④ViewBaseServlet 完整代码

为了简化视图页面处理过程,我们将 Thymeleaf 模板引擎的初始化和请求处理过程封装到一个 Servlet 基类中:ViewBaseServlet。以后负责具体模块业务功能的 Servlet 继承该基类即可直接使用。

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);

        // 3.给解析器对象设置参数
        // ①HTML是默认模式,明确设置是为了代码更容易理解
        templateResolver.setTemplateMode(TemplateMode.HTML);

        // ②设置前缀
        String viewPrefix = servletContext.getInitParameter("view-prefix");

        templateResolver.setPrefix(viewPrefix);

        // ③设置后缀
        String viewSuffix = servletContext.getInitParameter("view-suffix");

        templateResolver.setSuffix(viewSuffix);

        // ④设置缓存过期时间(毫秒)
        templateResolver.setCacheTTLMs(60000L);

        // ⑤设置是否缓存
        templateResolver.setCacheable(true);

        // ⑥设置服务器端编码方式
        templateResolver.setCharacterEncoding("utf-8");

        // 4.创建模板引擎对象
        templateEngine = new TemplateEngine();

        // 5.给模板引擎对象设置模板解析器
        templateEngine.setTemplateResolver(templateResolver);

    }

    protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1.设置响应体内容类型和字符集
        resp.setContentType("text/html;charset=UTF-8");

        // 2.创建WebContext对象
        WebContext webContext = new WebContext(req, resp, getServletContext());

        // 3.处理模板数据
        templateEngine.process(templateName, webContext, resp.getWriter());
    }
}

特别提醒:这个类不需要掌握,因为以后都被框架封装了,我们现在只是暂时用一下。

单层架构系统概要设计_java_07

⑤声明初始化参数

单层架构系统概要设计_html_08

<!-- 配置 Web 应用初始化参数指定视图前缀、后缀 -->
<!-- 
    物理视图举例:/WEB-INF/pages/index.html
    对应逻辑视图:index
-->                                                                                                                                                                                                                                                                                                                                                                                                                
<context-param>
    <param-name>view-prefix</param-name>
    <param-value>/WEB-INF/pages/</param-value>
</context-param>
<context-param>
    <param-name>view-suffix</param-name>
    <param-value>.html</param-value>
</context-param>
⑥Thymeleaf 的页面语法

第八章

2、ModelBaseServlet

①提出问题
[1]我们的需求

单层架构系统概要设计_servlet_09

[2]HttpServlet 的局限
  • doGet() 方法:处理 GET 请求
  • doPost() 方法:处理 POST 请求
②解决方案
  • 每个请求附带一个请求参数,表明自己要调用的目标方法
  • Servlet 根据目标方法名通过反射调用目标方法
③ModelBaseServlet 完整代码

单层架构系统概要设计_java_10

特别提醒:为了配合 TransactionFilter 实现事务控制,捕获的异常必须抛出。

public class ModelBaseServlet extends ViewBaseServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 在doGet()方法中调用doPost()方法,这样就可以在doPost()方法中集中处理所有请求
        doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 1.在所有request.getParameter()前面设置解析请求体的字符集
        request.setCharacterEncoding("UTF-8");

        // 2.从请求参数中获取method对应的数据
        String method = request.getParameter("method");

        // 3.通过反射调用method对应的方法
        // ①获取Class对象
        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, request, response);
        } catch (Exception e) {
            e.printStackTrace();

            // 重要提醒:为了配合 TransactionFilter 实现事务控制,捕获的异常必须抛出。
            throw new RuntimeException(e);
        }
    }
}
④继承关系

单层架构系统概要设计_html_11