java servlet

目录

一、servlet简述

二、servlet生命周期

三、容器执行过程

四、相关类

4.1、GenericServlet&HttpServlet

4.2、HttpRequest

4.3、HttpResponse

4.4、HttpSession

4.5、RequestDispatcher

4.6、ServletContext

4.7、ServletConfig

五、web.xml配置

5.1、映射servlet

5.2、配置servlet初始参数

5.3、Servlet Load-on-Startup

5.4、配置应用初始参数

六、cookies

七、过滤器

八、监听器

九、并发

参考


一、servlet简述

java servlet是一个对请求做出响应的对象。当浏览器向服务器发出请求后,web服务器接收到请求后查看是否为servlet,如果是则将请求传递给servlet容器,servlet容器找到对应的servlet对象,调用该对象的service方法来处理请求,产生响应,然后返回给浏览器。tomcat是一个servlet容器,但是它内部集成了web服务器。一个servlet容器可以含有多个web应用,一个web应用含有多个servlet,对应关系如下图所示:

Web applications with multiple servlets inside a Java Servlet container

二、servlet生命周期

servlet的生命周期由servlet容器管理,生命周期包含的步骤如下:

  1. 加载相应servlet类
  2. 创建servlet实例
  3. 调用init方法
  4. 调用service方法
  5. 调用destroy方法

步骤1、2、3只会执行一次,仅当servlet被加载时执行。默认第一次请求该servlet时才会加载servlet,但是可以配置web.xml文件来强制它在容器启动时加载。步骤4会被执行多次,每来一个请求会被执行一次。步骤5在servlet被卸载时会被执行,只执行一次。

三、容器执行过程

当容器启动时会加载web应用,同时创建ServletContext。ServletContext定义了一些和servlet容器交流的方法,比如得到其他文件的资源、转发请求或写日记。当请求来时会先找和URL匹配的filter过滤器来先预处理请求,然后再把请求传递给相应的servlet,但是如果servlet没有加载则先加载,对servlet初始化过程中会创建ServletConfig的对象,并传给servlet。可以在web.xml中为每一个servlet配置初始参数,可以通过ServletConfig的对象获取。在创建ServletConfig时会将ServletContext放入ServletConfig中,因此通过SerlvetConfig可以获得ServletContext。总之有了servlet并初始化后,则调用它的service方法处理请求,然后返回响应结果。其中请求信息HttpRequest获得,响应结果用HttpResponse表示。容器在放回结果给浏览器时还要将响应结果给filter过滤器进行处理,最后才返回结果。也就是说过滤器可以在servlet处理前后进行预处理和后处理。

上述有点乱、复杂,但是已经讲到了几乎所有要用到的类,过滤器、请求、响应、servlet、ServletConfig、ServletConfig。下面将会详细介绍。

四、相关类

4.1、GenericServlet&HttpServlet

这是一个通用的,协议无关的servlet,同时也是个抽象类,需要重写service方法。在该类的初始化中会被传入ServletConfig对象。

public class SimpleServlet extends GenericServlet {

  public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException {

         String yesOrNoParam = request.getParameter("param");

		  if("yes".equals(yesOrNoParam) ){

			  response.getWriter().write(
				"<html><body>You said yes!</body></html>");
		  }

		  if("no".equals(yesOrNoParam) ){
			
			  response.getWriter().write(
				"<html><body>You said no!</body></html>");
		  }
  }
}

上面对参数进行了判断,输出不同的响应结果。

HttpServlet继承于GenericServlet,与http协议有关,不同的http请求方式对应不同的方法,比如想要响应get和post方式的请求,只需重写doGet和doPost方法即可。

public class SimpleHttpServlet extends HttpServlet {

  protected void doGet( HttpServletRequest request,
                        HttpServletResponse response)
        throws ServletException, IOException {

      doPost(request, response);
  }

  protected void doPost( HttpServletRequest request,
                         HttpServletResponse response)
        throws ServletException, IOException {

      response.getWriter().write("GET/POST response");
    }
}

4.2、HttpRequest

HttRequest是和http协议相关的类,代表请求。继承于ServletRequest,比ServletRequest多了些协议相关的方法。

通过getParameter可以获得请求参数,比如String param1 = request.getParameter("param1");获得参数param1的值,该参数可以通过get或者post方法传给服务器。

通过getHeader方法可以获得http中相应头部的信息,比如String contentLength = request.getHeader("Content-Length");获得http请求体重的字节长度。

通过getInputStream可以获得字节流,读入二进制数据,通过getReader获得字符流,读入字符数据。

通过getSession方法可以获得会话信息。还有其他的方法使用,比如获得ServletConfig、getServletContext。

4.3、HttpResponse

HttpResponse是和http协议相关的类,代表响应。继承于ServletResponse。通过getWriter获得PrintWrite对象,可写入数据到响应结果中。通过setHeader可以设置响应头部,比如设置Content-Type告诉浏览器接下来返回的是HTML,response.setHeader("Content-Type", "text/html");通过getOutputStream可以写入二进制数据。通过sendRedirect可以让浏览器重定向。

4.4、HttpSession

Session代表会话,每一个会话对应一个用户,可以在用户多次访问web应用不同网页的信息时维护该用户的信息,同一个会话范围内的servlet都可以访问到会话信息。通过HttpSession可以绑定一些对象到会话上,允许用户在多次访问中保持信息一致性。会话可以通过cookies或者重写url实现。setAttribute绑定对象,getAttribute得到已绑定对象。setMaxInactiveInterval设置最大的访问间隔,也就是最多会保存会话信息多久。

4.5、RequestDispatcher

RequestDispatcher可以让你在一个servlet中调用另一个servlet,可以通过请求的getRequestDispatcher方法获得。含有两个方法:forward和include,分别对应着转发和包含的功能。

forward:将请求传发另一个servlet之前不能关闭流(或提交输出),否则会抛出异常,请求转发后会清空输出流,就是说转发之前写入的数据都被抛弃(测试了下,转发之后对response的输出无效)。因此该方法多用于对请求进行预处理,使之更适合目标servlet。传入的两个参数必须是之前的request和response,不能更换。

include:主要用于将其他servlet输出的内容包含进来。将请求转发给目标servlet后,目标servlet对response(响应)请求头、响应的状态的修改都会忽略,因此只会得到目标servlet内容的输出。因此此方法很适合将其他servlet的内容包含进来。

4.6、ServletContext

很多中方法可以获得ServletContext,比如通过请求的getServiceContext方法。该类可以与容器交流,主要可以存入对象到ServletContext中,由于ServletContext有着和web应用一样的生命周期且存放在容器中,因此所有servlet都可以在整个应用生命周期中访问该对象。

4.7、ServletConfig

servlet初始化时被提供的,可以通过它获得servlet对应的初始化参数,该参数由web.xml配置。

注意:对象可以存入到HttpSession和ServletContext中,然后可以在对应的范围内共享数据,但是由于HttpSession和ServletContext都是存在容器内存中的,因此在集群的服务器组中,却不能通过这两个类共享数据,因为每一个服务器都有一个容器,也就是说数据可能会出现多份。因此可以选择将共享数据存入到数据库中。

五、web.xml配置

容器是通过读入web应用的web.xml文件得到该应用所有的信息的,包括servlet的配置信息。只有在web.xml中配置了servlet,容器才是知道有该servlet,知道怎么使用该servlet。

5.1、映射servlet

通过web.xml,可以将servlet映射到某一个URL上,因此用户可以通过该URL来访问servlet。

<?xml version="1.0" encoding="UTF-8"?>	
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">

  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.luo.HelloServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/servlet1</url-pattern>
  </servlet-mapping>

</web-app>

通过servlet元素为一个servlet的设置名字,通过该名字在servlet-mapping中配置URL映射。通过[域名]/[应用名]/servlet1就可以访问HelloServlet类的对象了。

注意,后面的servlet的URL映射可以覆盖之前的servlet。

5.2、配置servlet初始参数

<servlet>
    <servlet-name>controlServlet</servlet-name>
    <servlet-class>com.jenkov.butterfly.ControlServlet</servlet-class>
    
        <init-param>
        <param-name>myParam</param-name>
        <param-value>paramValue</param-value>
        </init-param>
</servlet>

配置了一个参数myParam,值为paramValue。因此可以通过ServletConfig获取参数值了。

public class SimpleServlet extends GenericServlet {

  protected String myParam = null;

  public void init(ServletConfig servletConfig) throws ServletException{
    this.myParam = servletConfig.getInitParameter("myParam");
  }

  public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException {

    response.getWriter().write("<html><body>myParam = " +
            this.myParam + "</body></html>");
  }
}

5.3、Servlet Load-on-Startup

servlet默认第一次被访问时初始化,但是可以配置Load-on-Startup元素强制它初始化。

<servlet>
    <servlet-name>controlServlet</servlet-name>
    <servlet-class>com.jenkov.webui.ControlServlet</servlet-class>
    <init-param><param-name>container.script.static</param-name>
                <param-value>/WEB-INF/container.script</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

此时,ControlServlet会随着容器启动而启动,而里面的1代表着启动的顺序。如果是正整数,则值越小越先加载,如果值小于0或者没有设置该元素时,只有第一次请求时才会初始化。(貌似值为0,也是可以随容器启动而启动的,没有测试它的启动优先级,文档上也没有说值为0时会怎么样。。)

5.4、配置应用初始参数

可以给servlet配置初始参数,自然也可以给应用配置初始参数。

<context-param>
    <param-name>myParam</param-name>
    <param-value>the value</param-value>
</context-param>

然后通过ServletContext的getInitParameter方法获得该参数值。

六、cookies

设置cookies

Cookie cookie = new Cookie("myCookie", "myCookieValue");

response.addCookie(cookie);

读取cookies

Cookie[] cookies = request.getCookies();

String userId = null;
for(Cookie cookie : cookies){
    if("uid".equals(cookie.getName())){
        userId = cookie.getValue();
    }
}

设置过期时间

Cookie cookie = new Cookie("uid", "123");

cookie.setMaxAge(24 * 60 * 60);  // 24 hours. 

response.addCookie(cookie);

删除cookies

Cookie cookie = new Cookie("uid", "");

cookie.setMaxAge(0); 

response.addCookie(cookie);

七、过滤器

过滤器会拦截http到servlet的请求,能够在不改变servlet源代码的基础上添加新的功能。多个过滤器拦截一个servlet的请求时会构成一个链(chain),这些过滤器顺序执行。执行过程如下:

请求-->filter1-->filter2-->servlet-->filter2-->filter1。

过滤器实现:

public class MyFilter implements Filter{

	public void init(FilterConfig arg0) throws ServletException {}

	public void destroy() {}

	private static int count=0;
	private  int number=count++;
	
	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException, ServletException {
		PrintWriter writer=resp.getWriter();
		writer.write("filter"+number+"<br/>");
		chain.doFilter(req, resp);//sends request to next resource
		writer.write("filter"+number+"<br/>");
	}
}

web.xml配置

  <filter>
  	<filter-name>f1</filter-name>
 	 <filter-class>MyFilter</filter-class>
  </filter>
  <filter>
  	<filter-name>f2</filter-name>
 	 <filter-class>MyFilter</filter-class>
  </filter>
  <filter-mapping>
	  <filter-name>f1</filter-name>
 	 <url-pattern>*</url-pattern>
  </filter-mapping>
    <filter-mapping>
	  <filter-name>f2</filter-name>
 	 <url-pattern>*</url-pattern>
  </filter-mapping>

会拦截所有请求。

结果:

八、监听器

监听器可以监听到web应用中发生的事件,并作出反应。一个有三种层次的servlet事件:servlet context级别的事件、session级别的事件、请求级别的事件。每种级别都分为两类事件:声明周期变化,属性变化。下面是事件分类和接口对应的表格:

 

Event CategoryEvent DescriptionsJava Interface

Servlet context lifecycle changes

Servlet context creation, at which point the first request can be serviced

Imminent shutdown of the servlet context

javax.servlet. ServletContextListener

Servlet context attribute changes

Addition of servlet context attributes

Removal of servlet context attributes

Replacement of servlet context attributes

javax.servlet. ServletContextAttributeListener

Session lifecycle changes

Session creation

Session invalidation

Session timeout

javax.servlet.http. HttpSessionListener

Session attribute changes

Addition of session attributes

Removal of session attributes

Replacement of session attributes

javax.servlet.http. HttpSessionAttributeListener

request lifecycle changes

a ServletRequest is about to come into scope of the web application.

a ServletRequest is about to go out of scope of the web application.

javax.servlet.

ServletRequestEvent

request attribute changesThis is the event class for notifications of changes to the attributes of the servlet request in an application.

javax.servlet.

ServletRequestAttributeEvent

监听类需要实现上面的某个接口,然后在web.xml中的listener元素中声明这个监听器,比如下面声明了几个监听器:

<web-app>
   <display-name>MyListeningApplication</display-name>
   <listener>
      <listener-class>com.acme.MyConnectionManager</listenerclass>
   </listener>
   <listener>
      <listener-class>com.acme.MyLoggingModule</listener-class>
   </listener>
   <servlet>
      <display-name>RegistrationServlet</display-name>
      ...
   </servlet>
</web-app>

九、并发

当多个请求给同一个servlet时会出现并发执行service方法的情况。想让线程安全,必须遵循一些原则:

  1. service方法中尽量不要访问成员变量,除非成员变量是线程安全的。
  2. 在service方法中不要重新赋值给成员变量,如果必须赋值,则需要用同步块(synchronized)进行同步。
  3. 规则1、2都使用与静态变量
  4. 局部变量是线程安全的,但是局部变量指向的对象不一定是线程安全的。
public class SimpleHttpServlet extends HttpServlet {

  // Not thread safe, static.
  protected static List list = new ArrayList();

  // Not thread safe
  protected Map map = new HashMap();

  // Thread safe to access object, not thread safe to reassign variable.
  protected Map map = new ConcurrentHashMap();

  // Thread safe to access object (immutable), not thread safe to reassign variable.
  protected String aString = "a string value";


  protected void doGet( HttpServletRequest request,
                        HttpServletResponse response)
        throws ServletException, IOException {


    // Not thread safe, unless the singleton is 100% thread safe.
    SomeClass.getSomeStaticSingleton();


    // Thread safe, locally instantiated, and never escapes method.
    Set set = new HashSet();

  }
}

参考

教程:http://tutorials.jenkov.com/java-servlets/index.html

详细的web.xml配置:https://docs.oracle.com/cd/E24329_01/web.1211/e21049/web_xml.htm#WBAPP502

api 文档:https://javaee.github.io/javaee-spec/javadocs/javax/servlet/package-summary.html

过滤器和监听器:https://docs.oracle.com/cd/B14099_19/web.1012/b14017/filters.htm#i1000654

监听器和例子:https://www.journaldev.com/1945/servletcontextlistener-servlet-listener-example

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值