java web 中servlet原理以及创建方法详解内含众多知识点
2017-12-14 09:43
701 查看
servlet原理
1.什么是servlet?
servlet是用java编写的服务器端程序,采用请求——响应模式提供web服务,是运行在服务器端,
Servlet是服务器端程序,用来响应客户请求,动态生成响应,继承HttpServlet类,每个Servlet为一个组件,必须部署到Tomcat中才能运行
![](https://oscdn.geek-share.com/Uploads/Images/Content/201712/8f0cb0ac98702289a0b2961596cd80cd)
2.servlet与JSP有什么关系?
JSP在运行时首先会编译一个servlet,因此,servlet是JSP的基础,
实际JSP是servlet的衍生,JSP本质还是servlet
3.前台页面的创建【form表单的编写】:
3.1 form表单的编写方法有doget和dopost方法
两者的区别在于
一、生成方式get方式有四种:1)直接在URL地址栏中输入URL。2)网页中的超链接。3)form中method为get。4)form中method为空时,默认是get提交。post只知道有一种:form中method属性为post。2、数据传送方式get方式:表单数据存放在URL地址后面。所有get方式提交时HTTP中没有消息体。post方式:表单数据存放在HTTP协议的消息体中以实体的方式传送到服务器。3、服务器获取数据方式GET方式:服务器采用request.QueryString来获取变量的值。POST方式:服务器采用request.Form来获取数据。4、传送的数据量GET方式:数据量长度有限制,一般不超过2kb。因为是参数传递,且在地址栏中,故数据量有限制。POST方式:适合大规模的数据传送。因为是以实体的方式传送的。5、安全性GET方式:安全性差。因为是直接将数据显示在地址栏中,浏览器有缓冲,可记录用户信
4000
息。所以安全性低。POST方式:安全性高。因为post方式提交数据时是采用的HTTP post机制,是将表单中的字段与值放置在HTTP HEADER内一起传送到ACTION所指的URL中,用户是看不见的。6、在用户刷新时GET方式:不会有任何提示、POST方式:会弹出提示框,问用户是否重新提交
1. get是从服务器上获取数据,post是向服务器传送数据。2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。5. get安全性非常低,post安全性较高。但是执行效率却比Post方法好。 建议: 1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;二、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式; Servlet的doGet/doPost 是在 javax.servlet.http.HttpServlet 中实现的 doGet:处理GET请求
doPost:处理POST请求
当发出客户端请求的时候,调用service 方法并传递一个请求和响应对象。Servlet首先判断该请求是GET 操作还是POST 操作。然后它调用下面的一个方法:doGet 或 doPost。如果请求是GET就调用doGet方法,如果请求是POST就调用doPost方法。doGet和doPost都接受请求(HttpServletRequest)和响应(HttpServletResponse)。 get只有一个流,参数附加在url后,地址行显示要传送的信息,大小个数有严格限制且只能是字符串,大小限制在1024KB。post的参数是通过另外的流传递的, 不通过url,所以可以很大,也可以传递二进制数据,如文件的上传。 get通过URL提交的参数会显示在地址栏中,这在系统的安全方面可能带来问题;post提交的参数不会显示在地址栏中。这样post就可以提高get的安全性能,避免数据的泄露。 当form框里面的method为get时,执行doGet方法,使用get提交就必须在服务器端用doGet()方法接收;当form框里面的method为post时,执行doPost方法,使用post提交就必须在服务器端用doPost()方法接收。 在request请求里面,编码转换;get方法得到的内容每一个都要进行编码转换,而post方法则只要设置request.setCharacterEncoding("UTF-8")就可以,不要再从request得到的每个数据进行编码转换了。
4.servlet的生命周期?
1)加载和实例化加载和实例化servlet是由servlet容器实现的,加载servlet之后,容器会通过java的反射机制来创建servlet的实例。
2)初始化
在servlet的实例创建后,容器会调用servlet的init()方法来初始化该servlet对象。初始化的目的是让servlet对象在处理客户端请求前先完成一些初始化工作。对于每个servlet实例,只会调用一次init()方法。
3)服务/执行
当客户端请求到来后,servlet容器首先针对该请求创建servletRequest和servletResponse两个对象,然后servlet容器会自动调用servlet的service()方法来响应客户端的请求,同时把servletRequest和servletResponse两个对象传给service()方法,通过servletRequest对象,servlet实例可以获得客户端的请求信息,处理完请求后则将响应信息放在servletResponse对象中,最后销毁servletResponse和servletResponse对象。
注意:在service()方法调用前,init()方法必须已经成功执行。
4)销毁
当servlet实例需要从服务中移除时,容器会调用destory()方法,让该实例释放掉它所使用的资源,并将实例中的数据保存到持久的存储设备中,之后,servlet实例便会被java的垃圾回收器所回收。
在servlet的整个生命周期中,其初始化和销毁都只发生一次,service()方法的执行次数取决于servlet被客户端所访问的次数。构造方法:init():该方法在javax.servlet.Servlet接口中定义。创建servlet实例时会调用init()方法,在init()方法中完成类似于构造方法的初始化功能,其参数为servletConfig的实例。init()方法结束后,servlet就可以接受客户端请求。在servlet的整个生命周期中,只执行一次init()方法。service():该方法用来响应客户端发出的请求,service()方法使用servletResquest接口和servletResponset接口的对象作为参数,其中,servletRequest对象用来处理请求,servletResponse对象用来发送响应。service()方法执行时会检查HTTP请求的类型,并相应地调用doGet()、doPost()等方法,因此通常的做法是,不使用service()方法直接使用doGet()和doPost()等方法来处理请求。service()方法的语法形式如下:public void service (servletRequest request,ServletResponse response)throws ServletException ,IOEception其中,resquest是ServletRequest接口的对象,它作为参数来接收和存储客户端请求,response是ServletResponse接口的对象,它包含了servlet做出的响应。doGet()/doPost():destroy():当不再需要servlet实例或重新装入时,destory()方法被调用。使用destory()方法可以释放掉所有在init()方法中申请的资源,一个servlet实例一旦终止,就不允许再次被调用。只能等待被卸载。destory()方法通常用来执行一些清理任务,在destory()方法中一般安排释放资源的代码。原理:从Servlet的第一次请求开始(因为这时Servlet对象还没有创建),先执行new的操作(构造方法),再调用init()进行初始化。接着等待请求的到来,一旦有请求到来,service()方法被调用,根据请求类型决定调用doGet()/doPost()。只要有请求到来就会重复service()-->doGet()/doPost()这一过程。当服务器重启或者关闭时,destroy()方法被调用,进行销毁Servlet对象工作 注意:修改了Servlet文件中的代码后要重启Tomcat。5.数据的传递方式:
1).从页面到Servlet
request.getParameter("param");2)从servlet到servlet或JSP页面
6.Web.xml文件中servlet的配置?创建web.xml方法:你的项目上右键找到javaee工具点击第二项,web.xml就是告诉服务器toomcat你的项目有哪些服务信息
<servlet> <servlet-name>name</servlet-name> <servlet-class>classname</servlet-class></servlet><servlet-mapping> <servlet-name>name</servlet-name> <url-path>/xxxx</url-path></servlet-mapping>7.页面跳转有那几种方式?内部跳转和外部跳转对于内部跳转,地址栏没有变化,对于外部跳转,地址栏有变化
请求转发(内部跳转)与重定向(外部跳转)
请求转发:由服务器内部将请求转发给另一个Servlet/JSP资源,由其他资源去处理。在整个过程中,客户端(浏览器)没有意识到请求已转向,请求对象和响应对象都只有一个,浏览器的地址栏没有变化
页面重定向:由服务器向客户端发出一个特殊的响应,指导客户端重新向另一个资源发出新的请求。在这一过程中,客户端是清楚重定向的目标地址的,请求对象和响应对象是重新创建的,地址栏有变化
8.重定向和转发请求转发,又称内部跳转,用ResquestDispatcher对象的forward()方法。请求重定向,又称外部跳转,用Response对象的sendRedirect()方法。
通常接收到一个请求并处理后会让客户端访问一个指定的页面,这个过程可以通过两种方式来完成。建立一个登陆页面如果用户名密码正确则返回登陆成功页面,否则返回登陆失败页面方式一:
重定向:使用response对象的sendRedirect(“demo.html”);原理是客户端向服务器发一次登陆请求,服务端通过请求对象获取请求重定向对象,帮助客户端完成了要去的目的地,也就是说客户端只发了一次请求,剩下的事情都由服务端帮其完成
请求完的结果:[b]地址栏中显示第一次而且仅这一次的地址,而显示的内容也许是服务端处理后的最后页面,而非第一次请求的内容。也就是说在该例子中:登陆是通过一个loginservlet来处理的,无论是否成功地址栏只显示servlet,而不显示成功页面或者失败页面地址。因为servlet就是客户端的第一次请求。也就是地址栏不发生变化。而且该种方式只能定向到web应用程序内的资源。[/b][b]因为是一次请求,而剩下的请求都由服务器来完成,那么请求重定向对象的forward方法会将客户端的请求以及应答对象向下个页面传递,也就是说自始自终都是同一个请求。这样的好处是可以在下个页面比如jsp页面中对该请求进行操作等。
[/b]方式二: 请求转发器:通过request对象的getRequestDispatcher(“demo.html”);获取请求转发器对象RequestDispatcher,使用该对象的forward()方法进行转发。 原理是客户端向服务端发一次登陆请求,服务端对其进行处理后,通过应答对象告诉客户端要去的目的,那么客户端就再一次向服务器发一个请求,请求目的页面。请求完的结果: 在地址栏中显示最后一次服务端返回的请求地址。该地址可以是应用程序内部的资源,也可以是任意一个网络资源。 比如:可以定一个绝对地址百度。也就是地址栏发生变化。因为是多次请求,所以每一次的请求和应答对象都不同。
请求转发与页面重定向的区别:1、 请求转发只能将请求转发给同一个WEB应用程序中的其他资源,而重定向不仅可以重定向到同一个WEB应用程序中的其他资源,还可以重定向到其他的应用程序的资源。2、 请求转发浏览器的URL不会改变,而重定向浏览器的URL会改变3、 请求转发在页面跳转时使用的是相同的request和response,而重定向在页面跳转时是不同的request和response4、 请求转发在页面跳转时一共只有1次请求和1次响应,而重定向在页面跳转时一共有2次请求和2次响应
9.解决乱码问题的方法?乱码出现时期:
请求时 request
应答时 response
通过两个对象的setCharacterEncoding(“utf-8”)方法进行字符
集指定。
–request对象的该方法是对请求体中的数据进行编码。所以对post
请求有效。而且还要在获取参数前调用该方法。
–response对象的该方法必须在getWriter之前调用,执行级别高
于。
解决方法:
解决乱码问题中,设置请求内容的字符编码?request.setCharacterEncoding(“utf-8”);
解决乱码问题中,设置输出内容及字符编码
response.setContentType(“text/html;charset=utf-8”);关键代码:
request.setCharacterEncoding(“utf-8”);response.setContentType(“text/html;charset=utf-8”)或者response.setCharacterEncoding(“utf-8”);---------------------------------------------------------------------------------------------------------------------Servlet3.0中Servlet的使用目录1.注解配置2.异步调用3.文件上传 相对于之前的版本,Servlet3.0中的Servlet有以下改进:l 支持注解配置。l 支持异步调用。l 直接有对文件上传的支持。 在这篇文章中我将主要讲这三方面的应用示例。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* Servlet3.0支持使用注解配置Servlet。我们只需在Servlet对应的类上使用@WebServlet进行标注,
* 我们就可以访问到该Servlet了,而不需要再在web.xml文件中进行配置。@WebServlet的urlPatterns
* 和value属性都可以用来表示Servlet的部署路径,它们都是对应的一个数组。
*/
@WebServlet(name="exampleServlet", urlPatterns="/servlet/example")
public class ExampleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello User.");
}
}
初始化参数 使用@WebServlet时也可以配置初始化参数,它是通过@WebServlet的initParams参数来指定的。initParams是一个@WebInitParam数组,每一个@WebInitParam代表一个初始化参数。Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 带初始化参数的Servlet
* WebServlet的属性initParams可以用来指定当前Servlet的初始化参数,它是一个数组,
* 里面每一个@WebInitParam表示一个参数。
*/
@WebServlet(value="/servlet/init-param", initParams={@WebInitParam(name="param1", value="value1")})
public class WebInitParamServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Enumeration<String> paramNames = this.getServletConfig().getInitParameterNames();
String paramName;
while (paramNames.hasMoreElements()) {
paramName = paramNames.nextElement();
resp.getWriter().append(paramName + " = " + this.getServletConfig().getInitParameter(paramName));
}
resp.getWriter().close();
}
}
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 支持异步返回的Servlet
* 对于Servlet的异步返回,首先我们必须指定@WebServlet的asyncSupported属性为true(默认是false),同时在它之前的Filter
* 的asyncSupported属性也必须是true,否则传递过来的request就是不支持异步调用的。
*
*/
@WebServlet(value="/servlet/async", asyncSupported=true)
public class AsyncServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
final PrintWriter writer = resp.getWriter();
writer.println("异步之前输出的内容。");
writer.flush();
//开始异步调用,获取对应的AsyncContext。
final AsyncContext asyncContext = req.startAsync();
//设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
asyncContext.setTimeout(10*1000L);
//新起线程开始异步调用,start方法不是阻塞式的,它会新起一个线程来启动Runnable接口,之后主程序会继续执行
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5*1000L);
writer.println("异步调用之后输出的内容。");
writer.flush();
//异步调用完成,如果异步调用完成后不调用complete()方法的话,异步调用的结果需要等到设置的超时
//时间过了之后才能返回到客户端。
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
});
writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
writer.flush();
}
}
对于一个Servlet如果要支持异步调用的话我们必须指定其asyncSupported属性为true(默认是false)。使用@WebServlet注解标注的Servlet我们可以直接指定其asyncSupported属性的值为true,如:@WebServlet(value=”/servlet/async”, asyncSupported=true)。而对于在web.xml文件中进行配置的Servlet来说,我们需要在配置的时候指定其asyncSupported属性为true。Xml代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>xxx</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>xxx</servlet-name>
<url-pattern>xxx</url-pattern>
</servlet-mapping>
Servlet的异步调用程序的关键是要调用当前HttpServletRequest的startAsync()方法。至于利用返回的AsyncContext来新起一个线程进行异步处理就不是那么的必须了,因为在HttpServletRequest startAsync()之后,我们可以自己新起线程进行异步处理。Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
@WebServlet(value="/servlet/async", asyncSupported=true)
public class AsyncServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
final PrintWriter writer = resp.getWriter();
writer.println("异步之前输出的内容。");
writer.flush();
//开始异步调用,获取对应的AsyncContext。
final AsyncContext asyncContext = req.startAsync();
//设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
asyncContext.setTimeout(10*1000L);
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5*1000L);
writer.println("异步调用之后输出的内容。");
writer.flush();
//异步调用完成
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(r);
//开启自己的线程进行异步处理
t.start();
writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
writer.flush();
}
}
异步调用监听器 当我们需要对异步调用做一个详细的监听的时候,比如监听它是否超时,我们可以通过给AsyncContext设置对应的监听器AsyncListener来实现这一功能。AsyncListener是一个接口,里面定义了四个方法,分别是针对于异步调用开始、结束、出错和超时的。Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 支持异步返回的Servlet
* 对于Servlet的异步返回,首先我们必须指定@WebServlet的asyncSupported属性为true(默认是false),同时在它之前的Filter
* 的asyncSupported属性也必须是true,否则传递过来的request就是不支持异步调用的。
*
*/
@WebServlet(value="/servlet/async2", asyncSupported=true)
public class AsyncServlet2 extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
final PrintWriter writer = resp.getWriter();
writer.println("异步之前输出的内容。");
writer.flush();
//开始异步调用,获取对应的AsyncContext。
final AsyncContext asyncContext = req.startAsync();
//设置当前异步调用对应的监听器
asyncContext.addListener(new MyAsyncListener());
//设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
asyncContext.setTimeout(10*1000L);
//新起线程开始异步调用,start方法不是阻塞式的,它会新起一个线程来启动Runnable接口,之后主程序会继续执行
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5*1000L);
writer.println("异步调用之后输出的内容。");
writer.flush();
//异步调用完成
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
});
writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
writer.flush();
}
/**
* 异步调用对应的监听器
* @author Yeelim
* @date 2014-2-8
* @mail yeelim-zhang@todaytech.com.cn
*/
private class MyAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("异步调用完成……");
event.getSuppliedResponse().getWriter().println("异步调用完成……");
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println("异步调用出错……");
event.getSuppliedResponse().getWriter().println("异步调用出错……");
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("异步调用开始……");
event.getSuppliedResponse().getWriter().println("异步调用开始……");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("异步调用超时……");
event.getSuppliedResponse().getWriter().println("异步调用超时……");
}
}
}
注: 对于正常执行的异步调用而言上述代码中开始是没有监听到的,只有在异步调用超时,重新执行异步任务的时候才有监听到异步调用的开始。不过如果需要监听异步第一次开始的话,我们可以在异步调用开始的时候做相应的监听器监听到异步调用开始时需要做的内容。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>xxx.xxx</servlet-class>
<multipart-config></multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>xxx</servlet-name>
<url-pattern>/servlet/xxx</url-pattern>
</servlet-mapping>
不管是基于注解的@MultipartConfig,还是基于web.xml文件配置的multipart-config,我们都可以给它们设置几个属性。l file-size-threshold:数字类型,当文件大小超过指定的大小后将写入到硬盘上。默认是0,表示所有大小的文件上传后都会作为一个临时文件写入到硬盘上。l location:指定上传文件存放的目录。当我们指定了location后,我们在调用Part的write(String fileName)方法把文件写入到硬盘的时候可以,文件名称可以不用带路径,但是如果fileName带了绝对路径,那将以fileName所带路径为准把文件写入磁盘。l max-file-size:数值类型,表示单个文件的最大大小。默认为-1,表示不限制。当有单个文件的大小超过了max-file-size指定的值时将抛出IllegalStateException异常。l max-request-size:数值类型,表示一次上传文件的最大大小。默认为-1,表示不限制。当上传时所有文件的大小超过了max-request-size时也将抛出IllegalStateException异常。 上面的属性是针对于web.xml中配置Servlet而言的,其中的每一个属性都对应了multipart-config元素下的一个子元素。对于基于注解配置的Servlet而言,@MultipartConfig的属性是类型的,我们只需把上述对应属性中间的杠去掉,然后把对应字母大写即可,如maxFileSize。 下面给出Servlet3.0中文件上传的一个示例。Html:Html代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
<form method="post" action="servlet/upload" enctype="multipart/form-data">
<input type="file" name="upload"/>
<input type="submit" value="upload"/>
</form>
对应Servlet:Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
@WebServlet("/servlet/upload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
Part part = req.getPart("upload");
//格式如:form-data; name="upload"; filename="YNote.exe"
String disposition = part.getHeader("content-disposition");
System.out.println(disposition);
String fileName = disposition.substring(disposition.lastIndexOf("=")+2, disposition.length()-1);
String fileType = part.getContentType();
long fileSize = part.getSize();
System.out.println("fileName: " + fileName);
System.out.println("fileType: " + fileType);
System.out.println("fileSize: " + fileSize);
String uploadPath = req.getServletContext().getRealPath("/upload");
System.out.println("uploadPath" + uploadPath);
part.write(uploadPath + File.separator +fileName);
}
}
对于Servlet3.0中的文件上传还有一个需要注意的地方,当我们把Part写入到硬盘以后,我们原先的Part(也就是之前的临时文件)可能已经删了,这个时候如果我们再次去访问Part的内容的话,那它就是空的,系统会抛出异常说找不到对应的文件。
1.什么是servlet?
servlet是用java编写的服务器端程序,采用请求——响应模式提供web服务,是运行在服务器端,
Servlet是服务器端程序,用来响应客户请求,动态生成响应,继承HttpServlet类,每个Servlet为一个组件,必须部署到Tomcat中才能运行
2.servlet与JSP有什么关系?
JSP在运行时首先会编译一个servlet,因此,servlet是JSP的基础,
实际JSP是servlet的衍生,JSP本质还是servlet
3.前台页面的创建【form表单的编写】:
3.1 form表单的编写方法有doget和dopost方法
两者的区别在于
一、生成方式get方式有四种:1)直接在URL地址栏中输入URL。2)网页中的超链接。3)form中method为get。4)form中method为空时,默认是get提交。post只知道有一种:form中method属性为post。2、数据传送方式get方式:表单数据存放在URL地址后面。所有get方式提交时HTTP中没有消息体。post方式:表单数据存放在HTTP协议的消息体中以实体的方式传送到服务器。3、服务器获取数据方式GET方式:服务器采用request.QueryString来获取变量的值。POST方式:服务器采用request.Form来获取数据。4、传送的数据量GET方式:数据量长度有限制,一般不超过2kb。因为是参数传递,且在地址栏中,故数据量有限制。POST方式:适合大规模的数据传送。因为是以实体的方式传送的。5、安全性GET方式:安全性差。因为是直接将数据显示在地址栏中,浏览器有缓冲,可记录用户信
4000
息。所以安全性低。POST方式:安全性高。因为post方式提交数据时是采用的HTTP post机制,是将表单中的字段与值放置在HTTP HEADER内一起传送到ACTION所指的URL中,用户是看不见的。6、在用户刷新时GET方式:不会有任何提示、POST方式:会弹出提示框,问用户是否重新提交
1. get是从服务器上获取数据,post是向服务器传送数据。2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。5. get安全性非常低,post安全性较高。但是执行效率却比Post方法好。 建议: 1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;二、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式; Servlet的doGet/doPost 是在 javax.servlet.http.HttpServlet 中实现的 doGet:处理GET请求
doPost:处理POST请求
当发出客户端请求的时候,调用service 方法并传递一个请求和响应对象。Servlet首先判断该请求是GET 操作还是POST 操作。然后它调用下面的一个方法:doGet 或 doPost。如果请求是GET就调用doGet方法,如果请求是POST就调用doPost方法。doGet和doPost都接受请求(HttpServletRequest)和响应(HttpServletResponse)。 get只有一个流,参数附加在url后,地址行显示要传送的信息,大小个数有严格限制且只能是字符串,大小限制在1024KB。post的参数是通过另外的流传递的, 不通过url,所以可以很大,也可以传递二进制数据,如文件的上传。 get通过URL提交的参数会显示在地址栏中,这在系统的安全方面可能带来问题;post提交的参数不会显示在地址栏中。这样post就可以提高get的安全性能,避免数据的泄露。 当form框里面的method为get时,执行doGet方法,使用get提交就必须在服务器端用doGet()方法接收;当form框里面的method为post时,执行doPost方法,使用post提交就必须在服务器端用doPost()方法接收。 在request请求里面,编码转换;get方法得到的内容每一个都要进行编码转换,而post方法则只要设置request.setCharacterEncoding("UTF-8")就可以,不要再从request得到的每个数据进行编码转换了。
4.servlet的生命周期?
1)加载和实例化加载和实例化servlet是由servlet容器实现的,加载servlet之后,容器会通过java的反射机制来创建servlet的实例。
2)初始化
在servlet的实例创建后,容器会调用servlet的init()方法来初始化该servlet对象。初始化的目的是让servlet对象在处理客户端请求前先完成一些初始化工作。对于每个servlet实例,只会调用一次init()方法。
3)服务/执行
当客户端请求到来后,servlet容器首先针对该请求创建servletRequest和servletResponse两个对象,然后servlet容器会自动调用servlet的service()方法来响应客户端的请求,同时把servletRequest和servletResponse两个对象传给service()方法,通过servletRequest对象,servlet实例可以获得客户端的请求信息,处理完请求后则将响应信息放在servletResponse对象中,最后销毁servletResponse和servletResponse对象。
注意:在service()方法调用前,init()方法必须已经成功执行。
4)销毁
当servlet实例需要从服务中移除时,容器会调用destory()方法,让该实例释放掉它所使用的资源,并将实例中的数据保存到持久的存储设备中,之后,servlet实例便会被java的垃圾回收器所回收。
在servlet的整个生命周期中,其初始化和销毁都只发生一次,service()方法的执行次数取决于servlet被客户端所访问的次数。构造方法:init():该方法在javax.servlet.Servlet接口中定义。创建servlet实例时会调用init()方法,在init()方法中完成类似于构造方法的初始化功能,其参数为servletConfig的实例。init()方法结束后,servlet就可以接受客户端请求。在servlet的整个生命周期中,只执行一次init()方法。service():该方法用来响应客户端发出的请求,service()方法使用servletResquest接口和servletResponset接口的对象作为参数,其中,servletRequest对象用来处理请求,servletResponse对象用来发送响应。service()方法执行时会检查HTTP请求的类型,并相应地调用doGet()、doPost()等方法,因此通常的做法是,不使用service()方法直接使用doGet()和doPost()等方法来处理请求。service()方法的语法形式如下:public void service (servletRequest request,ServletResponse response)throws ServletException ,IOEception其中,resquest是ServletRequest接口的对象,它作为参数来接收和存储客户端请求,response是ServletResponse接口的对象,它包含了servlet做出的响应。doGet()/doPost():destroy():当不再需要servlet实例或重新装入时,destory()方法被调用。使用destory()方法可以释放掉所有在init()方法中申请的资源,一个servlet实例一旦终止,就不允许再次被调用。只能等待被卸载。destory()方法通常用来执行一些清理任务,在destory()方法中一般安排释放资源的代码。原理:从Servlet的第一次请求开始(因为这时Servlet对象还没有创建),先执行new的操作(构造方法),再调用init()进行初始化。接着等待请求的到来,一旦有请求到来,service()方法被调用,根据请求类型决定调用doGet()/doPost()。只要有请求到来就会重复service()-->doGet()/doPost()这一过程。当服务器重启或者关闭时,destroy()方法被调用,进行销毁Servlet对象工作 注意:修改了Servlet文件中的代码后要重启Tomcat。5.数据的传递方式:
1).从页面到Servlet
request.getParameter("param");2)从servlet到servlet或JSP页面
6.Web.xml文件中servlet的配置?创建web.xml方法:你的项目上右键找到javaee工具点击第二项,web.xml就是告诉服务器toomcat你的项目有哪些服务信息
<servlet> <servlet-name>name</servlet-name> <servlet-class>classname</servlet-class></servlet><servlet-mapping> <servlet-name>name</servlet-name> <url-path>/xxxx</url-path></servlet-mapping>7.页面跳转有那几种方式?内部跳转和外部跳转对于内部跳转,地址栏没有变化,对于外部跳转,地址栏有变化
请求转发(内部跳转)与重定向(外部跳转)
请求转发:由服务器内部将请求转发给另一个Servlet/JSP资源,由其他资源去处理。在整个过程中,客户端(浏览器)没有意识到请求已转向,请求对象和响应对象都只有一个,浏览器的地址栏没有变化
页面重定向:由服务器向客户端发出一个特殊的响应,指导客户端重新向另一个资源发出新的请求。在这一过程中,客户端是清楚重定向的目标地址的,请求对象和响应对象是重新创建的,地址栏有变化
8.重定向和转发请求转发,又称内部跳转,用ResquestDispatcher对象的forward()方法。请求重定向,又称外部跳转,用Response对象的sendRedirect()方法。
通常接收到一个请求并处理后会让客户端访问一个指定的页面,这个过程可以通过两种方式来完成。建立一个登陆页面如果用户名密码正确则返回登陆成功页面,否则返回登陆失败页面方式一:
重定向:使用response对象的sendRedirect(“demo.html”);原理是客户端向服务器发一次登陆请求,服务端通过请求对象获取请求重定向对象,帮助客户端完成了要去的目的地,也就是说客户端只发了一次请求,剩下的事情都由服务端帮其完成
请求完的结果:[b]地址栏中显示第一次而且仅这一次的地址,而显示的内容也许是服务端处理后的最后页面,而非第一次请求的内容。也就是说在该例子中:登陆是通过一个loginservlet来处理的,无论是否成功地址栏只显示servlet,而不显示成功页面或者失败页面地址。因为servlet就是客户端的第一次请求。也就是地址栏不发生变化。而且该种方式只能定向到web应用程序内的资源。[/b][b]因为是一次请求,而剩下的请求都由服务器来完成,那么请求重定向对象的forward方法会将客户端的请求以及应答对象向下个页面传递,也就是说自始自终都是同一个请求。这样的好处是可以在下个页面比如jsp页面中对该请求进行操作等。
[/b]方式二: 请求转发器:通过request对象的getRequestDispatcher(“demo.html”);获取请求转发器对象RequestDispatcher,使用该对象的forward()方法进行转发。 原理是客户端向服务端发一次登陆请求,服务端对其进行处理后,通过应答对象告诉客户端要去的目的,那么客户端就再一次向服务器发一个请求,请求目的页面。请求完的结果: 在地址栏中显示最后一次服务端返回的请求地址。该地址可以是应用程序内部的资源,也可以是任意一个网络资源。 比如:可以定一个绝对地址百度。也就是地址栏发生变化。因为是多次请求,所以每一次的请求和应答对象都不同。
请求转发与页面重定向的区别:1、 请求转发只能将请求转发给同一个WEB应用程序中的其他资源,而重定向不仅可以重定向到同一个WEB应用程序中的其他资源,还可以重定向到其他的应用程序的资源。2、 请求转发浏览器的URL不会改变,而重定向浏览器的URL会改变3、 请求转发在页面跳转时使用的是相同的request和response,而重定向在页面跳转时是不同的request和response4、 请求转发在页面跳转时一共只有1次请求和1次响应,而重定向在页面跳转时一共有2次请求和2次响应
9.解决乱码问题的方法?乱码出现时期:
请求时 request
应答时 response
通过两个对象的setCharacterEncoding(“utf-8”)方法进行字符
集指定。
–request对象的该方法是对请求体中的数据进行编码。所以对post
请求有效。而且还要在获取参数前调用该方法。
–response对象的该方法必须在getWriter之前调用,执行级别高
于。
解决方法:
解决乱码问题中,设置请求内容的字符编码?request.setCharacterEncoding(“utf-8”);
解决乱码问题中,设置输出内容及字符编码
response.setContentType(“text/html;charset=utf-8”);关键代码:
request.setCharacterEncoding(“utf-8”);response.setContentType(“text/html;charset=utf-8”)或者response.setCharacterEncoding(“utf-8”);---------------------------------------------------------------------------------------------------------------------Servlet3.0中Servlet的使用目录1.注解配置2.异步调用3.文件上传 相对于之前的版本,Servlet3.0中的Servlet有以下改进:l 支持注解配置。l 支持异步调用。l 直接有对文件上传的支持。 在这篇文章中我将主要讲这三方面的应用示例。
1.注解配置
在以往我们的Servlet都需要在web.xml文件中进行配置(Servlet3.0同样支持),但是在Servlet3.0中引入了注解,我们只需要在对应的Servlet类上使用@WebServlet注解进行标记,我们的应用启动之后就可以访问到该Servlet。对于一个@WebServlet而言,有一个属性是必须要的,那就是它的访问路径。@WebServlet中有两个属性可以用来表示Servlet的访问路径,分别是value和urlPatterns。value和urlPatterns都是数组形式,表示我们可以把一个Servlet映射到多个访问路径,但是value和urlPatterns不能同时使用。如果同时使用了value和urlPatterns,我们的Servlet是无法访问到的。下面是一个使用@WebServlet的简单Servlet示例。Java代码![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* Servlet3.0支持使用注解配置Servlet。我们只需在Servlet对应的类上使用@WebServlet进行标注,
* 我们就可以访问到该Servlet了,而不需要再在web.xml文件中进行配置。@WebServlet的urlPatterns
* 和value属性都可以用来表示Servlet的部署路径,它们都是对应的一个数组。
*/
@WebServlet(name="exampleServlet", urlPatterns="/servlet/example")
public class ExampleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("Hello User.");
}
}
初始化参数 使用@WebServlet时也可以配置初始化参数,它是通过@WebServlet的initParams参数来指定的。initParams是一个@WebInitParam数组,每一个@WebInitParam代表一个初始化参数。Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 带初始化参数的Servlet
* WebServlet的属性initParams可以用来指定当前Servlet的初始化参数,它是一个数组,
* 里面每一个@WebInitParam表示一个参数。
*/
@WebServlet(value="/servlet/init-param", initParams={@WebInitParam(name="param1", value="value1")})
public class WebInitParamServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Enumeration<String> paramNames = this.getServletConfig().getInitParameterNames();
String paramName;
while (paramNames.hasMoreElements()) {
paramName = paramNames.nextElement();
resp.getWriter().append(paramName + " = " + this.getServletConfig().getInitParameter(paramName));
}
resp.getWriter().close();
}
}
2.异步调用
在Servlet3.0中,在Servlet内部支持异步处理。它的逻辑是当我们请求一个Servlet时,我们的Servlet可以先返回一部分内容给客户端。然后在Servlet内部异步处理另外一段逻辑,等到异步处理完成之后,再把异步处理的结果返回给客户端。这意味着当我们的Servlet在处理一段比较费时的业务逻辑时,我们可以先返回一部分信息给客户端,然后异步处理费时的业务,而不必让客户端一直等待所有的业务逻辑处理完。等到异步处理完之后,再把对应的处理结果返回给客户端。 异步调用是通过当前HttpServletRequest的startAsync()方法开始的,它返回一个AsyncContext。之后我们可以调用AsyncContext的start()方法来新起一个线程进行异步调用。在新线程内部程序的最后我们最好是调用一下当前AsyncContext的complete()方法,否则异步调用的结果需要等到设置的超时时间过后才会返回到客户端。另外当异步调用超时以后会接着调用异步任务,即新起的线程。Java代码![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 支持异步返回的Servlet
* 对于Servlet的异步返回,首先我们必须指定@WebServlet的asyncSupported属性为true(默认是false),同时在它之前的Filter
* 的asyncSupported属性也必须是true,否则传递过来的request就是不支持异步调用的。
*
*/
@WebServlet(value="/servlet/async", asyncSupported=true)
public class AsyncServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
final PrintWriter writer = resp.getWriter();
writer.println("异步之前输出的内容。");
writer.flush();
//开始异步调用,获取对应的AsyncContext。
final AsyncContext asyncContext = req.startAsync();
//设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
asyncContext.setTimeout(10*1000L);
//新起线程开始异步调用,start方法不是阻塞式的,它会新起一个线程来启动Runnable接口,之后主程序会继续执行
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5*1000L);
writer.println("异步调用之后输出的内容。");
writer.flush();
//异步调用完成,如果异步调用完成后不调用complete()方法的话,异步调用的结果需要等到设置的超时
//时间过了之后才能返回到客户端。
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
});
writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
writer.flush();
}
}
对于一个Servlet如果要支持异步调用的话我们必须指定其asyncSupported属性为true(默认是false)。使用@WebServlet注解标注的Servlet我们可以直接指定其asyncSupported属性的值为true,如:@WebServlet(value=”/servlet/async”, asyncSupported=true)。而对于在web.xml文件中进行配置的Servlet来说,我们需要在配置的时候指定其asyncSupported属性为true。Xml代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>xxx</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>xxx</servlet-name>
<url-pattern>xxx</url-pattern>
</servlet-mapping>
Servlet的异步调用程序的关键是要调用当前HttpServletRequest的startAsync()方法。至于利用返回的AsyncContext来新起一个线程进行异步处理就不是那么的必须了,因为在HttpServletRequest startAsync()之后,我们可以自己新起线程进行异步处理。Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
@WebServlet(value="/servlet/async", asyncSupported=true)
public class AsyncServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
final PrintWriter writer = resp.getWriter();
writer.println("异步之前输出的内容。");
writer.flush();
//开始异步调用,获取对应的AsyncContext。
final AsyncContext asyncContext = req.startAsync();
//设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
asyncContext.setTimeout(10*1000L);
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5*1000L);
writer.println("异步调用之后输出的内容。");
writer.flush();
//异步调用完成
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread t = new Thread(r);
//开启自己的线程进行异步处理
t.start();
writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
writer.flush();
}
}
异步调用监听器 当我们需要对异步调用做一个详细的监听的时候,比如监听它是否超时,我们可以通过给AsyncContext设置对应的监听器AsyncListener来实现这一功能。AsyncListener是一个接口,里面定义了四个方法,分别是针对于异步调用开始、结束、出错和超时的。Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 支持异步返回的Servlet
* 对于Servlet的异步返回,首先我们必须指定@WebServlet的asyncSupported属性为true(默认是false),同时在它之前的Filter
* 的asyncSupported属性也必须是true,否则传递过来的request就是不支持异步调用的。
*
*/
@WebServlet(value="/servlet/async2", asyncSupported=true)
public class AsyncServlet2 extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
final PrintWriter writer = resp.getWriter();
writer.println("异步之前输出的内容。");
writer.flush();
//开始异步调用,获取对应的AsyncContext。
final AsyncContext asyncContext = req.startAsync();
//设置当前异步调用对应的监听器
asyncContext.addListener(new MyAsyncListener());
//设置超时时间,当超时之后程序会尝试重新执行异步任务,即我们新起的线程。
asyncContext.setTimeout(10*1000L);
//新起线程开始异步调用,start方法不是阻塞式的,它会新起一个线程来启动Runnable接口,之后主程序会继续执行
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5*1000L);
writer.println("异步调用之后输出的内容。");
writer.flush();
//异步调用完成
asyncContext.complete();
} catch (Exception e) {
e.printStackTrace();
}
}
});
writer.println("可能在异步调用前输出,也可能在异步调用之后输出,因为异步调用会新起一个线程。");
writer.flush();
}
/**
* 异步调用对应的监听器
* @author Yeelim
* @date 2014-2-8
* @mail yeelim-zhang@todaytech.com.cn
*/
private class MyAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("异步调用完成……");
event.getSuppliedResponse().getWriter().println("异步调用完成……");
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println("异步调用出错……");
event.getSuppliedResponse().getWriter().println("异步调用出错……");
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("异步调用开始……");
event.getSuppliedResponse().getWriter().println("异步调用开始……");
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("异步调用超时……");
event.getSuppliedResponse().getWriter().println("异步调用超时……");
}
}
}
注: 对于正常执行的异步调用而言上述代码中开始是没有监听到的,只有在异步调用超时,重新执行异步任务的时候才有监听到异步调用的开始。不过如果需要监听异步第一次开始的话,我们可以在异步调用开始的时候做相应的监听器监听到异步调用开始时需要做的内容。
3.文件上传
在Servlet3.0中上传文件变得非常简单。我们只需通过request的getPart(String partName)获取到上传的对应文件对应的Part或者通过getParts()方法获取到所有上传文件对应的Part。之后我们就可以通过part的write(String fileName)方法把对应文件写入到磁盘。或者通过part的getInputStream()方法获取文件对应的输入流,然后再对该输入流进行操作。要使用request的getPart()或getParts()方法对上传的文件进行操作的话,有两个要注意的地方。首先,用于上传文件的form表单的enctype必须为multipart/form-data;其次,对于使用注解声明的Servlet,我们必须在其对应类上使用@MultipartConfig进行标注,而对于在web.xml文件进行配置的Servlet我们也需要指定其multipart-config属性,如:Xml代码![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
<servlet>
<servlet-name>xxx</servlet-name>
<servlet-class>xxx.xxx</servlet-class>
<multipart-config></multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>xxx</servlet-name>
<url-pattern>/servlet/xxx</url-pattern>
</servlet-mapping>
不管是基于注解的@MultipartConfig,还是基于web.xml文件配置的multipart-config,我们都可以给它们设置几个属性。l file-size-threshold:数字类型,当文件大小超过指定的大小后将写入到硬盘上。默认是0,表示所有大小的文件上传后都会作为一个临时文件写入到硬盘上。l location:指定上传文件存放的目录。当我们指定了location后,我们在调用Part的write(String fileName)方法把文件写入到硬盘的时候可以,文件名称可以不用带路径,但是如果fileName带了绝对路径,那将以fileName所带路径为准把文件写入磁盘。l max-file-size:数值类型,表示单个文件的最大大小。默认为-1,表示不限制。当有单个文件的大小超过了max-file-size指定的值时将抛出IllegalStateException异常。l max-request-size:数值类型,表示一次上传文件的最大大小。默认为-1,表示不限制。当上传时所有文件的大小超过了max-request-size时也将抛出IllegalStateException异常。 上面的属性是针对于web.xml中配置Servlet而言的,其中的每一个属性都对应了multipart-config元素下的一个子元素。对于基于注解配置的Servlet而言,@MultipartConfig的属性是类型的,我们只需把上述对应属性中间的杠去掉,然后把对应字母大写即可,如maxFileSize。 下面给出Servlet3.0中文件上传的一个示例。Html:Html代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
<form method="post" action="servlet/upload" enctype="multipart/form-data">
<input type="file" name="upload"/>
<input type="submit" value="upload"/>
</form>
对应Servlet:Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201604/bfec35983ba76ff53a6a0262fe55d6ea.png)
@WebServlet("/servlet/upload")
@MultipartConfig
public class FileUploadServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
Part part = req.getPart("upload");
//格式如:form-data; name="upload"; filename="YNote.exe"
String disposition = part.getHeader("content-disposition");
System.out.println(disposition);
String fileName = disposition.substring(disposition.lastIndexOf("=")+2, disposition.length()-1);
String fileType = part.getContentType();
long fileSize = part.getSize();
System.out.println("fileName: " + fileName);
System.out.println("fileType: " + fileType);
System.out.println("fileSize: " + fileSize);
String uploadPath = req.getServletContext().getRealPath("/upload");
System.out.println("uploadPath" + uploadPath);
part.write(uploadPath + File.separator +fileName);
}
}
对于Servlet3.0中的文件上传还有一个需要注意的地方,当我们把Part写入到硬盘以后,我们原先的Part(也就是之前的临时文件)可能已经删了,这个时候如果我们再次去访问Part的内容的话,那它就是空的,系统会抛出异常说找不到对应的文件。
相关文章推荐
- 【JavaWeb-5】Servlet的原理、周期、创建方法、转发、ServletConfig以及重要的ServletContext
- JAVAWEB开发之Servlet3.0新特性的使用以及注解的详细使用和自定义注解的方法、动态代理的使用、利用动态代理实现细粒度的权限控制以及类加载和泛型反射
- 详解Java的内置异常以及创建自定义异常子类的方法
- WEB后台--邮件和短信业务实现(包括Java一键实现、封装和异步)以及原理详解
- Web工程中创建Servlet常见的问题以及解决方法
- WEB后台--邮件和短信业务实现(包括Java一键实现、封装和异步)以及原理详解
- 【Java.Web】Servlet —— Servlet容器的启动及Servlet创建及初始化,容器默认的Servlet
- JAVA中的protected(详解),以及和clone()方法有关的一些问题
- Java Web 开发必须了解的 HTTP 知识点和 Servlet 之间对应关系
- java Web.xml中servlet配置方法
- Oracle创建存储过程以及JAVA调用方法
- Java读写文件创建文件夹多种方法示例详解
- 【java项目实战】Servlet详解以及Servlet编写登陆页面(二)
- HBase单机模式配置以及不能创建表问题的解决方法(Caused by: java.net.ConnectException: Connection refused)
- Maven普通java项目以及web项目快速创建及导入汇总
- java web 的servlet web.xml配置详解
- JNI学习(四)、本地方法创建java对象,以及对字符串的操作
- 学习笔记之JavaWeb基础:Servlet的HelloWorld和原理
- android-WebView详解实例(JavaScript调用Java方法,Java调用JavaScript方法)
- 解析xml的几种方法,他们的原理,比较 以及JAVA源码