【Java从零到架构师第二季】【03】Servlet_JSP


持续学习&持续更新中…

学习态度:守破离


Servlet

什么是Servlet

在这里插入图片描述

service方法

当一个Servlet接收到请求的时候,首先会调用service方法,然后由service方法根据请求方式来分发请求至doGet或者doPost。

也就是说,service方法无论是收到get或者收到post请求都会被调用。而doGet是专门来处理get请求的,doPost是专门来处理post请求的。开发中,如果不需要区分请求是get或者post,想统一处理的话,使用service方法即可。



@WebServlet("/test")
public class TestServlet extends HttpServlet {
    
    

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //如果重写service,那么doGet和doPost不会被调用。因为是HttpServlet在其service方法中分发请求的
        System.out.println(this + "_service被调用");
    }
    
}


init、destroy生命周期方法

默认情况下,Servlet对象是在第一次被请求的时候创建的,程序中只有一个实例存在,以后就再也不会创建了。

在这里插入图片描述

注解:

// @WebServlet("/website/*")
@WebServlet(value = "/website/*", loadOnStartup = 1)
public class WebsiteServlet extends BaseServlet<Website> {
    
    
}

XML:

    <!-- 配置Servlet -->
    <servlet>
        <servlet-name>ContactServlet</servlet-name>
        <servlet-class>com.mj.xr.servlet.ContactServlet</servlet-class>
        <!-- <load-on-startup>-1</load-on-startup> -->
        <!-- <load-on-startup>0</load-on-startup> -->
        <!-- <load-on-startup>1</load-on-startup> -->
    </servlet>

    <servlet-mapping>
        <servlet-name>ContactServlet</servlet-name>
        <url-pattern>/contact/*</url-pattern>
    </servlet-mapping>

乱码问题解决

在这里插入图片描述

  • Tomcat8之前get请求使用request.setCharacterEncoding("UTF-8")还是会有乱码问题(post请求不会),解决方法见Maven_安装_配置_常用操作 + IDEA与Maven

  • 在TOMCAT_HOME/conf/web.xml搜索mime-type即可快速定位至查看MIME-TYPE的地方。想要看哪种类型文件对应的MIME-TYPE,ctrl+f搜索即可。

    <mime-mapping>
        <extension>html</extension> <!-- 文件扩展名 -->
        <mime-type>text/html</mime-type> <!-- 该文件类型所对应的MIME-TYPE类型 -->
    </mime-mapping>

ServletContext

在这里插入图片描述

在这里插入图片描述

Servlet的一些细节

在这里插入图片描述

为什么不需要关闭流?

        PrintWriter writer = resp.getWriter();

1、因为这是你通过resp对象get到的别人创建的PrintWriter对象,该对象不是自己创建的,不需要自己负责任。

2、不要随意更改别人的代码,因为有可能出问题。做好自己的本职工作即可。

3、Servlet会自动关闭该流。

通过注解设置Servlet对应的请求路径

1、一个Servlet对应一个请求路径:

@WebServlet("/path_single")
public class PathTestServlet extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println(this + "_service 被调用_" + req.getServletPath());
    }
}

2、一个Servlet被多个请求路径对应:

@WebServlet({
    
    "/path_multi_01", "/path_multi_02", "/path_multi_03"})
public class PathTestServlet extends HttpServlet {
    
    

    public PathTestServlet() {
    
    
        System.out.println(this + "_构造方法");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println(this + "_service 被调用_" + req.getServletPath());
    }
    
}

输出:

coder.lp.two_javaeestart.PathTestServlet@609f335e_构造方法
coder.lp.two_javaeestart.PathTestServlet@609f335e_service 被调用_/path_multi_01
coder.lp.two_javaeestart.PathTestServlet@609f335e_service 被调用_/path_multi_01
coder.lp.two_javaeestart.PathTestServlet@609f335e_service 被调用_/path_multi_02
coder.lp.two_javaeestart.PathTestServlet@609f335e_service 被调用_/path_multi_03

可以看出,一个Servlet只会被服务器创建一次,只存在一个实例。

注意:Servlet并没有设计成单例模式。

单例模式:单例模式是指某个类只能创建(存在)一个实例。

Servlet处理请求的常见过程

在这里插入图片描述

例子:Servlet响应请求的写法

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        String username = req.getParameter("username");
        String userpswd = req.getParameter("userpswd");

		System.out.println("username : " + username + " , userpwd : " + userpswd);

        PrintWriter writer = resp.getWriter();
        if ("123".equals(username) && "123".equals(userpswd)) {
    
    
            successful(writer);
        } else {
    
    
            failed(writer);
        }
    }

    private void successful(PrintWriter writer) {
    
    
        writer.write("<h1 style=\"color: blue; border: 2px solid black\">登录成功</h1>\n");
        writer.write(getSuccessfulDataString());
    }
    private void failed(PrintWriter writer) {
    
    
        writer.write("<h1 style=\"color: red; border: 2px solid black\">登录失败</h1> ");
        writer.write("<a href=\"http://localhost:8080/three_crm/login.html\">返回首页</a>");
    }

    private String getSuccessfulDataString() {
    
    

        final List<Customer> list = getDataList();

        Customer customer;
        StringBuilder sb = new StringBuilder();
        sb.append("<table>\n" +
                "    <thead>\n" +
                "    <th>姓名</th>\n" +
                "    <th>性别</th>\n" +
                "    <th>电话</th>\n" +
                "    </thead>\n" +
                "    <tbody>\n");
        for (int i = 0; i < list.size(); i++) {
    
    
            customer = list.get(i);
            sb.append("<tr> <td >");
            sb.append(customer.getName());
            sb.append("</td > <td >");
            sb.append(customer.getSex());
            sb.append("</td > <td >");
            sb.append(customer.getPhone());
            sb.append("</td > </tr>");
        }
        sb.append("    </tbody>\n" +
                "</table>");
        return sb.toString();
    }

    private List<Customer> getDataList() {
    
    
        List<Customer> list = new ArrayList<>();

		// 读取数据
        // for (int i = 0; i < 10; i++) {
    
    
        //     list.add(new Customer("张三" + i, "1211531125" + i, (((i & 1) == 1)) ? "男" : "女"));
        // }

		// 数据应该是从数据库读取的

        return list;
    }




    private void success(PrintWriter out) {
    
    
        out.write("<html>");
        out.write("<head>");
        out.write("<link rel=\"stylesheet\" href=\"http://localhost:8080/crm/login.css\">");
        out.write("</head>");
        out.write("<body>");
        out.write("<h1 style=\"color: blue; border: 1px solid black;\">登录成功</h1>");
        out.write("<table>");
        out.write("<thead>");
        out.write("<tr>");
        out.write("<th>姓名</th>");
        out.write("<th>电话</th>");
        out.write("<th>性别</th>");
        out.write("</tr>");
        out.write("</thead>");
        out.write("<tbody>");

        List<Customer> customers = getCustomers();
        for (Customer customer : customers) {
    
    
            out.write("<tr>");
            out.write("<td>" + customer.getName() + "</td>");
            out.write("<td>" + customer.getPhone() + "</td>");
            out.write("<td>" + customer.getSex() + "</td>");
            out.write("</tr>");
        }

        out.write("</tbody>");
        out.write("</table>");
        out.write("</body>");
        out.write("</html>");
    }




可以看到,有大量的字符串拼接代码,特别冗余和恶心。

这样是不是很繁琐?很辛苦?于是相对于Servlet,更新的技术JSP出现了。

JSP

使用Servlet返回网页给客户端,会存在大量可读性差,难以维护的字符串拼接代码。

比如说我们现在需要返回一个页面给浏览器,这个页面需要引入js和css,这时我们有两种做法:

  • 1 将css、js复制到html文件中,然后拼接字符串输出。

  • 2 将前端的css、js文件copy到我们的后端项目中,然后字符串拼接html的link标签、script标签,通过拼接html将css、js引入。

不管怎么做都会有一大堆的可读性差、难以维护的字符串拼接代码。

为了解决这个问题,于是相对于Servlet,更新的技术JSP出现了。

什么是JSP

在这里插入图片描述

注意:

HTML中本身就有注释<!-- 注释内容 -->,该注释不能注释包含Java的内容。

而JSP的注释<%-- 注释内容 --%>就可以注释HTML,也可以注释Java。

<% %>中写的是Java内容,因此可以使用//或者/* */

JSP的本质

JSP的本质是Servlet

JSP就是将.jsp编译为Servlet(.jsp中的内容会被直接copy并进行一些处理),然后写出对应的HTML给浏览器。也就是说,JSP帮助我们实现了手写Servlet中out.writer()步骤。

JSP会被编译为Servlet,然后通过生成的Servlet(在Tomcat启动时,打印输出的日志中找到CATALINA_BASE,然后复制后面的路径,从文件夹中打开该路径,就可以看到生成的Servlet Java文件)帮助我们将JSP中的内容通过PrintWriter write出去。

自动生成的Servlet代码片段:

     out = pageContext.getOut();
      _jspx_out = out;

      out.write("\n");
      out.write("<!DOCTYPE html>\n");
      out.write("<html>\n");
      out.write("<head>\n");
      out.write("    <title>JSP-start</title>\n");
      out.write("</head>\n");
      out.write("<body>\n");
      out.write("\n");
      out.write("<p>JSP - start</p>\n");
      out.write("\n");

    for (int i = 0; i < 10; i++) {
    
    
        out.print("hhhh\n");
    }

      out.write("\n");
      out.write("\n");
      out.write("</body>\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
    
    

JSP中的对象

.jsp文件会被编译为Servlet,也就是说会变为.java文件。

既然JSP的本质是Servlet,那么,jsp就会有内置对象。(在编译为.java文件的过程中,.jsp文件中的内容会被直接copy过去然后进行一些处理,因此,我们可以直接使用Servlet所拥有的的对象。)

比如说:我们就可以直接使用Servlet的request和response对象。

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>JSP-start</title>
</head>
<body>

<p>JSP - start</p>

<%
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");

    response.getWriter().write(request.getParameter("xxxx"));
%>

</body>
</html>

JSP的9大内置对象

在这里插入图片描述


  • 利用pageContext共享、存储数据的共享范围:当前jsp页面

  • 利用request共享、存储数据的共享范围:一次完整的请求内

  • 利用session共享、存储数据的共享范围:同一个客户端发送的多个请求之间(一次会话内)

  • 利用application共享、存储数据的共享范围同一个项目下的多个客户端之间(只要项目不被取消部署)


Tomcat启动后,控制台会打印输出Using CATALINA_BASE,复制其后面的路径,然后在文件夹中打开,就可以找到jsp文件解析成的Servlet文件,其中有个service方法,里面就定义了8大内置对象:

  public void _jspService(final javax.servlet.http.HttpServletRequest **request**, final javax.servlet.http.HttpServletResponse **response**)
      throws java.io.IOException, javax.servlet.ServletException {
    
    
// ...
    final javax.servlet.jsp.PageContext **pageContext**;
    javax.servlet.http.HttpSession **session** = null;
    final javax.servlet.ServletContext **application**;
    final javax.servlet.ServletConfig **config**;
    javax.servlet.jsp.JspWriter **out** = null;
    final java.lang.Object **page** = this;
// ...

只有jsp页面中写了<%@ page isErrorPage="true" %>才会有exception对象

    java.lang.Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request);
    if (exception != null) {
    
    
      response.setStatus(javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }

JSP处理请求的常见过程

在这里插入图片描述

例子:直接使用JSP响应请求的写法:

<%@ page import="java.util.ArrayList" %>
<%@ page import="coder.lp.four_jsp_start.Customer" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>login</title>
</head>
<body>
<%!

    private String getSuccessfulDataString() {
    
    

        final List<Customer> list = getDataList();

        Customer customer;
        StringBuilder sb = new StringBuilder();
        sb.append("<table>\n" +
                "    <thead>\n" +
                "    <th>姓名</th>\n" +
                "    <th>性别</th>\n" +
                "    <th>电话</th>\n" +
                "    </thead>\n" +
                "    <tbody>\n");
        for (int i = 0; i < list.size(); i++) {
    
    
            customer = list.get(i);
            sb.append("<tr> <td >");
            sb.append(customer.getName());
            sb.append("</td > <td >");
            sb.append(customer.getSex());
            sb.append("</td > <td >");
            sb.append(customer.getPhone());
            sb.append("</td > </tr>");
        }
        sb.append("    </tbody>\n" +
                "</table>");
        return sb.toString();
    }

    private List<Customer> getDataList() {
    
    
        List<Customer> list = new ArrayList<>();

        // 读取数据
        // for (int i = 0; i < 10; i++) {
    
    
        //     list.add(new Customer("张三" + i, "1211531125" + i, (((i & 1) == 1)) ? "男" : "女"));
        // }

        // 数据应该是从数据库读取的
        
        return list;
    }
%>

<%
    request.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");

    String username = request.getParameter("username");
    String userpswd = request.getParameter("userpswd");

    System.out.println("username : " + username + " , userpwd : " + userpswd);

    if ("123".equals(username) && "123".equals(userpswd)) {
    
    
%>
        <h1 style="color: blue; border: 2px solid black">登录成功</h1>
        <%=getSuccessfulDataString()%>
<%  } else {
    
    
%>
        <h1 style="color: red; border: 2px solid black">登录失败</h1>
        <a href="http://localhost:8080/jspstart/login.html">返回首页</a>
<%
    }
%>

</body>
</html>

直接使用JSP的弊端

通过上述例子可以看出直接使用JSP有很大的弊端和不足:

在这里插入图片描述

  • 如果只使用Servlet响应请求时,都是Java代码,只有拼接HTML字符串的麻烦。

  • 而直接使用JSP的话,JSP中Java和HTML代码混合在一起,更加的不可维护、可读性差了。

  • 所以可以得出一个结论:直接使用JSP还不如直接使用Servlet来的方便、效率高。

那么怎么改进JSP呢?

请看下一篇文章。

参考

李明杰: Java从0到架构师②JavaEE技术基石.


本文完,感谢您的关注支持!


猜你喜欢

转载自blog.csdn.net/weixin_44018671/article/details/120688363
今日推荐