java Servlet介绍
Java Servlet是java EE标准规范之一。Servlet是Java Web 技术的核心基础,
因此掌握好Servlet是很有必要的。掌握Servlet的工作原理是成为一名合格的
Java Web技术开发人员的基本要求。
Servlets are very important topic of Java EE and all of the web
applications framework such as Spring and Struts are built on top of it.
Servlet is J2EE server driven technology to create web
applications in java. The javax.servlet and javax.servlet.http
packages provide interfaces and classes for writing our own servlets.
All servlets must implement the javax.servlet.Servlet interface,
which defines servlet lifecycle methods. When implementing a
generic service, we can extend the GenericServlet class provided
with the Java Servlet API. The HttpServlet class provides
methods, such as doGet() and doPost(), for handling HTTP-
specific services.
Most of the times, web applications are accessed using HTTP
protocol and thats why we mostly extend HttpServlet class.
备注:Servlet是java web开发的核心基础一定要深入掌握,只有在开发Java web有一段时间之后就会发现Servlet很重要,你可以不学习jsp但是一定要掌握好Servlet
Servlet核心
1、Servlet 体系结构
![Servlet接口层次](http://i.imgur.com/KYSRfCL.png)
Servlet
Defines methods that all servlets must implement.
A servlet is a small Java program that runs within a Web server.
Servlets receive and respond to requests from Web clients,
usually across HTTP, the HyperText Transfer Protocol.
备注:通常我们写servlet不会直接实现该接口而是通过继承HttpServlet来实现。
ServletConfig
A servlet configuration object used by a servlet container
to pass information to a servlet during initialization.
备注:ServletConfig是由一个Servlet所持有的,保存着这个Servlet的配置参
数。通常是配置在web.xml中,servlet3.0之后也可以使用WebInitParam注解来配置。
ServletContext
Defines a set of methods that a servlet uses to communicate with
its servlet container, for example, to get the MIME type of a
file, dispatch requests, or write to a log file.
There is one context per "web application" per Java Virtual Machine.
备注:ServletContext的作用范围是整个web应用,为了方便可以理解为静态变量,该web应用下所有servlet都可以持有其引用。
ServletRequest
Defines an object to provide client request information to a
servlet. The servlet container creates a ServletRequest object
and passes it as an argument to the servlet's service method.
备注:该对象为包装Client请求信息的对象,通常使用最多的是HttpServletRequest。基于http协议请求。
ServletResponse
Defines an object to assist a servlet in sending a response to
the client.The servlet container creates a ServletResponse
object andpasses it as an argument to the servlet's service method.
备注:该对象为包装web服务器响应信息的对象,通常使用最多的是HttpServletResponse。基于http协议响应。
**备注:**ServletConfig作用对象是一个Servlet,ServletContext作用对象是整个web应用。
2、Servlet的完整生命周期
Servlet Code
为了观察Servlet的生命周期,最好的办法就是通过实现Servlet接口来实现一个Servlet服务。
public interface Servlet {
/**
* The servlet container calls the init
* method exactly once after instantiating the servlet.
* The init method must complete successfully
* before the servlet can receive any requests.
*/
//1、初始化方法
public void init(ServletConfig config) throws ServletException;
//2、servlet最核心方法,处理请求响应方法
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
//3、servlet卸载的时候调用销毁方法
public void destroy();
public ServletConfig getServletConfig();
public String getServletInfo();
}
3、Servlet域对象(ServletRequest、HttpSession、ServletContext)
ServletRequest域对象:
请求域对象,只要是转发或包含(include)不是重定向那么请求域对象生命周期还有效。
HttpSession域对象:
会话域对象,只要用户Client没有关闭与服务器的连接那么会话域对象生命周期还有效。
ServletContext域对象:
应用级别域对象,只要web applicaiton应用没有关闭那么应用域对象生命周期还有效。
备注:
生命周期越久的域对象其消耗占用的资源就越多,所以能够使用请求域对象时就
不要使用会话域对象,能够使用会话域对象时就不要用应用级别域对象。
Servlet attributes are used for inter-servlet communication, we
can set, get and remove attributes in web application. There are
three scopes for servlet attributes – request scope, session
scope and application scope.
4、Servetl 3.x新特性 基于注解开发
在Servlet3.0以前使用servlet开发java web应用通常需要在web.xml中配置servlet和mapper映射,相对而言比较繁琐,Servlet3.0之后支持了使用注解方式来配置Servlet从而可以达到零配置和极大减少配置文件。
WebServlet注解
/**
* Annotation used to declare a servlet.
* This annotation is processed by the container at deployment time,
* and the corresponding servlet made available at the specified URL
* patterns.
* @since Servlet 3.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
//The name of the servlet
String name() default "";
//value和urlPatterns二选一即可
//The URL patterns of the servlet
String[] value() default {};
//The URL patterns of the servlet
String[] urlPatterns() default {};
//The load-on-startup order of the servlet
int loadOnStartup() default -1;
//The init parameters of the servlet
WebInitParam [] initParams() default {};
//Declares whether the servlet supports asynchronous operation mode.
boolean asyncSupported() default false;
}
备注:该注解用来声明该Class为Servlet并可以配置servlet路径。
//基于xml配置servlet方式
<!-- 配置Servlet class -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.xuyi.servlet.HelloServlet</servlet-class>
</servlet>
<!-- 配置Servlet 映射路径 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
WebInitParam注解
/**
* This annotation is used on a Servlet or Filter implementation class
* to specify an initialization parameter.
* @since Servlet 3.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam {
//Name of the initialization parameter
//配置参数key
String name();
//Value of the initialization parameter
//配置参数value
String value();
}
备注:该注解通常用来配置Servlet的初始参数。
WebFilter注解
/**
* Annotation used to declare a servlet filter.
* This annotation is processed by the container at deployment time,
* and the corresponding filter applied to the specified URL patterns,
* servlets, and dispatcher types.
* @since Servlet 3.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebFilter {
//The init parameters of the filter
WebInitParam[] initParams() default {};
//The names of the servlets to which the filter applies.
String[] servletNames() default {};
//The URL patterns to which the filter applies
String[] value() default {};
//value或urlPatterns二选其一即可
//The URL patterns to which the filter applies
String[] urlPatterns() default {};
//The dispatcher types to which the filter applies
DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
//Declares whether the filter supports asynchronous operation mode.
boolean asyncSupported() default false;
}
//备注:WebFilter和WebServlet比较类似。
WebListener注解
/**
* This annotation is used to declare a WebListener.
*
* Any class annotated with WebListener must implement one or more of
* the {@link javax.servlet.ServletContextListener},
* {@link javax.servlet.ServletContextAttributeListener},
* {@link javax.servlet.ServletRequestListener},
* {@link javax.servlet.ServletRequestAttributeListener},
* {@link javax.servlet.http.HttpSessionListener}, or
* {@link javax.servlet.http.HttpSessionAttributeListener} interfaces.
*
* @since Servlet 3.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebListener {
// Description of the listener
String value() default "";
}
备注:该注解用来配置监听器,需要满足一定条件的类才能配置。
load-on-startup配置的详解
1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
2)它的值必须是一个整数,表示servlet应该被载入的顺序当值为0或者大于0
时,表示容器在应用启动时就加载并初始化这个servlet;
3)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
4)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
5)当值相同时,容器就会自己选择顺序来加载。
x,中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。
5、Servlet Session对象
HttpSession对象的创建和维护都是在服务端维护的,保存在服务器内存中,客户
端(浏览器)只是保存了我们的jsessionId而已。在分布式场景下通常会使用数据
库来管理Session对象(关系型或noSql数据库).session是以cookie或URL重写
为基础的,默认使用cookie来实现。
Provides a way to identify a user across more than one page
request or visit to a Web site and to store information about that user.
The servlet container uses this interface to create a session
between an HTTP client and an HTTP server. The session persists
for a specified time period, across more than one connection or
page request from the user. A session usually corresponds to one
user, who may visit a site many times. The server can maintain a
session in many ways such as using cookies or rewriting URLs.
This interface allows servlets to
View and manipulate information about a session, such as
the session identifier, creation time, and last accessed time
Bind objects to sessions, allowing user information to persist
across multiple user connections
When an application stores an object in or removes an object from a
session, the session checks whether the object implements
{@link HttpSessionBindingListener}. If it does,
the servlet notifies the object that it has been bound to or unbound
from the session. Notifications are sent after the binding methods complete.
For session that are invalidated or expire, notifications are sent after
the session has been invalidated or expired.
When container migrates a session between VMs in a distributed container
setting, all session attributes implementing the {@link HttpSessionActivationListener}
interface are notified.
A servlet should be able to handle cases in which
the client does not choose to join a session, such as when cookies are
intentionally turned off. Until the client joins the session,
isNew returns true. If the client chooses
not to join
the session, getSession will return a different session
on each request, and isNew will always return
true.
Session information is scoped only to the current web application
(ServletContexte), so information stored in one context
will not be directly visible in another.
HttpSession Code
public interface HttpSession {
//获得创建session的时间(时间戳)
public long getCreationTime();
//Returns a string containing the unique identifier assigned to this session.
//获得session对象的唯一标识jessionID
public String getId();
//获得上次用户访问时间
public long getLastAccessedTime();
//获得当前web应用的ServletContext对象
public ServletContext getServletContext();
//设置session最长生存时间(单位是秒)
public void setMaxInactiveInterval(int interval);
//获得session最长生存时间(单位是秒)
public int getMaxInactiveInterval();
//获得session对象name对应的属性值
public Object getAttribute(String name);
//获得session对象里的所有属性值
public Enumeration<String> getAttributeNames();
//为session对象对应的name赋值属性
public void setAttribute(String name, Object value);
//删除session对象对应的name属性值
public void removeAttribute(String name);
//使整个session对象失效
public void invalidate();
public boolean isNew();
}
备注:
要清楚Session的实现原理和应用场景,在用户规模很大或分布式场景下通常需
要借组数据库来实现分布式Session对象的管理
6、Servelt Cookies对象
7、Servlet Filter对象
Filter介绍
A filter is an object that performs filtering tasks on either the
request to a resource (a servlet or static content), or on the response
from a resource, or both.
Filters perform filtering in the doFilter method.
Every Filter has access to a FilterConfig object from which it can obtain
its initialization parameters, and a reference to the ServletContext which
it can use, for example, to load resources needed for filtering tasks.
Filters are configured in the deployment descriptor of a web
application.
Examples that have been identified for this design are:
Authentication Filters
Logging and Auditing Filters
Image conversion Filters
Data compression Filters
Encryption Filters
Filters that trigger resource access events
XSL/T filters
Mime-type chain Filter
Filter Code
public interface Filter {
//初始化Filter的初始化参数
public void init(FilterConfig filterConfig) throws ServletException;
/**
* The doFilter method of the Filter is called by the
* container each time a request/response pair is passed through the
* chain due to a client request for a resource at the end of the chain.
* The FilterChain passed in to this method allows the Filter to pass
* on the request and response to the next entity in the chain.
*/
//核心方法doFilter(request,response)
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;
//销毁方法
public void destroy();
}
备注:
Filter和Servlet非常的类似,甚至可以说Filter是由Servlet演变而来的。我
们既可以基于在web.xml配置文件中配置filter也可以给予@WebFilter注解来配置Filter.
Filter主要作用
Authentication Filters(认证)
Logging and Auditing Filters(日志和审核)
Image conversion Filters(图片转换)
Data compression Filters(数据压缩)
Encryption Filters(密码过滤)
Filters that trigger resource access events
XSL/T filters
Mime-type chain Filter
参考:http://blog.sina.com.cn/s/blog_98014c7e0101h3o9.html
开发常用:统一字符编码过滤、用户权限控制
备注:曾经很经典的Struts2.x框架就是基于过滤器(责任链模式)实现的。
8、Servlet Listener对象
java servlet开发中最常涉及到的Listener如下:
javax.servlet.ServletContextListener//最常用的
javax.servlet.ServletContextAttributeListener
javax.servlet.ServletRequestListener
javax.servlet.ServletRequestAttributeListener
javax.servlet.http.HttpSessionListener
javax.servlet.http.HttpSessionAttributeListener
public interface XxxListener extends EventListener {
public void XxxInitialized(XxxEvent sce);
public void XxxDestroyed(XxxEvent sce);
}
public class XxxEvent extends java.util.EventObject {
public Xxx getXxx () {
return Xxx;
}
}
//通常Listener接口会有两个方法一个创建一个销毁方法,其入参引用通常都是可以获得该对象的。
备注:监听器是基于事件绑定监听的,其实GUI开发中事件点击就有监听器使用。
9、Servlet 文件上传(3.x之前和之后的区别使用)
在Servlet3.0以前我们通常会借助于common fileupload组件来实现
在Servlet3.0之后我们可以直接使用Part对象的write(String fileName)方法来实现。
httpServletRequest对象的getPart(String name)方法
public Part getPart(String name) throws IOException, ServletException;
Part对象的write(String fileName)方法
public void write(String fileName) throws IOException;
Code Example
@WebServlet(urlPatterns = "/upLoadServlet", name = "upLoadServlet")
//千万不要忘记这个注解
@MultipartConfig
public class UpLoadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//支持一个或多个文件上传
String savePath = "e:" + File.separator + "upload";
Collection<Part> parts = req.getParts();
for (Part part : parts) {
part.write(savePath + File.separator + getFileName(part));
//如果不需要获取文件名字就更简单了
}
}
/**
* 获取上传的文件名
* @param part
* @return
*/
private String getFileName(Part part) {
String fileNameExtractorRegex = "filename=\".+\"";
String headString = part.getHeader("content-disposition");
String fileName = null;
Pattern pattern = Pattern.compile(fileNameExtractorRegex);
Matcher matcher = pattern.matcher(headString);
if (matcher.find()) {
fileName = matcher.group();
fileName = fileName.substring(10, fileName.length() - 1);
}
return fileName;
}
}
备注:
使用Servlet3.x上传文件就是那么简单的几行代码就可以完成,当然实际开发我
们会使用web框架比如spring mvc上传文件也是非常方便的。
不管是那个版本的都要求上传文件是request type为multipart/form-data。否则都会失败。
参考:http://pisces-java.iteye.com/blog/723125
10、Async Servlet
Servlet3.x之后支持异步处理请求
Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:首先,
Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;接着,
调用业务接口的某些方法,以完成业务处理;最后,根据处理的结果提交响应,
Servlet 线程结束。其中第二步的业务处理通常是最耗时的,这主要体现在数据
库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状
态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而
得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常
是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。
Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0
的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:首先,
Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接
着,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至
容器,此时 Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直
接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象
的引用),或者将请求继续转发给其它 Servlet。如此一来, Servlet 线程不
再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即
返回。
AsyncServlet Code
/**
* Servlet3.x的异步处理请求机制
* @author xuyi
*/
@WebServlet(urlPatterns = "/asyncServlet", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这个设置很重要,如果不设置的话看不出效果啊?
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.println("begin time:" + new Date());
out.flush();
// 获得异步上下文对象(主要包含request、response对象)
AsyncContext asyncContext = req.startAsync();
// 启动异步处理线程处理请求繁忙的任务
// 这里我是为了方便演示只是简单的新建一个线程,其实可以使用线程池(ThreadPoolExecutor)来管理线程,这样对于企业级开发会更节约资源。
new Thread(new ExecuteThread(asyncContext, 5)).start();
out.println("end Servlet time" + new Date());
out.flush();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(req, resp);
}
}
Servlet 3.0 还为异步处理提供了一个监听器,使用 AsyncListener 接口表示
ExecuteThread
public class ExecuteThread implements Runnable {
private AsyncContext asyncContext;
private int time;
public ExecuteThread(AsyncContext asyncContext, int time) {
this.asyncContext = asyncContext;
this.time = time;
}
public void run() {
doTask(time);// 做繁忙的任务
PrintWriter out;
try {
out = this.asyncContext.getResponse().getWriter();
out.println("end time:" + new Date());
out.flush();
this.asyncContext.complete();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 做繁忙的IO任务
*/
private void doTask(int time) {
try {
Thread.sleep(1000 * time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}