servlet进阶

问题1:Servlet的构造器调用了几次?
这个问题实际上很容易测试,只需要在Servlet的中写一个无参构造器,在方法中写一个打印语句,然后向该Servlet发送请求,会发现打印语句仅仅输出了一次,由此证明构造器只调用了一次。上边我们也说过,Servlet是单实例的,而调用构造器就是用来创建实例的。所以构造器只会被调用一次。
问题2:Servlet是线程安全的吗?
由于Servlet是单实例的,所以当容器调用service方法处理请求时是以多线程的方式调用的,但是因为性能问题所以在这个方法中并没有考虑同步的问题,所以Servlet并不是线程安全的,但是这样做的好处是性能较好。
由于Servlet不是线程安全的,所以尽量不要在使用Servlet处理请求时操作变量,因为有可能会出现同步的问题。(实际应用中只有非常高的并发的情况下才有可能出现这个问题,虽然如此但还是尽量不要那么做)。
问题3:Servlet实例只能在第一次请求时被创建吗?
一般情况下Servlet会在第一次收到请求时被创建,所以当我们第一次访问某个Servlet时会比平时慢一些,这种我们称为第一次惩罚。
如果希望在服务器启动时就创建Servlet的实例,可以在web.xml中进行配置,在servlet标签中还有一个load-on-startup标签,这个标签需要一个大于等于0的整数作为参数,当配置了这个属性后,该Servlet将会在服务器启动时就被创建,且值越小创建的时机越早。

问题4:url-pattern映射的规则是什么?
url-pattern配置的Servlet映射的地址,他的配置规则如下:
精确匹配:
当前项目下指定的URL必须完全
如:/path/ServletName
只有URL为:http://localhost:8080/项目名/path/ServletName
由此也可知/是代表的项目根目录,而在html中/代表的是服务器根目录
路径匹配:
当前项目下指定路径的URL地址
如:/path/*
当URL为:http://localhost:8080/项目名/path/任意值
全匹配:
当前项目下所有的URL地址
如:/*
所有URL都可以
后缀匹配:
当前项目下指定后缀的URL地址
如:*.action
当URL为:http://localhost:8080/项目名/任意值. action
优先级:
1、精确匹配
2、路径匹配
3、全匹配
4、后缀匹配
5、还有一种“/”,这种也是全部匹配,优先级最低
关于“*”:
“*”就是通配符,匹配任意字符
“*”只能出现前面和后面,不能出现在中间
如:/*.action错误
“*”只能出现一次
如:/错误
“*”不能单独出现
如:* 错误
问题5:web.xml文件仅仅是看到的那么简单吗?
web.xml文件整个项目中的配置文件,非常重要。但是纵观我们项目下的web.xml文件似乎内容不多,那如此重要的文件为什么只配置的这么少的内容呢?实际上在Tomcat中还有一个总的web.xml文件,就在%CATALINA_HOME%/conf目录下。
重要配置:
DefaultServlet:用于处理静态资源的默认Servlet

<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>

JspServlet:用于处理JSP的Servlet

<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>
</servlet-mapping>

<servlet-mapping>
  <servlet-name>jsp</servlet-name>
  <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

session的过期时间

<session-config>
        <session-timeout>30</session-timeout>
    </session-config>

MIME类型

<mime-mapping>
<extension>123</extension>
  <mime-type>application/vnd.lotus-1-2-3</mime-type>
 </mime-mapping>

ServletContext
1.1 ServletContext简介
每个WEB应用服务器都会为其创建一个ServletContext对象,项目启动时ServletContext对象被创建,项目停止或重新加载时ServletContext对象被销毁
ServletContext对象主要作用就是在Servlet之间共享数据和加载WEB应用的配置信息,还记得ServletConfig对象可以获取到每个Servlet的配置信息吧,而我们的ServletContext可以或取整个WEB应用的配置信息。
1.2 获取ServletContext
在Serlet接口中,可以通过init方法中的ServletConfig调用getServletContext()方法来获得ServletContext对象。
GenericServlet和HttpServlet可以直接调用getServletContext()方法。(实际上也是ServletConfig的getServletContext()方法)。
HttpSession对象的getServletContext()方法同样也可以获取。
1.3 域对象
ServletContext是JavaWeb的四个域对象之一
PageContext
ServletRequest
HttpSession
ServletContext
域对象主要用来存储传递数据,每个域对象的内部都有一个map用来存储对象
读取数据的方法:
void setAttribute(String name, Object value):用来存储一个对象,也可以称之为存储一个域属性,例如:servletContext.setAttribute(“key”, “value”),在ServletContext中保存了一个域属性,域属性名称为key,域属性的值为value;
Object getAttribute(String name):用来获取ServletContext中的数据;
void removeAttribute(String name):用来移除ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;
Enumeration getAttributeNames():获取当前域中所有属性的名称;
1.4 获取当前应用的初始化参数
和ServletConfig功能类似,ServletContext也可以获取初始化参数,这不过config获取的是当前Servlet的而context获得的是整个Web应用的;
由于整个应用中都拥有同一个ServletContext所以所有Servlet都能获得相同的初始化参数。
在web.xml中配置初始化参数,在根标签中创建context-param元素,通过元素中param-name和param-value标签分别配置key和value,每一个context-param代表一条键值对的初始化参数。

  <context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
  </context-param>
  <context-param>
    <param-name>password</param-name>
    <param-value>1234</param-value>
  </context-param>
</web-app>

1.5 获取项目根目录
项目根目录就是端口号后边的第一个路径。如:http://localhost:8080/hello/HelloServlet,红色部分/hello就是项目的根目录。这样做的目的是,因为在项目在开发时或交付后,项目名很有可能被修改,也就是红色部分会变成其他内容,当项目根目录修改后,页面中很多资源的路径会失效,所以需要动态获取根目录。
主要通过servletContext. getContextPath()来获取。
通过HttpServletRequest对象也可以获得该值。
1.6 获取资源的流和真实路径(物理路径)
在web项目中,我们访问服务器上的资源一般都是使用URL地址或者相对路径,但是有时我们会需要获取服务器中的文件流,或者获取文件的物理地址(比如在上传或下载文件时)。
获取文件流:
InputStream in = getServletContext().getResourceAsStream(“/1.jpg”);
该方法会获取项目根目录下1.jpg的输入流
获取文件真实路径
String realPath = getServletContext().getRealPath(“/1.jpg”);
该方法获得项目根目录下1.jpg的路径地址
注意:给方法并不会判断文件是否存在
请求和响应
请求和响应的过程
请求和响应的过程

HttpServletResponse对象
HttpServletResponse对象封装了服务器响应给客户端的信息,该对象由服务器创建,在Servlet处理请求时,服务器会调用Servlet的service方法并将HttpServletResponse对象作为参数传递。所以,我们可以直接在service方法中使用该对象。一般我们习惯简称他为response。
response的主要功能有:
设置响应头信息
response.setHeader(“Refresh”, “3;URL=http://www.baidu.com“)
response.setContentType(“text/html;charset=UTF-8”);
设置状态码
response.sendError(404,”访问的资源未找到”);
设置响应体
response.getWriter().print(“h1>Hello World/h1>”);
重定向
response.sendRedirect(“index.html”);
HttpServletRequest对象
HttpServletRequest对象封装了客户端发送给服务器的信息,该对象由服务器创建,在Servlet处理请求时,服务器会调用Servlet的service方法并将HttpServletRequest对象作为参数传递。所以,我们可以直接在service方法中使用该对象。一般我们习惯简称他为request。
request的主要功能有:
获取请求参数
String username = request.getParameter(“username”);
在请求域中读写数据
request.setAttribute(“key1”, “value1”);
String key1 = (String) request.getAttribute(“key1”);
获取项目名
String contextPath = request.getContextPath();
转发请求
request.getRequestDispatcher(“/index.html”).forward(request, response);
转发和重定向
2.1 转发
转发是通过request对象发起的,通过request对象获取一个RequestDispatcher对象,通过RequestDispatcher的forward方法发起转发。
转发是在服务器内部进行的:
整个过程浏览器只发送了一个请求。
浏览器不能知道转发行为的发生。
由于在服务器内部进行,所以转发以项目路径为根目录,输入地址时不需要输入项目名。
转发是一次请求,所以request中的数据可以共享。
转发只能转发到应用内部的资源,而不能转发到其他应用
2.2 重定向
重定向是通过response对象发起的,通过response的sendRedirect()方法进行重定向。
重定向是在浏览器中进行的:
整个过程中,浏览器发送了两次请求。
浏览器知道转发行为的发生。
由于在浏览器端进行,重定向的路径是以服务器目录为根目录,所以输入地址时需要输入项目名。
重定向是两次请求,不能共享request中的数据。
重定向不只限定于内部资源,可以重定向到任意web资源。
2.3 路径问题
通常我们访问一个web应用地址格式如下:http://localhost:8080/MyWeb/HelloServlet
http://localhost:8080 这一部分我们称它为服务器的根目录
/MyWeb 这一部分我们称它为项目的根目录
/HelloServlet 这一部分是我们Servlet的映射地址
绝对路径和相对路径
绝对路径:使用“/ ”开头的路径称为决定路径,绝对路径表示从根目录开始寻找资源。
相对路径:不使用“ / ”开头的路径称为相对路径,相对路径表示从当前资源所在目录开始寻找资源
2.4 服务器端路径
服务器端路径,主要指在Servlet中使用转发时的路径。
服务器端的根目录指的是项目的根目录,也就是我们的项目名。
例如,我们现在访问如下地址的Servlet:
http://localhost:8080/MyWeb/hello/HelloServlet
在HelloServlet中调用转发方法
request.getRequestDispatcher(“/index.html”).forward(request, response);
在路径地址处如果加了“ / ”相当于由项目根目录开始寻找资源
也就相当于将请求转发到如下地址:
http://localhost:8080/MyWeb/index.html
request.getRequestDispatcher(“index.html”).forward(request, response);
在路径地址处如果不加“/ ”相当于由当前项目所在目录开始寻找资源
也就相当于将请求转发到如下地址:
http://localhost:8080/MyWeb/hello/index.html
在实际应用中,由于我们的资源(Servlet和JSP)所在的位置有可能会发生变动,所以通常我们会使用绝对路径。

2.5 客户端路径
客户端路径,主要是值在页面中引用外部资源,以及在Servlet中做重定向操作时的路径。
客户端路径的根目录指的是我们tomcat的服务器的根目录,也就是项目名前面那段路径。
例1:我们现在访问如下地址的Servlet:
http://localhost:8080/MyWeb/hello/HelloServlet
在HelloServlet中调用重定向方法
response.sendRedirect(“/index.html”);
在路径地址处如果加了“ / ”相当于由项目根目录开始寻找资源
也就相当于将请求重定向到如下地址:
http://localhost:8080/ index.html
但是这个地址明显不是我们想要的,所以在重定向使用绝对路径时必须要加入项目的名字,如下:
response.sendRedirect(“/MyWeb/index.html”);
如此请求将会重定向到http://localhost:8080/MyWeb/ index.html
response.sendRedirect(“index.html”);
在路径地址处如果不加“/ ”相当于由当前项目所在目录开始寻找资源
也就相当于将请求重定向到如下地址:
http://localhost:8080/MyWeb/hello/index.html
例2:在MyWeb项目中有form.html页面,目录结构如下:
webapps/MyWeb/hello/form.html
现在我在form.html中创建超链接访问/hello/HelloServlet
连接格式如下:
HelloServlet
使用绝对路径,网页和重定向的根目录相同,都是服务器的根目录
因此点击超链接后会访问如下地址
http://localhost:8080 /HelloServlet
这个地址明显不对,所以应该从项目名开始写起
正确如下:
HelloServlet
点击后访问地址:
http://localhost:8080 /MyWeb/hello/HelloServlet
HelloServlet
使用相对路径,会从当前html所在目录开始寻找资源,也就是从/MyWeb/hello/开始。
因此点击超链接后会访问如下地址:
http://localhost:8080 /MyWeb/hello/HelloServlet
同样的,在实际开发中客户端的路径我们也会使用绝对路径,而不使用相对路径。
但是,这块有一个问题,在实际开发中我们项目名有可能会改变,比如:开发中的名字可能为DMS,而实际部署时就变成了Baidu_DMS。但是这是我们在项目中的路径是以/DMS开头的,那就意味着,我们要把项目中所有的页面中、Servlet中的/DMS修改成/Baidu_DMS,如此一来工作量是十分大的,那要如何解决呢?实际上我们可以通过request对象动态的获取项目名来解决这个问题,但是这还需要配合JSP使用,所以我们在这里先不考虑该问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值