tomcat和servlet的关系

时间:2024-12-12 08:07:43

一、什么是servlet?

处理请求和发送响应的过程是由一种叫做Servlet的程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西。理解这个的前提是了解一些http协议的东西,并且知道B/S模式(浏览器/服务器)。

二、tomcat和servlet的关系

Tomcat 是Web应用服务器,是一个Servlet/JSP容器. 
Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户. 
而Servlet是一种运行在支持Java语言的服务器上的组件. 
Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品. 
从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。 
但是并不知道是如何转变的,只知道浏览器发送过来的请求也就是request,我们响应回去的就用response。 
①:Tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象, 
所有的HTTP头数据读可以通过request对象调用对应的方法查询到。 
②:Tomcat同时会要响应的信息封装为HttpServletResponse类型的response对象, 
通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器 
Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法, 
还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。 
所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。

三、编写Servlet代码

1、创建一个MyServlet继承HttpServlet,重写doGet和doPost方法,根据请求的方式是get还是post,然后用不同的处理方式来处理请求。 
2、在web.xml中配置MyServlet,为什么需要配置? 
让浏览器发出的请求知道到达哪个servlet,也就是让tomcat将封装好的request找到对应的servlet让其使用。

  <servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>controller.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>

按照步骤,首先浏览器通过http://localhost:8080//servlet来找到web.xml中的url-pattern,这就是第一步,匹配到了url-pattern后,就会找到第二步servlet的名字servlet,知道了名字,就可以通过servlet-name找到第三步,到了第三步,也就能够知道servlet的位置class了。然后到其中找到对应的处理方式进行处理。

四 、 创建servlet的原理

1 、 servlet的生命周期是什么?

服务器启动时(web.xml中配置load-on-startup=1,默认为0)或者第一次请求该servlet时,就会初始化一个Servlet对象,也就是会执行初始化方法init(ServletConfig conf)。 
该servlet对象去处理所有客户端请求,在service(ServletRequest req,ServletResponse res)方法中执行。 
最后服务器关闭时,才会销毁这个servlet对象,执行destroy()方法。

2、servlet的生命周期中,可以看出,执行的是service方法,为什么我们就只需要写doGet和doPost方法呢?为什么创建的servlet是继承自httpServlet,而不是直接实现Servlet接口?

(1).httpServlet的继承结构

httpServlet继承GenericServlet,GenericServlet实现了Servlet接口和ServletConfig接口。 
GenericServlet(通用Servlet)的作用是什么? 
GenericServlet实现Servlet接口的方法,简化编写servlet的步骤。

(2).ServletConfig和ServletContext

getServletConfig()方法来获取ServletConfig对象,ServletConfig对象可以获取到Servlet的一些信息,例如ServletName、ServletContext、InitParameter、InitParameterNames。 
通过查看ServletConfig这个接口就可以知道,ServletConfig接口中ServletContext对象是servlet上下文对象,功能有很多,获得了ServletContext对象,就能获取大部分我们需要的信息,比如获取servlet的路径和servlet方法。

(3).Servlet接口中的内容和作用

Servlet生命周期的三个关键方法,init、service、destroy。 
获取ServletConfig,而通过ServletConfig又可以获取到ServletContext。 
而GenericServlet实现了Servlet接口后,也就说明我们可以直接继承GenericServlet,就可以使用上面我们所介绍Servlet接口中的那几个方法了,能拿到ServletConfig,也可以通过ServletConfig拿到ServletContext,不过那样太麻烦,不能直接获取ServletContext,所以GenericServlet除了实现Servlet接口外,还实现了ServletConfig接口,那样,就可以直接获取ServletContext了。因为httpServlet继承了GenericServlet,所以我们可以直接使用httpServlet。

(4).GenericServlet类的内容详解

Servlet和ServletConfig接口所实现的方法,有9个,这很正常, 
但是我们可以发现,init方法有两个,一个是带有参数ServletConfig的,一个有无参的方法.

    public class ServletDemo extends GenericServlet {
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
}
@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
super.init(config);
}
}

首先看init(ServletConfig config)方法,因为init(ServletConfig config)中带有ServletConfig对象,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,然后通过getServletConfig()方法就能够获取ServletConfig对象了,可以通过全局变量保存ServletConfig对象。 
在GenericServlet类中增加一个init()方法,如果以后需要在init方法中需要初始化别的数据,只需要重写init()这个方法,而不需要去覆盖init(ServletConfig config)这个方法。

再来看service(ServletRequest req, ServletResponse res) 方法

    public class ServletDemo extends GenericServlet {
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
}
}

在GenericServlet类中并没有实现该内容,因为在它下面还有一层,还有一个子类继承GenericServlet,并实现该方法,service方法中的参数还是ServletRequest,ServletResponse,并没有跟http相关对象挂钩。

(5).HttpServlet类详解

这个类主要的功能是实现service方法的各种细节和设计。并且通过类名可以知道,该类就跟http挂钩了。

public class ServletDemo extends HttpServlet {
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(arg0, arg1);
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(arg0, arg1);
}
}

五.四个重点的对象。ServletConfig、ServletContext,request、response

1.ServletConfig对象

getServletConfig(); //获取ServletConfig对象
getServletName(); //获取servlet的名称,也就是我们在web.xml中配置的servlet-name
getServletContext(); //获取ServletContext对象
getInitParameter(String); //获取在servlet中初始化参数的值。这里注意与全局初始化参数的区分。这个获取的只是在该servlet下的初始化参数
getInitParameterNames(); //获取在Servlet中所有初始化参数的名字,也就是key值,可以通过key值,来找到各个初始化参数的value值。注意返回的是枚举类型

注意:在上面我们所分析的源码过程中,我们就知道,其实可以不用先获得ServletConfig,然后在获取其各种参数, 
可以直接使用其方法,比如上面我们用的ServletConfig().getServletName();可以直接写成getServletName(); 
而不用在先获取ServletConfig();了,原因就是在GenericServlet中,已经帮我们获取了这些数据,我们只需要直接拿就行。

2.ServletContext对象

getServletContext();
getServletConfig().getServletContext();
//这两种获取方式的区别就跟上面的解释一样,第一种是直接拿,
//在GenericServlet中已经帮我们用getServletConfig().getServletContext()拿到了ServletContext。
//我们只需要直接获取就行了,第二种就相当于我们自己在获取一遍,两种是一样的。

功能:tomcat为每个web项目都创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目*享数据,管理web项目资源,为整个web配置公共信息。通俗点讲,就是一个web项目,就存在一个ServletContext实例,每个Servlet都可以访问到它。 
1、web项目*享数据,

setAttribute(String name, Object obj);// 在web项目范围内存放内容,以便让在web项目中所有的servlet读能访问到
getAttribute(String name);// 通过指定名称获得内容
removeAttribute(String name);// 通过指定名称移除内容  

2、整个web项目的初始化参数,这个就是全局初始化参数,每个Servlet中都能获取到该初始化值

getInitPatameter(String name);  //通过指定名称获取初始化值
getInitParameterNames();  //获得枚举类型

3、获取web项目资源

getServletContext().getRealPath("/WEB-INF/web.xml");//获取web项目下指定资源的路径
InputStream getResourceAsStream(java.lang.String path);//获取web项目下指定资源的内容,返回的是字节输入流
getResourcePaths(java.lang.String path);//指定路径下的所有内容。

3.request对象

request就是将请求文本封装而成的对象,所以通过request能获得请求文本中的所有内容,请求头、请求体、请求行 。

String getHeader(java.lang.String name);// 获得指定头内容String
long getDateHeader(java.lang.String name);// 获得指定头内容Date
int getIntHeader(java.lang.String name);// 获得指定头内容int
Enumeration getHeaders(java.lang.String name);// 获得指定名称所有内容

请求体的获取 – 请求参数的获取分两种,一种get请求,一种post请求 
get请求参数:http://localhost:8080/MyServlet?username=123&password=123 
post请求参数:<form method="post"><input type="text" name="username">

String request.getParameter(String);// 获得指定名称,一个请求参数值。
String[] request.getParameterValues(String);// 获得指定名称,所有请求参数值。例如:checkbox、select等
Map<String , String[]> request.getParameterMap();// 获得所有的请求参数  

请求转发

request.getRequestDispatcher(String path).forward(request,response);  
//path:转发后跳转的页面,这里不管用不用"/"开头,都是以web项目根开始,因为这是请求转发
//请求转发只局限与在同一个web项目下使用,所以这里一直都是从web项目根下开始的

特点:浏览器中url不会改变,也就是浏览器不知道服务器做了什么,是服务器帮我们跳转页面的,并且在转发后的页面,能够继续使用原先的request,因为是原先的request,所以request域中的属性都可以继续获取到。

4.response对象

response.setHeader(java.lang.String name, java.lang.String value) ;//设置指定的响应头,常用。
response.setHeader("Refresh",3);//设置每隔3秒就自动刷新一次,

重定向(页面跳转) 
方式一:手动方案

response.setStatus(302);  //状态码302就代表重定向
response.setHeader("location","http://www.baidu.com");

方式二:使用封装好的,通过response.sendRedirect("http://www.baidu.com"); 
特点:服务器告诉浏览器要跳转的页面,是浏览器主动去跳转的页面,浏览器的地址栏中url会变,是浏览器重新发起一个请求到另外一个页面,所以request是重新发起的,跟请求转发不一样,原先request域中的属性不能获取。 
重定向没有任何局限,可以重定向web项目内的任何路径,也可以访问别的web项目中的路径,并且这里就用”/”区分开来。 
如果使用了”/”开头,就说明我要重新开始定位了,不访问刚才的web项目,自己写项目名,可以访问服务器上的其他项目。 
如果没有使用”/”开始,那么就知道是访问原来那个web项目下的servlet,就可以省略项目名了。

六. 实例代码

  -->实现javax.servlet.Servlet接口  -->
-->继承javax.servlet.GenericServlet类 (适配器模式) -->
-->继承javax.servlet.http.HttpServlet类 (模板方法设计) -->
-->继承HttpServlet
最常用的就是继承HttpServlet,我们使用起来很方便
public class servletdemo extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("");
out.print("");
//创建cookie,
Cookie cookie = new Cookie("name", "Value");
//设置cookie的有效时间,单位是秒
cookie.setMaxAge(1);
/*负数:cookie的数据存在浏览器缓存中
* 0:删除。路径要保持一致,否则可能删错人。
* 正数:缓存(持久化到磁盘上)的时间*/
//设置cookie的path
cookie.setPath(request.getContextPath());
/*客户端在访问服务器另外资源时,根据访问的路径来决定是否带着Cookie到服务器
*当前访问的路径如果是以cookie的path开头的路径,浏览器就带。否则不带。
cookie.setMaxAge(0);相当于删除cookie*/
//把cookie信息写回到客户端
response.addCookie(cookie); //Cookie遍历
Cookie[] cookies = request.getCookies();//获取客户端的所有Cookie对象
for (int i = 0;cookies!=null && i < cookies.length; i++) {
System.out.println(cookies[i].getName());
System.out.println(cookies[i].getValue());
System.out.println(cookies[i].getPath());
System.out.println(cookies[i].getMaxAge());
}
//session
request.getSession().setAttribute("","");
//得到session对象
HttpSession session = request.getSession();
//session操作
session.getAttribute("");
session.setAttribute("", "");
session.getId();
//请求转发
request.getRequestDispatcher("").forward(request, response);
/*转发是服务器行为,重定向是客户端行为
1.转发在服务器端完成的;重定向是在客户端完成的
2.转发的速度快;重定向速度慢
3.转发的是同一次请求;重定向是两次不同请求
4.转发不会执行转发后的代码;重定向会执行重定向之后的代码
5.转发地址栏没有变化;重定向地址栏有变化
6.转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成 */
//请求重定向
response.setStatus(302);
response.sendRedirect("");
//告知客户端不缓存
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setDateHeader("expires", 0); System.out.println(request.getMethod());//获得请求方式
System.out.println(request.getRequestURL());//返回客户端发出请求时的完整URL
System.out.println(request.getContextPath());//当前应用的虚拟目录
System.out.println(request.getRequestURI());//返回请求行中的资源名部分
System.out.println(request.getQueryString());//返回请求行中的参数部分
System.out.println(request.getHeaderNames());//得到所有头信息name
//Properties
//获取资源路径
//此处填写的路径一定要注意是WEB服务器部署后的路径
String path=this.getServletContext().getRealPath("/test.properties");
//创建Properties
Properties pro =new Properties();
pro.load(new FileInputStream(path));//读入文件test.properties //通过调用GenericServlet的getServletContext方法得到ServletContext对象
ServletContext setServletContext=this.getServletContext();
//向ServletContext添加一个键值对
setServletContext.setAttribute("name", "value"); /*获取非表单数据,先set后get,可以remove
void setAttribute(String name, Object value);
Object getAttribute(String name);
Void removeAttribute(String name);
*/
//服务器和客户端浏览器编码要统一,否则会乱码
// resp.setCharacterEncoding("UTF-8");//设置服务器编码
// resp.setHeader("content-type", "text/html;charset=UTF-8");//通过响应消息头设置客户端浏览器编码
// resp.setContentType("text/html;charset=UTF-8");//此行代码封装了上面两行代码
// PrintWriter out=resp.getWriter();//得到一个字符输出流
// out.write("Response文本信息");//向客户端响应文本内容 //获取编码 //第一种方式
// String encoding=config.getInitParameter("encoding");
// System.out.println("第一种方式"+encoding);
// //第二种方式
// System.out.println("第二种方式"+this.getServletConfig().getInitParameter("encoding"));
//第三种方式
// System.out.println("第三种方式"+super.getInitParameter("encoding")); } }
public class DownloadFileDemo extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//通过路径得到输出流
String path =this.getServletContext().getRealPath("/WEB-INF/classes/cn/servlet/Servlet.jpg");
FileInputStream file=new FileInputStream(path);
//创建输出流
ServletOutputStream sos=resp.getOutputStream();
//得到文件名
String filename=path.substring(path.lastIndexOf("\\")+1);//分割字符串,取最后一个'"\\'"符号后面的1个字符
//设置文件名的编码
filename=URLEncoder.encode(filename,"UTF-8");//将编码改为UTF-8
//告知客户端下载文件
resp.setHeader("content-disposition", "attachment;filename="+filename);
resp.setHeader("content-type","image/jpg");
//输出
int len=1;
byte[] b=new byte[1024];
while((len=file.read(b))!=-1) {
sos.write(b,0,len);
}
sos.close();
file.close();
}
}
public class ServletDemo implements Servlet{
//Servlet生命周期的方法
//在servlet第一次被访问时调用
//实例化
public ServletDemo(){
System.out.println("ServLetDemo执行了,在servlet实例化时调用");
}
//Servlet生命周期的方法
//在servlet第一次被访问时调用
//初始化
public void init(ServletConfig arg0) throws ServletException {
System.out.println("init执行了,在servlet第一次被访问时调用"); }
//Servlet生命周期的方法
//每次访问时都会被调用
//服务
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
//System.out.println("hello servlet");
System.out.println("service执行了,每次访问时都会被调用");
}
//Servlet生命周期的方法
//销毁
public void destroy() {
System.out.println("destroy执行了,销毁");
}
public ServletConfig getServletConfig() {
System.out.println("getServletConfig"); return null;
}
public String getServletInfo() {
System.out.println("getServletInfo"); return null;
}
}