持续学习&持续更新中…
学习态度:守破离
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技术基石.
本文完,感谢您的关注支持!