Servlet起步

什么是Servlet

Servlet是sun公司制定的用来扩展web服务器功能的组件规范,通俗理解为遵循Servlet规范开发的实现了某个功能的Java组件。该组件没有 main 方法,不能独立地运行,只能在Servlet容器中运行,容器管理其从创建到销毁的整个过程。

早期web服务器(Apache)不能处理动态页面,为了扩展该功能,web服务器将请求发送给帮助程序(tomcat)处理。tomcat就是Servlet容器, WEB-INF目录下的web.xml部署描述符文件是web应用的配置文件,容器根据该配置来指定Servlet处理具体请求。

Web请求的过程

  1. 浏览器依据ip、port与服务器建立连接
  2. 浏览器将相关数据(如请求参数)打包,然后发送请求
  3. web服务器的通信模块解析请求数据包,发送给Serlvet容器。容器将解析的数据封装到request(HttpServletRequest)对象中,同时创建一个response(HttpServletResponse)对象。
  4. 容器依据请求路径找到Servlet类,加载class文件并创建Servlet对象(如果已经存在则跳过)。然后调用该对象的service()方法,将request(可以获取请求中所有的数据)和response(可以封装服务器的响应数据)作为参数传递进去,执行业务逻辑。
  5. 容器读取response中的处理结果,然后将处理结果发送给通信模块,通信模块将数据打包发送给浏览器。
  6. 浏览器解析响应数据包,生成响应的页面。
  7. WEB应用程序停止时,Servlet容器将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

简单来说就是tomcat容器通过web.xml文件中的<url-patten>找到对应的servlet,然后调用service()方法处理浏览器请求。

开发servlet步骤

  1.在web容器中配置url映射

<servlet>
<servlet-name>servletTest</servlet-name>
<servlet-class>com.servlet.servletTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletTest</servlet-name>
<url-pattern>/servlet/test</url-pattern>
</servlet-mapping>

  2.开发servlet类

定义一个servlet,继承HttpServlet(能够处理HTTP请求),然后覆写doGet/doPost或覆写service()方法。

抽象类Httpservlet同时实现了service、doGet和doPost方法,其中service方法会根据HTTP请求自动调用相应的doxxx方法(默认实现为向客户端返回一个错误)。所以实际开发中如果service方法不需要处理业务逻辑,则只需重写相应的doxxx方法(向客户端发送数据),不用重写service方法。如果需要service处理业务逻辑而重写了service方法,则里面必须包括相应的转发逻辑(转发到其他Servlet组件或调用doxxx)。

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class Servlettest extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ServletCofig config = this.getServletConfig(); //可以直接调用
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY></HTML>");
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}

一个servlet处理多个请求

此时servlet充当控制器的作用,将不同请求分发给不同资源,需要采用url-pattern的扩展名规则。

String uri=request.getRequestURI(); //获取请求路径:appName/list.do,appName/add.do
String action= uri.substring(uri.lastIndexOf(“/”)+1,uri.lastIndexOf(“.”));
//根据不同的路径,调用不同的分支处理
if(action.equals("add")){
...
}else if(action.equals("list")){
...
}

Servlet生命周期

1.实例化:容器不管收到多少个请求,只会创建一个servlet对象。

  实例化时机:收到请求时,或设置<load-on-startup>1</load-on-startup>于容器启动时创建。

2. 初始化: 容器调用init(ServletConfig config)方法对Serlvet进行初始化,GernericServlet提供了init方法的实现(而且通过this.config=config将ServletConfig对象保存),不用开发者写。Servlet的整个生命周期内,该方法只被调用一次。在init方法内,通过config.getInitParameter("name")或静态方法ServletConfig.getInitParameter("name")可以获取servlet的初始化参数init-param。如果需要重写init方法执行别的操作,最后应调用 super.init() 以确保正确的初始化。

3.  就绪:容器收到请求后调用service()方法。

4.  销毁:只执行一次。

Servlet起步

Servlet线程安全问题

重定向与转发

重定向:服务器向浏览器发送一个302状态码及一个Location消息头(值为重定向地址),浏览器收到后立即向重定向地址发送请求。

使用场景:当资源移动到新的位置,需要客户端向新地址发送请求时,或是为了负载均衡,或者只是为了简化用户的操作(提交信息后跳转)。

使用响应对象的API即可实现重定向,重定向过程中涉及到的web组件不共享同一个request和response对象。

out.println("会被清空");
response.sendRedirect("任意url");
System.out.println("该处代码仍会执行,等整个service方法结束才发送响应数据");
//重定向后会清空response对象中的数据,Content-Lenght:0

转发:一个web组件(servlet/jsp)将未完成的请求处理通过容器转交给另外一个web组件继续完成,如servlet获取数据后转发给jsp展现。

转发前后涉及的web组件用的是同一个request和response对象。在服务器内部完成,和浏览器无关。

使用步骤:

//1.绑定数据到request对象
request.setAttribute(String name,Object obj);
//Object request.getAttribute(String name); 转发目标组件内获取数据 //2.获得转发器
RequestDispatcher rd=request.getRequestDispatcher(String path);
//path:转发目的地,必须是同一个应用内部的绝对路径或相对路径 //3.转发
rd.forward(request,response);
other code ... //转发语句之后的代码依然会被执行完

过滤器、监听器与SpringMVC的拦截器

过滤器:是Servlet2.3规范中定义的一种封装了一些功能的小型、可插入web组件,用来拦截Servlet容器的请求过程和响应过程,以便处理请求和响应数据。

使用场景:确认用户是否登陆过,提交的内容是否有敏感词,字符集转变,管理会话属性,将多个相同处理逻辑的模块集中到过滤器中方便代码的维护。

编写过滤器步骤:

  1.编写一个java类,实现filter接口,拦截处理逻辑在doFilter方法中实现

/* 敏感词过滤器 */
public class CommentFilter implements Filter{
private FilterConfig config;
private illegalWord;
//容器启动后创建Filter实例,调用init方法一次。
//容器会将创建好的FilterConfig对象作为参数传入init方法,借此可以获取初始化的配置信息
//可以将FilterConfig作为成员保存在对象*后续使用
public void init(FilterConfig filterConfig)throws ServletException{
this.config=filterConfig;
illegalWord=filterConfig.getInitParameter("illegalWord");
...
} //doFilter是主要方法,可以调用过滤器链的doFilter方法,也可以直接向客户端返回响应信息,或者利用HttpServletResponse的sendRedirect()方法将请求转向到其他资源
//参数chain是过滤器链对象,过滤器链的dofilter()方法会调用下一个过滤器的doFilter方法。若无则调用相应的Serlvet的service方法
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)throws IOException,ServletException{
HttpServletRequest req=(HttpServletRequest)request; //强制转到子接口
HttpServletResponse res=(HttpServletResponse)response;
req.setCharacterEncoding("UTF-8");
res.setContentType("text/html;charset=utf-8");
PrintWriter out=req.getWriter();
String comment=req.getParameter("coment");
if(coment.indexOf("shit")!=-1){
//res.sendRedirect("error/_error.jsp");//可以直接转发
out.print("<h3>有敏感词</h3>"); //也可以直接响应数据。
}else{
chain.doFilter(req,res);
…//other code here will be executed when response comes back;
}
}
//容器删除过滤器实例之前调用,只执行一次
public void destroy(){this.config = null;...}
}

  2.将过滤器配置到web.xml中

<!-- 配置编码过滤器 -->
<filter>
<filter-name>setCharacterEncoding</filter-name>
<filter-class>com.company.strutstudy.web.servletstudy.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
//存在多个过滤器时,会按照filter-mapping的先后顺序依次调用
<filter-mapping>
<filter-name>setCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Servlet起步

监听器:Servlet规范中定义的一种特殊的组件,用来监听Servlet容器产生的特定事件并进行相应的处理,包括容器创建/销毁request、session和ServletContext时的事件,调用reqeust、session和ServletContext的setAttribute、removeAttribute方法时产生的事件。

使用场景:统计在线用户数量/网站访问量,记录访问日志,系统启动时初始化等。

监听器的启动顺序:按照web.xml的配置顺序来启动

容器加载顺序:监听器>过滤器>Servlet

编写监听器步骤:

  1.编写一个java类,实现特定的监听器接口(ServletContextListener/ServletContextAttributeListener,HttpSessionListener/HttpSessionAttributeListene,ServletRequestListener/ServletRequestAttributeListener)

//统计网站在线人数
public class CountListener implements HttpSessionListener{
private int count=0;
public void sessionCreated(HttpSessionEvent hse){
//其他接口对应的requestInitialized,contextInitialized方法
count++;
//通过监听的事件对象获得session对象,然后通过session获得servlet上下文
HttpSession session=hse.getSession();
ServletContext ctx=session.getServletContext();
ctx.setAttribute("count",count)
  }
public void sessionDestroyed(HttpSessionEvent hse){
//requestDestroyed,contextDestroyed方法
//关闭时操作
  }
}

  2.在web.xml中配置该监听器

<listener>
<listener-class>web.CountListener</listener-class>
</listener>

拦截器:Spring的HandlerMapping处理器支持拦截器应用。拦截器组件是SpringMVC特有的组件,可以在进入Controller之前拦截,也可以在执行Controller之后拦截,还可以在jsp解析完成后向浏览器输出前拦截。

使用场景:日志记录,权限检查,后台处理时间监控,通用逻辑共用(如读取cookie)。

拦截器接口及方法:

public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response,Object handler, ModelAndView modelAndView) throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception;
}

继承HandlerInterceptor接口,三个方法必须同时实现。如果只需要实现某个的回调,可以继承HandlerInterceptorAdapter接口。

preHandle:处理器执行前被调用,第三个参数为处理请求的处理器。

返回值:true表示继续调调其他拦截器或处理器;false表示流程中断,不继续调用其他的拦截器或处理器,此时需要通过response重定向来产生响应,否则页面是白板。

postHandle:处理器执行后、视图处理前调用,可以通过modelAndView对模型数据进行处理或对视图进行处理。

afterCompletion整个请求处理完毕后调用。如性能监控中可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理。只要preHandle返回true,拦截器的afterCompletion就会被执行

如何配置:

<mvc:interceptors>
<!-- 定义在mvc:interceptors元素下面的Interceptor将拦截所有的请求 -->
<bean class="com.test.AllInterceptor"/>
<mvc:interceptor>
<!-- 定义在mvc:interceptor下面的Interceptor将拦截特定的请求 -->
<mvc:mapping path="/test/check.do"/>
<!-- 不拦截的请求 -->
<mvc:exclude-mapping path="/login/*">
<bean class="com.test.SomeInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

 Servlet起步

SpringMVC拦截器与Filter的区别 

二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。

不同之处:

Filter是Servlet规范规定的,由Servlet容器支持的,只能用于Web程序中,只在Servlet前后起作用。

拦截器是在Spring容器内,由Spring框架支持的,既可以用于Web程序,也可以用于Application、Swing程序中。拦截器是一个Spring的组件,能使用Spring里的任何资源。在Spring构架的程序中,拦截器的使用具有更大的弹性,优先使用。

同时配置过滤器和拦截器时的请求处理过程:

Servlet起步

Servlet起步

上一篇:C#窗体嵌套


下一篇:python中的模块调用