前言
万丈高楼平地起,学习框架前先将Servlet学好
学习过程中,不断提问自己真的理解了Servlet吗?
目录
- Tomcat
- Tomcat中的Servlet
- 一个Servlet案例
3.1. XML中的配置
3.2. Tomcat的运行 - Servlet层次
4.1. Servlet接口
4.2. GenericServlet类
4.3. HttpServlet类 - Request与Response
5.1. 一个获得请求与返回响应的案例
5.2. Request和Response类 - 总结
Tomcat
学习JavaWeb,肯定都接触过Tomcat这个轻量级服务器
也叫Servlet/JSP服务器
对于JavaEE的13种规范:
JDBC,JNDI,EJB,RMI,JSP,Servlets,XML,JMS,JavaIDL,JTS,JTA,JavaMail,JAF
Tomcat只实现了两种:Servlet和JSP,也就是写Servlet和JSP可以不用导Jar包(内置了),而如果写其他规范就需要导jar包
JavaWeb三大组件:Servlet(容器)、Listener(监听器)、Filter(过滤器)
所以说JavaEE和JavaWeb不是一个级别的
一个JavaWeb程序是怎样运行的呢?
这些在Tomcat的核心配置文件server.xml中可以看到
tomcat - > conf - > server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="GBK" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
我们前面学习了XML,根据xml的知识:
server.xml中,根目录Server代表服务器
下面有3种子元素:Service、Listener、GlobalNamingResources
其中Service代表服务,真正处理业务,有且仅有一个,Listener代表监听器
Service 服务中有两种子元素:Connector连接器、Engine引擎
Connector负责监听客户端或者其他连接请求,也不会处理业务
Engine是Tomcat引擎,处理业务的地方,Engine下面有host主机(localhost)
host中的appBase属性是不是就是我们的工程目录:webapps
核心配置再对于相关的配置文件与解析器,就做到了对请求响应逻辑的封装,程序员只需写好业务就ok了
Tomcat服务器=Web服务器+Servlet/JSP容器(Web容器)
再将Tomcat抽象一点:
Tomcat中Web服务器接收、响应客户端请求(静态资源),Web容器装载Servlet/JSP,让它们去处理动态资源
总的来说,Tomcat将各种请求逻辑、响应逻辑都封装好了,我们只需编写静态页面、动态资源和业务(Servlet)
Tomcat中的Servlet
Servlet容器,就是存放着各种Servlet对象的容器,其中可以有自定义的Servlet和内置的Servlet
通过Web服务器映射的URL访问资源,需要接收请求、处理请求、响应请求;接收请求和响应请求由Web服务器处理,我们要做的就是处理请求,通过Servlet容器
Tomcat中,所有的业务都是各种各样的Servlet实现的
我们可以看Tomcat的另一个核心配置文件:web.xml
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
还有很多,全是各种Servlet元素
其中default的匹配路径是"/",也就是默认地址localhsot:8080/,通过DefaultServlet处理
jsp页面的处理方法JspServlet
一个Servlet案例
当然先要配置好Tomcat
在src/test中,编写一个helloServlet,实现Servlet接口 ,这就是一个Servlet处理程序
package test;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class helloServlet implements Servlet {
public helloServlet() {
System.out.println("构造器方法");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init方法");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//转型
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
//获得请求方法
String method = httpServletRequest.getMethod();
//如果是get
if (method.equals("GET")) {
System.out.println("this is servlet: GET");
}
//如果是post
else if (method.equals("POST")){
System.out.println("this is servlet: POST");
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("销毁servlet");
}
}
在web.xml中配置helloServlet的访问
<?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标签给Tomcat配置servlet-->
<servlet>
<servlet-name>helloServlet</servlet-name>
<!--servlet全类名-->
<servlet-class>test.helloServlet</servlet-class>
</servlet>
<!--servlet-mapping配置servlet程序配置访问地址-->
<servlet-mapping>
<!--servlet-name告诉服务器,当前地址给该Servlet使用-->
<servlet-name>helloServlet</servlet-name>
<!--url配置访问地址-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
这样启动一个Tomcat就可以访问这个helloServlet
输入地址:http://localhost:8080/hello
XML中的配置
< servlet>< /servlet>:这个之间配置的是Servlet的类信息
< servlet-name>:这是Servlet 的别名,一个名字对应一个Servlet。相当于变量名 < servlet-class>:Servlet 的全类名,服务器会根据全类名找到这个Servlet
< servlet-mapping>< /servlet-mapping>:这个之间配置的是Servlet 的请求映射信息
< servlet-name>:Servlet的别名,说明这个Servlet将会响应下面url-pattern的请求
< url-pattern>:Servlet响应的请求路径。如果访问这个路径,这个Servlet就会响应。这个url-pattern可以配置多个,这时表示的就是访问这些url都会触发这个Servlet进行响应
对于客户端的请求:http://localhost:8080/hello
Tomcat先查找web.xml中的url配置,存在就继续找到给Servlet(name - > class)
然后运行这个Servlet,返回响应
Tomcat的运行
学到了JavaWeb,发现已经不用写main方法了,启动一下Tomcat就可以实现我们编写的helloServlet
可以想到Tomcat中,应该帮我们定义好了main()方法
说实话,看不懂这位大佬的源码解析,但大概可以知道,Tomcat在启动startup时会通过各种反射机制和线程池调用线程,最终达到想要的效果:Tomcat启动后一直等待连接,我们仅需编写好Servlet就可以处理好业务
Tomcat的main方法入口org.apache.catalina.startup.Bootstrap#main
Servlet层次
1)从广义上来讲,Servlet 规范是Sun 公司制定的一套技术标准,包含与Web 应用相关的一系列接口,是Web 应用实现方式的宏观解决方案。而具体的Servlet 容器负责提供标准的实现。
2)从狭义上来讲,Servlet 指的是javax.servlet.Servlet 接口及其子接口,也可以指实现了Servlet 接口的实现类。
3)Servlet 作为服务器端的一个组件,它的本意是“服务器端的小程序”。Servlet 的实例对象由Servlet 容器负责创建;Servlet 的方法由容器在特定情况下用;Servlet 容器会在Web 应用卸载时销毁Servlet 对象的实例
Servlet接口的实现体系:
Servlet接口
Servlet接口很简单,仅仅定义了5个方法的规范,是Servlet的规范
这5个方法中,3个是Servlet生命周期的方法:
- init(ServletConfig var1):Servlet初始化函数,仅在Servlet创建时调用一次,初始时会传入ServletConfig 配置对象
- getServletConfig():获得ServletConfig对象
- service(ServletRequest var1, ServletResponse var2):Servlet执行方法,收到客户端请求ServletRequest时,执行该方法
- getServletInfo():返回此Servlet 的描述信息
- destroy():销毁Servlet前调用该方法
但是如案例中,实现Servlet接口必须要实现5个方法,能不能更简单一点?
GenericServlet类
GenericServlet抽象类实现了Servlet接口
除了重写的5个方法,还有一些自己的方法
仅有service方法是抽象的,也就是我们仅需要关注我们要处理的业务(当然,重写其他方法也是可以的)
为什么GenericServlet是抽象类:
抽象类的作用是什么?
- 防止实例化
- 规范实现类
仔细看GeneriServlet源码中对Servlet接口的实现
这些方法几乎都没有实际的作用,作者设置抽象类就是认为程序员别实例化GenericServlet,里面的方法没有实际用处
HttpServlet类
GenericServlet几乎没有实现Servlet接口,而且本身也是个抽象类,具体的实现是它的子类HttpServlet
可以发现HttpServlet中实现了service方法,我们的业务方法
我们知道Http请求有get方法、post方法等
处理时需要自己通过request.getMethod()得到方法,然后分类处理
它可以判断请求,然后分发到不同方法处理:
get - > doGet ; post - > doPost
它在GenericServlet的基础上再次细化封装,我们连判断客户端访问是什么方法都不需要了
Servlet子类就是不断偷懒的过程
Servlet - > GenericServlet - > HttpServlet
Request与Response
Request:请求信息,客户端发送的请求信息被Tomcat封装到Request中
Response:响应信息,服务器处理完业务后返回给客户端的响应信息
注意:Tomcat连接到客户端后,会创建Request与Response,Request封装各个请求头(Header),请求地址(URL),请求参数(QueryString),Response在Tomcat传给Servlet时是空的
随着后续的业务处理,会存进response中
回传用response的getWriter方法获得字符输出流对象,然后通过流可以响应信息
一个获得请求与返回响应的案例
TestReqResp 类判断登录信息
package test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class TestReqResp extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
String name = req.getParameter("name");
String password = req.getParameter("password");
System.out.println("客户端输入的name:"+name);
System.out.println("客户端输入的password:"+password);
PrintWriter writer = resp.getWriter();
if (name.equals("zhangsan")&&password.equals("123456")){
writer.println("hello Client");
}
else {
writer.println("登录失败");
}
}
}
记得在web.xml中配置Servlet
<servlet>
<servlet-name>testReqResp</servlet-name>
<servlet-class>test.TestReqResp</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testReqResp</servlet-name>
<url-pattern>/testReqResp</url-pattern>
</servlet-mapping>
输入网址:http://localhost:8080/testReqResp?name=zhangsan&password=123456
网址返回了信息:
控制台也得到了登录信息:
当然,这是一个通过URL传递参数发方法,可以选择使用HTML的表单登录
Request和Response类
从Servlet接口server方法传递的参数ServeltRqeuset、ServeltResponse
到HttpServlet的HttpServletRequest、HttpServletResponse
ServeltRqeuset:
由继承图可以看出ServletRquest、HttpServletRequest都是接口
他们规范方法,具体由ServletRequestWrapper和HttpServletRequestWrapper实现
ServletRquest是针对客户端发出的请求信息
HttpServletRequest继承ServletRquest,并添加了一些获得HTTP协议信息的方法
Response和Request类似
总结
- Tomcat是一个轻量级的服务器,支持Servlet/JSP
- Tomcat服务器=Web服务器+Servlet容器;Web服务器负责连接与响应,Servlet容器中的各种Servlet负责处理业务
- Servlet层次:Servlet接口、GenericServlet抽象类、HttpServlet类
- Servlet接口定义5种方法,程序员需要重写这些方法;GenericServlet抽象类中帮我们实现了4个,仅剩server抽象方法供我们处理业务;HttpServlet类进一步将server方法细化,对不同的请求分发到对应的方法;越来越便捷
- Request和Response在Tomcat连接到服务器就会创建并传递给对应的Servlet;继承了Servlet接口、GenericServlet抽象类是封装得到ServletRequest与ServletResponse,包含了请求信息;继承HttpServlet类是封装成HttpServletRequest和HttpServletResponse,在前者的基础上多包含了Http协议的一些信息