简述
服务器的资源可以分为静态资源和动态资源。静态资源是指访问同一个页面时所展现的内容没有发生变化,动态资源就是指同一页面展现的内容发生了变化。
Servlet的几种实现方式
手写一个Servlet应用
用IDEA方式实现一个Servlet应用
方式一,继承GenericServlet,并在web.xml里配置
//IDEA方式一,继承GenericServlet,并在web.xml里配置
public class SecondServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello");
servletResponse.getWriter().println("second");
}
}
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>com.simon.SecondServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>/second</url-pattern>
</servlet-mapping>
方式二,HttpServlet,并在web.xml里配置
//IDEA方式二,HttpServlet,并在web.xml里配置
public class ThirdServlet extends HttpServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello,http");
servletResponse.getWriter().println("third...");
}
}
<servlet>
<servlet-name>servlet2</servlet-name>
<servlet-class>com.simon.ThirdServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<url-pattern>/third</url-pattern>
</servlet-mapping>
方式三,继承HttpServlet,并在@WebServlet里配置
//IDEA方式三,继承HttpServlet,并在@WebServlet里配置
@WebServlet(name = "servlet4",urlPatterns = "/fourth")
public class FourthServlet extends HttpServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello,http");
servletResponse.getWriter().println("fourth");
}
}
方式三进化版,继承HttpServlet,并在@WebServlet里配置,注解里参数可简写
//IDEA方式三,继承HttpServlet,并在@WebServlet里配置,注解里参数可简写
@WebServlet("/fifth")
public class FifthServlet extends HttpServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("hello,http");
servletResponse.getWriter().println("fifth");
}
}
Tomcat与IDEA的关联方式
当我们在IDEA中,将一个写好的应用放置于Tomcat中后,IDEA与Tomcat是这样关联的:
首先,在IDEA的Tomcat Catalina Log里找到CATALINA_BASE,得到的这个目录,显示如下:
这个与Tomcat的解压缩目录高度雷同,其实就是因为IDEA复制了Tomcat的配置文件,据此自己生成了一个有类似功能的Tomcat。打开conf\Catalina\localhost\app.xml,其实可以看到如下内容:
而上图的docBase,在文件中的情况如下:
我们自己的开发环境的目录如下:
其实可以发现,IDEA在整个过程中是这样的情况:
IDEA从Tomcat那里复制一个IDEA版本的Tomcat来运行我们的应用,conf中app.xml的docBase指向的目录其实就是部署根目录,即部署根目录里WEB-INF和静态资源文件都是部署在这个目录中的。
这是IDEA版的Tomcat部署根目录,其组成其实是许多不同的资源或应用。
web根目录里包括WEB-INF、1.html和index.jsp,其中WEB-INF和index.jsp是从创建应用就有的,而1.html是自己创建的,创建之后临时放在web目录里,但最终作为静态资源会被放到部署根目录里;而开发环境的众多src源代码经过编译之后,在编译路径里有一份,可以在project compiler output路径或者Moduls的paths的Compiler output里找到,与此同时还有一份.class文件是要放到部署根目录的classes里的。
部署描述符是IDEA帮我们创建的web.xml文件,不需要我们手动创建。
Web Resource Directories是将我们开发环境下的web目录的所有文件在编译之后放到部署根目录中。
项目在编译完之后会打包成一个war格式的文件,部署到根目录之后,Tomcat会将这些war包展开,是为war exploded,形成解压形式的文件。这里是将war exploded作一个映射,servlet:war exploded就是整个输出路径(其就是部署根目录)的变量名。Output Layout就是输出的排布:classes里面的‘servlet’ compile output 其实就是Moduls的编译路径,即modules的.class文件复制到了部署根目录的classes目录下;‘servlet’modules:‘Web’ facet resources是指开发环境的web里所有资源会被同步到部署根目录的响应资源文件的位置上,比如web.xml和1.html、index.jsp等。
到此为止,部署根目录的所有文件或者应用的来源都搞清楚了,即:
- WEB-INF的classes来源于Modules的编译输出的.class文件;
- WEB-INF的web.xml来源于Facets的新创建的Web的部署描述符对应的路径,IDEA一开始就创建好的;
- 1.html和index.jsp是静态资源文件,来源于要么我们在web的自建的一个1.html,要么创建web应用时就包含的index.jsp。
将一个SE项目改造成EE项目
初始状态
改造过程
展示效果
生命周期
介绍
servlet从出生到死亡就三个时期,servlet实例化、业务处理和销毁方法,其中实例化和销毁在一个servlet的生命周期只能执行一次,而service方法可以执行多次。
package com.simon;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/life")
public class LifeServlet extends HttpServlet {
int count = 0;
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("servlet init...");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
count++;
System.out.println("servlet post..");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
count++;
System.out.println("servlet get..");
}
@Override
public void destroy() {
System.out.println("访问次数:"+count);
System.out.println("servlet destory...");
}
}
运行效果如下:
提前加载的问题
有时候希望初始化能在请求来到之前,这样使得servlet的容错性更强,即更能及早地发现问题。参数是load-on-startup这里有2种方式,一种是配置文件,一种是注解形式。
配置文件
<servlet>
<servlet-name>Life</servlet-name>
<servlet-class>com.simon.LifeServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Life</servlet-name>
<url-pattern>/life</url-pattern>
</servlet-mapping>
注解
效果显示
关于url-partten
简述
url-partten是指浏览器过来的请求所要访问的实际应用名的映射,需要知道的是,一个servlet可以对应多个url-partten,但是不同servlet不能对应任何一个相同的url-partten。url-partten的写法有2种,一种是/开头,一种是*.开头,当一个请求附带url-partten来的时候,总是优先级最高的servlet去接收它,而这个优先级,以下面这个例子说明:
普通url-partten
假设有4个servlet和4个请求,每一个请求来都会有一个servlet来接收它:
- Servlet1 映射到 /abc/*
- Servlet2 映射到 /*
- Servlet3 映射到 /abc
Servlet4 映射到 *.do
public class UrlPartten1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("/abc/*");
}
}
public class UrlPartten2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("/*");
}
}
public class UrlPartten3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("/abc");
}
}
public class UrlPartten4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("*.do");
}
}
<servlet>
<servlet-name>urlpartten1</servlet-name>
<servlet-class>com.simon.urlpartten.UrlPartten1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>urlpartten1</servlet-name>
<url-pattern>/abc/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>urlpartten2</servlet-name>
<servlet-class>com.simon.urlpartten.UrlPartten2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>urlpartten2</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>urlpartten3</servlet-name>
<servlet-class>com.simon.urlpartten.UrlPartten3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>urlpartten3</servlet-name>
<url-pattern>/abc</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>urlpartten4</servlet-name>
<servlet-class>com.simon.urlpartten.UrlPartten4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>urlpartten4</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
通过试验可以证明:①/开头的url-partten优先级高于开头的,即开头的请求总是会被/*对应的servlet接收到。 ②统一是/开头的url-partten,匹配程度越高,优先级越高。
特殊url-partten
public class SpecialUrpartten1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("/*");
}
}
<servlet>
<servlet-name>special1</servlet-name>
<servlet-class>com.simon.specialUrlpartten.SpecialUrpartten1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>special1</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
public class SpecialUrpartten2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("/");
}
}
<servlet>
<servlet-name>special2</servlet-name>
<servlet-class>com.simon.specialUrlpartten.SpecialUrpartten2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>special2</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
当一开始发起1.html请求和index.jsp请求的时候,都是/*对应的servlet在接收,如下图:
将/注释之后,相同条件下,idnex.jsp却不会被/接收,而是输出自己的内容。如下图:
这是因为没有servlet可以处理/1.html请求,这个时候正常应该交给缺省servlet,但是你的应用中又一个 /,表示你重新实现了一个新的缺省servlet,系统默认提供的就不会再给你使用了,所以最终显示出来 /
缺省Servlet的本质:*不管你去请求动态资源还是去请求静态资源html、jpg、png等,最终都会有一个servlet来处理你的请求。
请求的执行流程 HTTP
以访问http://localhost/app/servlet1 http://localhost/app/form.html
1.浏览器生成一个HTTP请求报文,传到目的机器之后
2.被监听着80端口号的Connector接收到,将请求报文解析成为Request对象,同时还提供一个response对象
3.这两个对象被传给engine,engine挑选合适的host将对象进行进一步传递
4.host去挑选一个叫做app的 Context,将这两个对象传给Context
5.请求是/servlet1或者/form.html,请求到来首先看有没有servlet可以处理该请求,如果有的话,那么交给优先级最高的那个servlet,(如果是第一次访问,通过反射实例化一个对象出来,执行init方法),直接执行该servlet的service方法,将这两个对象作为参数传递进去;如果没有servlet可以处理该请求,还是会交给缺省servlet来处理,大体的逻辑就是找到该文件,然后做出对应的响应,如果你系统中重新实现了一个新的缺省servlet,那么最终会调用你自己实现的这个。
6.service方法在执行的过程中,我们传入了response对象,往response里面写入数据,Connector读取response里面的内容,然后生成响应报文
7.响应报文再传回给客户端,客户端进行解析,渲染
ServletConfig
package com.simon.config.servletConfig;
import javax.servlet.*;
import java.io.IOException;
public class ServletConfigDemo1 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException {
//tomcat在启动的时候,会去解析init-param标签,然后将这个键值对封装到ServletConfig对象中
//ServletConfig里面存了一个map,解析init-param里面的数据,然后放入config对象中
//你需要去做的,只是拿到ServletConfig对象,然后利用它提供的一个方法,来获取数据即可
ServletConfig servletConfig = getServletConfig();
//就是对map的一层封装
String username = servletConfig.getInitParameter("username");
String password = servletConfig.getInitParameter("password");
System.out.println("username:" + username + ",password:" + password);
}
}
<servlet>
<servlet-name>servletconfig</servlet-name>
<servlet-class>com.simon.config.servletConfig.ServletConfigDemo1</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>zs</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>iamzs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servletconfig</servlet-name>
<url-pattern>/servletconfig</url-pattern>
</servlet-mapping>
ServletContext
上下文对象。该对象会随着应用的启动而创建,只有当应用被卸载,该对象才会消失。它的生命周期基本和应用的生命周期是一模一样的。
域
domain。共享空间。如果两个servlet在运行的时候,希望能够共享数据?
servlet1运行时产生了部分数据,接下来其他servlet也需要用到,如果将数据在多个servlet之间进行共享呢?
package com.simon.servletContext;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/domain1")
public class ServletContextDemo1 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
servletContext.setAttribute("username","what is love");
}
}
package com.simon.servletContext;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/domain2")
public class ServletContextDemo2 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Object username = servletContext.getAttribute("username");
System.out.println(username);
}
}
Demo:实现网站的历史访问人数
package com.simon.historyCount;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/v1")
public class ViewPage1 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
if (count == null){
count = 1;
}else {
count++;
}
servletContext.setAttribute("count",count);
System.out.println("访问次数:" + count);
}
}
package com.simon.historyCount;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("/v2")
public class ViewPage2 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
if (count == null){
count = 1;
}else {
count++;
}
servletContext.setAttribute("count",count);
System.out.println("访问次数:" + count);
}
}
获取绝对路径
package com.simon.path;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.File;
import java.io.IOException;
@WebServlet("/path")
public class AbsolutePath extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//获取form.html绝对路径
//1.为什么会到tomcat/bin目录下?
//new File里面输入的是一个相对路径,相对的是哪个路径,user.dir jvm
//ee项目没有一个main方法,程序是tomcat运行之后调用我们的代码 tomcat开启jvm
//2.怎么才能获取到部署根目录下的一个文件的绝对路径
// 想一个概念 应用 Context path docBase 应用的根目录
//docBase的地址 tomcat绝对知道 它给我们提供了一个入口
File file = new File("form.html");
//输出——> D:\Software\Tomcat\apache-tomcat-8.5.73\bin\form.html
System.out.println(file.getAbsolutePath());
//
ServletContext servletContext = getServletContext();
//这个方法其实就是对于docBase的一个封装
//如果你输入的是一个空字符串,那么它返回的就是docBase的地址
//接下来,如果你希望获取部署根目录下面的任意一个文件的绝对路径
//其实只需要docBase + 相对部署根目录的相对路径 === 绝对路径
String realPath = servletContext.getRealPath("");
File f = new File(realPath + "/" + "form.html");
System.out.println(f.exists()); //true
//能不能获取到WEB-INF目录下面一个文件的绝对路径
//WEB-INF只是用来拦截客户端的直接访问 屏蔽客户端的访问
//坚固的堡垒总是从内部突破
File f2 = new File(realPath + "/WEB-INF/1.txt");
System.out.println(f2.exists()); //true
}
}