servlet/JSP技术仍然在javaweb开发中占据一席之地,尽管出现大量框架,如struts2,springMVC这样的MVC框架,也很有必要弄懂这项技术,下面我将以自己的理解来详细说明一下servlet/JSP和MVC模式。
下图是MVC结构图以及servlet,jsp在结构中所处的位置。
V:view(试图),一般是指jsp呈现给用户的页面
C:Controller(控制器),指servlet接收响应并处理客户端(浏览器,jsp页面)发来的请求。
M: model(模型),是指javabean(就是一个普通类,但是要符合一些规范,这样就可以应用一些工具类)。
将这3部分分开后,他们各司其职,结构清晰,有利于开发。
Servlet是运行在web服务器(如tomcat)中的java程序,狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
Servlet是一套接口规范,规定了这一套程序在服务器中的职责,即:Servlet通过http协议接收和响应web客户端(浏览器)发来的请求。tomcat为我们实现了整套接口,我们拿来直接用就可以了。
那么,我们结合一个小项目先来看看从浏览器访问服务器的过程是什么样的。这个小项目实现了登录和注册的功能,项目详情请见本人博客:点击查看博客
准备工作都做好后,我们在WebRoot下新建一个login.jsp文件,内容如下,这个jsp页面提供了一个表单,用户填写了信息后,点提交,jsp就将信息交给servlet来处理,servlet处理后会给出反馈(反馈的形式可以是跳转到某页面或给出成功或失败的提示)。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
head>
<body>
<h1>欢迎来到登陆页面h1>
<form action="/webDemo1/servlet/LoginServlet" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="checkbox" name="autoLogin"/>自动登录<br>
<input type="submit" value="登陆">
form>
body>
html>
该jsp中有一个表单,用户输入了用户名和密码后,点提交,就将输入的字符串传送到servlet中进行处理,servlet对用户名和密码进行验证,并跳转到其他页面。那么jsp是如何找到对应的servlet?servlet又是如何工作的呢?
jsp是通过配置文件来找到对应的servlet的,在WEB-INF/下有个web.xml文件,这个文件就是一个web项目中各个servlet,jsp文件之间相互跳转的重要枢纽,代码如下:
<web-app version="2.5"
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_2_5.xsd">
<servlet>
<servlet-name>LoginServletservlet-name>
<servlet-class>com.jimmy.servlet.LoginServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>LoginServletservlet-name>
<url-pattern>/servlet/LoginServleturl-pattern>
servlet-mapping>
web-app>
在上面的web.xml文件中,web-app标签是固定的,我们在其中自己写2个标签:servlet和servlet-mapping。servlet标签中定义servlet的名字(servlet-name标签,name我们自己随便起)和servlet类的位置(servlet-class标签)。servlet-mapping标签中同样有2个标签,第一个标签servlet-name内容要跟servlet标签中的servlet-name内容相同,第二个标签url-pattern为该servlet在本项目中定义一个访问路径,其他资源要想访问这个servlet,就要访问这个路径。现在回过头来看代码片段1:login.jsp中的form表单标签的action属性,该属性的作用就是当form表单提交时,将数据提交到哪里,其值为:/webDemo1/servlet/LoginServlet。jsp就将该值到web.xml中去匹配url-pattern,于是乎就匹配到了代码片2中的url-pattern,然后就得到了servlet-name,也就得到了servlet-class(servlet类的位置),再然后,浏览器(jsp页面)就通过http协议将数据传递给servlet,servlet即可处理这些信息。
servlet接收到浏览器发送来的请求后,就进行处理,在讲解servlet是怎么处理的之前,先看一下servlet家族的接口定义和类的继承关系:
Sun公司提供了javax.servlet.http 和javax.servlet两个扩展包来开发Servlet。这两个包属于Java的标准扩展Servlet API。
javax.servlet包提供了控制Servlet生命周期所必需的Servlet接口。
javax.servlet.http包提供了从Servlet接口派生出的专门用于处理HTTP请求的抽象类和一般的工具类。
由上图我们可以看出,Sun公司定义了一系列servlet接口,这些接口明确了servlet程序的职责和执行过程。还为我们提供了2个实现类(GenericServlet和HttpServlet)。这些接口和实现类了解即可,我们在编写自己的servlet类时,只需要继承HttpService并重写doGet方法即可。
我们自己的servlet程序代码格式如下:
package com.jimmy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HttpServletDemo1 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1,获取表单数据
//2,调用业务逻辑
//3,分发转向
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
各个类中的具体方法请查阅javaEE_API文档,点击下载javaeeAPI文档
servlet的处理过程由servlet类中的doGet()方法控制,该方法通过其参数(request和response)来接收和响应请求。request和response对象代表着请求和响应,那我们要获取客户端提交过来的数据,只需要找request对象即可。要向容器输出数据,只需要找response对象即可。
public void doGet(HttpServletRequest request, HttpServletResponse response)
一,request对象
HttpServletRequest 对象代表客户端的请求,当客户端通过http协议访问服务器时,http请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得用户这些信息。方法详见API,常用的有如下这些:
1,获得客户机的请求头
——getHead(String name)
2,获得客户机的请求数据
——getParameter(String name)
3,request实现请求转发
——getRequestDispatcher().forward()
二,response对象
这个对象封装了向客户端发送数据,发送响应头,发送响应状态码的方法。常用方法如下:
1,向客户端输出数据
——getOutputStream().write(xxx)
2,发送http头
——setHeader()
3,response实现请求重定向
——sendRedirect()
Filter是一个过滤器,用于拦截指定资源之间的跳转,比如上面的例子,带有form表单的jsp页面要向loginServlet去跳转,而filter就可以在两者之间拦截一下,即先跳转到filter,filter处理过后,再跳往servlet,这个操作就叫过滤。filter是一个接口,它的结构和生命周期都很像servlet,但又不同。下面我们写一个类实现Filter接口.
package com.jimmy.Filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class FilterDemo2 implements Filter{
//初始化函数:tomcat一启动,即初始化Filter对象
public void init(FilterConfig filterConfig) throws ServletException { }
//拦截函数:同样有req和res对象,就可以获得信息记性处理。并添加了FilterChain对象,该对象只有一个doFilter()方法,就是放行方法。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { }
//销毁方法:服务器关闭即销毁filter对象
public void destroy() { }
}
filter程序的执行流程同样由服务器来控制,那么服务器怎么知道什么时候该拦截,什么时候不该拦截?同样的,还是由配置文件(web.xml)来决定。下面来看filter的配置项。
<web-app version="2.5"
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_2_5.xsd">
<servlet>
<servlet-name>LoginServletservlet-name>
<servlet-class>com.jimmy.servlet.LoginServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>LoginServletservlet-name>
<url-pattern>/servlet/LoginServleturl-pattern>
servlet-mapping>
<filter>
<filter-name>FilterDemo1filter-name>
<filter-class>com.jimmy.Filter.FilterDemo1filter-class>
filter>
<filter-mapping>
<filter-name>FilterDemo1filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
可以看到,filter的配置项跟servlet的配置项很像。其中,filter标签中的filter-name,filter-class标签分别表示filter的名字和类所在的位置。但是,filter-mapping标签中的url-pattern标签代表的是你要拦截的位置。如果其值是“/*”,表示访问该应用中的任何资源时,都会被拦截,被拦截的信息按照web.xml中设置的路径找到相应的filter类,进行处理后再传递给真正要访问的资源。如果其值为“/servlet/LoginServlet”,则只拦截访问特定servlet的资源。
过滤器filter可以做很多事情,比如项目中的自动登录功能。再看一个简单的例子,通常我们在处理web编码问题的时候,都会在servlet程序中添加一句话:request.setCharacterEncoding(“utf-8”)。如果有很多个servlet,则每个servlet都要写上这句话,代码冗余了。那么,我们可以写一个过滤器,将处理编码的这句话写在过滤器中,每次访问servlet都会执行这段代码。下面来看程序:
首先编写一个jsp页面,包含一个form:
<body>
This is my JSP page. <br>
<form action="/FilterDemo2/servlet/LoginServlet" method="post">
请输入用户名:<input type="text" name="username"><br>
<input type="submit" value="提交">
form>
body>
再写一个servlet,获取表单数据,并打印,如果是中文,则会乱码。
package com.jimmy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username = request.getParameter("username");
System.out.println(username);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
通常的解决方法是在doGet()方法的第一行写:request.setCharacterEncoding(“utf-8”),这样做的问题上面已经说过了,我们来写一个filter
package com.jimmy.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FilterDemo1 implements Filter{
public void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//1,转换类型
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
//2,处理业务
req.setCharacterEncoding("utf-8");
//3,放行
chain.doFilter(request, response);
}
public void destroy() {}
}
最后编写web.xml
<web-app version="2.5"
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_2_5.xsd">
<display-name>display-name>
<servlet>
<servlet-name>LoginServletservlet-name>
<servlet-class>com.jimmy.servlet.LoginServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>LoginServletservlet-name>
<url-pattern>/servlet/LoginServleturl-pattern>
servlet-mapping>
<filter>
<filter-name>FilterDemo1filter-name>
<filter-class>com.jimmy.filter.FilterDemo1filter-class>
filter>
<filter-mapping>
<filter-name>FilterDemo1filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<welcome-file-list>
<welcome-file>index.jspwelcome-file>
welcome-file-list>
web-app>
再输入中文,也不会出现乱码了。这样提高了代码的可扩展性。