笔记代码

分割线

MVC 架构

Model 模型 view 视图 Controller 控制 三方分离的架构

  • 之前的架构:

    • Servlet 直接进行 CRUD 操作,程序比较臃肿,不利于维护
    20210409093031

  • 于是,为了解决这种不便利性,再加一层! (没有什么是加一层解决不了的)

    20210409093440

  • 各部分的职责:

    • Model

      • 业务处理 :业务逻辑(Service)

      • 数据持久层:CRUD (Dao - 数据持久化对象)

    • View

      • 展示数据

      • 提供链接发起 Servlet 请求 (a,form,img…)

    • Controller (Servlet)

      • 接收用户的请求 :(req:请求参数、Session 信息….)

      • 交给业务层处理对应的代码

      • 控制视图的跳转 (转发/重定向)


  • 一条流程:

    View 层点击登录 --> 接收用户的登录请求 --> 处理用户的请求(获取用户登录的参数,username,password)–> 交给业务层处理登录业务(判断用户名密码是否正确:操作事务)–> Dao 层查询用户名和密码是否正确 --> 数据库

    顺利查询到数据之后就是反过来再进行数据传递

分割线

中间层工具


Filter 过滤器

介绍

20210409222335

比如处理中文乱码,敏感词汇,登录验证

例子: 比如 Shiro 安全框架技术就是用 Filter 来实现的


导入依赖

  • 老四样:

    <!-- servlet依赖 -->
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
    </dependency>
    <!-- JSP依赖 -->
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.3</version>
    <scope>provided</scope>
    </dependency>
    <!-- JSTL表达式依赖 -->
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    <!-- Standard标签库 -->
    <dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
    </dependency>

  • 之后好像还会用到 SQL 驱动依赖,到时候在说.


filter 例子

  • src/main/java/filter/CharacterEncodingFilter.java

    package filter;

    import javax.servlet.*;
    import java.io.IOException;

    public class CharacterEncodingFilter implements Filter {
    //初始化并等待过滤对象出现
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("CharacterEncodingFilter初始化");
    }

    /*
    1. 过滤中的所有代码,在过滤特定请求的时候都会执行
    2. 必须要让过滤器继续执行 (因为filter可能不止一个,需要传递)
    chain.doFilter(request,response);
    */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");
    response.setContentType("text/html");

    System.out.println("CharacterEncodingFilter执行前....");
    chain.doFilter(request, response); //让我们的请求继续走,如果不写,程序到这里就被拦截停止!
    System.out.println("CharacterEncodingFilter执行后....");
    }

    //销毁:web服务器关闭的时候,过滤器会销毁
    @Override
    public void destroy() {
    System.out.println("CharacterEncodingFilter销毁");
    }
    }

  • src/main/java/servlet/Show.java

    package servlet;

    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 Show extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 通过filter实现处理乱码
    resp.getWriter().write("你好!");
    }
    }

  • web.xml 配置 servlet 和 filter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>show</servlet-name>
<servlet-class>servlet.Show</servlet-class>
</servlet>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>filter.CharacterEncodingFilter</filter-class>
</filter>

<!--还是乱码的页面-->
<servlet-mapping>
<servlet-name>show</servlet-name>
<url-pattern>/show</url-pattern>
</servlet-mapping>
<!--通过过滤器解决了乱码的页面-->
<servlet-mapping>
<servlet-name>show</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<!--filter的地址映射-->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
</web-app>

分割线

监听器

  • 对应不同业务需求,存在 N 中不同的监听器接口可供实现.
  • 监听器经常在 GUI 编程中使用.
  • 例子: 设置 session 监听器监听有多少 session 连接

  • src/main/java/listener/OnlineCountListener.java

    package listener;

    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;

    //统计网站在线人数 -> 统计session
    public class OnlineCountListener implements HttpSessionListener {
    private ServletContext ctx;
    private Integer onlineCount;

    //一旦创建Session就会触发一次这个事件
    public void sessionCreated(HttpSessionEvent se) {
    initParam(se);
    ctx.setAttribute("OnlineCount", ++onlineCount);
    }

    /*
    一旦销毁Session就会触发一次这个事件
    Session销毁:
    1. 手动销毁 getSession().invalidate();
    2. 自动销毁
    */
    public void sessionDestroyed(HttpSessionEvent se) {
    initParam(se);
    ctx.setAttribute("OnlineCount", --onlineCount);
    }

    public void initParam(HttpSessionEvent se) {
    ctx = se.getSession().getServletContext();
    onlineCount = (Integer) ctx.getAttribute("OnlineCount");

    if (onlineCount == null) {
    ctx.setAttribute("OnlineCount", 0);
    }
    }
    }

  • index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>在线人数测试</title>
    </head>
    <body>
    <h1>当前有 <span
    style="color: turquoise;"><%=this.getServletConfig().getServletContext().getAttribute("OnlineCount")%></span> 人在线
    </h1>
    </body>
    </html>

  • 注册监听器

    <!--注册监听器-->
    <listener>
    <listener-class>listener.OnlineCountListener</listener-class>
    </listener>

GUI 编程例子

  • GUI 编程常会用到监听器,这里简单介绍一下 : src/main/java/listener/TestPanel.java

    package listener;

    import java.awt.*;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;

    public class TestPanel {
    public static void main(String[] args) {
    Frame frame = new Frame("Hello World!"); //新建一个窗体
    Panel panel = new Panel(null); //面板
    frame.setLayout(null); //设置窗体的布局

    frame.setBounds(300, 300, 500, 500);
    frame.setBackground(new Color(0, 0, 255)); //设置背景颜色

    panel.setBounds(50, 50, 300, 300);
    panel.setBackground(new Color(0, 255, 0)); //设置背景颜色

    frame.add(panel);

    frame.setVisible(true);

    //监听事件,监听关闭事件
    frame.addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosing(WindowEvent e) {
    System.exit(0);
    }
    });
    }
    }

登录验证 demo

  • 项目结构,如图:

    20210411004248
  • JSP 页面

    • 主页(登录页面): web/demo/index.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
      <title>主页</title>
      </head>
      <body>
      <h1>登录</h1>
      <form action="${pageContext.request.contextPath}/demo/login" method="post">
      <label>
      用户名:
      <input type="text" name="username"/>
      </label>
      <input type="submit" value="登录">
      </form>
      </body>
      </html>

    • 登陆失败: web/demo/failed.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
      <title>登录失败</title>
      </head>
      <body>
      <h1>登录失败进入的页面</h1>

      <a href="${pageContext.request.contextPath}/demo/index.jsp">返回主页</a>
      </body>
      </html>

    • 登录成功: web/demo/sys/success.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
      <title>已登录</title>
      </head>
      <body>
      <h1>登录成功后进入的页面</h1>

      <hr>

      <a href="${pageContext.request.contextPath}/demo/logout">注销</a>
      </body>
      </html>

  • Java 后端

    • login: src/main/java/demo/servlet/Login.java

      package demo.servlet;

      import demo.utils.Constants;

      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      import java.io.IOException;

      public class Login extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
      this.doPost(req, resp);
      }

      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
      String username = req.getParameter("username");
      HttpSession session = req.getSession();

      if (username.equals("admin")) {
      session.setAttribute(Constants.USER_SESSION, session.getId());
      resp.sendRedirect("/demo/sys/success.jsp");
      } else {
      resp.sendRedirect("/demo/failed.jsp");
      }
      }
      }

    • 注销: src/main/java/demo/servlet/Logout.java

      package demo.servlet;

      import demo.utils.Constants;

      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import javax.servlet.http.HttpSession;
      import java.io.IOException;

      public class Logout extends HttpServlet {
      @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 {

      HttpSession session = req.getSession();
      Object attribute = session.getAttribute(Constants.USER_SESSION);

      /*
      频繁操作session连接会影响性能,通过attribute间接验证登录信息
      通过设置或移除 USER_SESSION 属性来验证是否登录
      存在一个Bug: 在注销状态通过浏览器的后退,可以返回登陆成功的页面(且不存在USER_SESSION)
      */
      if (attribute != null) {
      session.removeAttribute(Constants.USER_SESSION);
      resp.sendRedirect("/demo/index.jsp");
      }
      }

    • 提取的常量工具类: src/main/java/demo/utils/Constants.java

      package demo.utils;

      /**
      * 此类内定义一些其他各种地方需要经常调用的常量
      */
      public class Constants {
      public final static String USER_SESSION = "USER_SESSION";
      }

    • 过滤器类: src/main/java/demo/filter/SysFilter.java

      package demo.filter;

      import demo.utils.Constants;

      import javax.servlet.*;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;

      public class SysFilter implements Filter {
      @Override
      public void init(FilterConfig filterConfig) throws ServletException {

      }

      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest req = (HttpServletRequest) request;
      HttpServletResponse resp = (HttpServletResponse) response;

      Object attribute = req.getSession().getAttribute(Constants.USER_SESSION);
      if (attribute == null) {
      resp.sendRedirect("/demo/failed.jsp");
      }

      chain.doFilter(request, response);
      }

      @Override
      public void destroy() {

      }
      }

  • Web.xml

    <servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>demo.servlet.Login</servlet-class>
    </servlet>
    <servlet>
    <servlet-name>logout</servlet-name>
    <servlet-class>demo.servlet.Logout</servlet-class>
    </servlet>
    <filter>
    <filter-name>sysFilter</filter-name>
    <filter-class>demo.filter.SysFilter</filter-class>
    </filter>

    <servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/demo/login</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>logout</servlet-name>
    <url-pattern>/demo/logout</url-pattern>
    </servlet-mapping>
    <filter-mapping>
    <filter-name>sysFilter</filter-name>
    <url-pattern>/demo/sys/*</url-pattern>
    </filter-mapping>