链接:http://www.importnew.com/14621.html
https://blog.csdn.net/yanmiao0715/article/details/79949911#commentBox
概述:
Servlet 是一些遵从Java Servlet API的Java类,这些Java类可以响应请求。尽管Servlet可以响应任意类型的请求,但是它们使用最广泛的是响应web方面的请求。 Servlet必须部署在Java servlet容器才能使用。虽然很多开发者都使用Java Server Pages(JSP)和Java Server Faces(JSF)等Servlet框架,但是这些技术都要在幕后通过Servlet容器把页面编译为Java Servlet。
在IDEA中开发简单servlet:
新建项目->Java->Web Application;
在WEB-INF下新建文件夹classes和lib;
新建Servlet;
servlet.jar包放在lib文件夹下;导入jar包,右键lib目录->Add as Library,或project structure->Libraries->Java->E:\project\j2ee\web\WEB-INF\lib\servlet-api.jar;
指定输出目录->project structure->设置project complier output;
修改web.xml映射或用注解的方式@WebServlet(name = “HelloServlet”,urlPatterns = {"/hello"});
配置Tomcat->在IDEA中打开Tomcat配置->指定Tomcat->指定当前项目部署到Tomcat中(->Deployment->加号->Artifact)
关于xml文件:
MyServlet cn.roobtyan.servlet.FirstServlet MyServlet /first当你访问/first的时候,服务器自然就会把请求交给MyServlet进行处理了.
使用注解配置:
@WebServlet(name = "LoginServlet",urlPatterns = {"/login"})
public class LoginServlet extends HttpServlet {
}
Servlet的生命周期:
Servlet运行在Servlet容器中,其生命周期由容器来管理。Servlet的生命周期通过javax.servlet.Servlet接口中的init()、service()和destroy()方法来表示
- 在Servlet生命周期的初始化阶段,web容器通过调用init()方法来初始化Servlet实例,并且可以传递一个实现 javax.servlet.ServletConfig 接口的对象给它。这个配置对象(configuration object)使Servlet能够读取在web应用的web.xml文件里定义的名值(name-value)初始参数。这个方法在Servlet实例的生命周期里只调用一次。
- 初始化后,Servlet实例就可以处理客户端请求了。web容器调用Servlet的service()方法来处理每一个请求。service() 方法定义了能够处理的请求类型并且调用适当方法来处理这些请求。
3)destroy方法,销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象,同样也是调用一次.
处理Servlet请求和响应:
一个HTML网页表单包含了要发送给Servlet的参数。在对象是HttpServlet的情况下,客户端是web浏览器,响应是web页面。的 action属性指定了使用哪个Servlet来处理表单里的参数值。
为了获取请求参数,需要调用 HttpServletRequest 对象的 getParameter() 方法。
String value1 = req.getParameter("param1");
String value1 = req.getParameter("param2");
一旦获取了参数值,它们就会在需要时被处理。对客户端的响应和我们上面部分讨论的一样。我们使用 HttpServletResponse 对象给客户端发送响应。
为了发送内容给客户端,你需要使用从 HttpServletResponse 里获取的 PrintWriter 对象。任何写到这个对象的内容都会被写进outputstream里,并会把内容发送回给客户端。
PrintWriter pw = response.getWriter();
pw.println(html);
service()方法:
get方式:
form默认的提交方式
如果通过一个超链访问某个地址
如果在地址栏直接输入某个地址
ajax指定使用get方式的时候
post方式:
在form上显示设置 method="post"的时候
ajax指定post方式的时候
service()方法:
LoginServlet继承了HttpServlet,同时也继承了一个方法
service(HttpServletRequest , HttpServletResponse )
实际上,在执行doGet()或者doPost()之前,都会先执行service(),由service()方法进行判断,到底该调用doGet()还是doPost(),可以发现,service(), doGet(), doPost() 三种方式的参数列表都是一样的。所以,有时候也会直接重写service()方法,在其中提供相应的服务,就不用区分到底是get还是post了。比如把前面的登录的LoginServlet,改为提供service方法,也可以达到相同的效果 。
中文问题:
获取中文:
1. login.html中加上
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
这句话的目的是告诉浏览器,等下发消息给服务器的时候,使用UTF-8编码
2. login.html
form的method修改为post
3. 在servlet进行解码和编码
byte[] bytes= name.getBytes("ISO-8859-1");
name = new String(bytes,"UTF-8");
先根据ISO-8859-1解码,然后用UTF-8编码,这样就可以得到正确的中文参数了,这样需要对每一个提交的数据都进行编码和解码处理,如果觉得麻烦,也可以使用一句话代替:
request.setCharacterEncoding("UTF-8");
并且把这句话放在request.getParameter()之前
返回中文响应:
在Servlet中,加上
response.setContentType("text/html; charset=UTF-8");
关于转发与重定向:
转发(服务端跳转):
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/welcome.jsp");
requestDispatcher.forward(request,response);
重定向(客户端跳转):
response.sendRedirect("/welcome.jsp");
转发和重定向的区别:
虽然二者最终实现的功能是相同的.但是还是有很大不同的.不同之处如下:
地址栏变化:转发不会改变地址栏中的URL,而重定向则会改变。
跳转范围:转发只能访问到当前web应用中的内容,而重定向则可以访问到任意web应用中的内容。
request对象作用范围:转发后,在转发后的页面中仍然可以使用原来的request对象,而重定向,原来的request对象则失去作用。
所以,如果想要在多个页面使用相同的request对象,那么只能使用转发,而不能使用重定向。
servlet自启动:
Servlet实现自启动 即,伴随着tomcat的启动,自动启动初始化,在初始化方法init()中,就可以进行一些业务代码的工作了。
web.xml:
<load-on-startup>10</load-on-startup>
取值范围是1-99,即表明该Servlet会随着Tomcat的启动而初始化。
上传文件:
首先,在上传页面upload.html中:
1.form 的method必须是post的,get不能上传文件。 还需要加上enctype="multipart/form-data" 表示提交的数据是二进制文件
2. 需要提供type="file" 的字段进行上传
<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<form action="uploadPhoto" method="post" enctype="multipart/form-data">
英雄名称:<input type="text" name="heroName" /> <br>
上传头像 : <input type="file" name="filepath" /> <br>
<input type="submit" value="上传">
</form>
然后,在UploadPhotoServlet 中:
commons-io-1.4.jar和commons-fileupload-1.2.2.jar两个包。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadPhotoServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response) {
String filename = null;
try {
//前部分代码是固定写法,用来做一些准备工作。 直到遍历出Item,一个Item就是对应一个浏览器提交的数据,通过 item.getInputStream可以打开浏览器上传的文件的输入流。
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置上传文件的大小限制为1M
factory.setSizeThreshold(1024 * 1024);
List items = null;
try {
items = upload.parseRequest(request);
} catch (FileUploadException e) {
e.printStackTrace();
}
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (!item.isFormField()) {
// 客户提交的文件名有可能是一样的,所以在服务端保存文件的时候,不能使用客户提交的文件名。这里使用的是一种粗糙的解决文件名重复的办法,即使用时间戳。
filename = System.currentTimeMillis() + ".jpg";
//通过getRealPath获取上传文件夹,如果项目在e:/project/j2ee/web,那么就会自动获取到 e:/project/j2ee/web/uploaded,读取输入流中的数据,保存在服务端的目录下 e:/project/j2ee/web/uploaded,这个目录是通过getRealPath获取到的。 如果项目部署在其他地方,那么会自动做相应的变化。
String photoFolder =request.getServletContext().getRealPath("uploaded");
File f = new File(photoFolder, filename);
f.getParentFile().mkdirs();
// 通过item.getInputStream()获取浏览器上传的文件的输入流
InputStream is = item.getInputStream();
// 复制文件
FileOutputStream fos = new FileOutputStream(f);
byte b[] = new byte[1024 * 1024];
int length = 0;
while (-1 != (length = is.read(b))) {
fos.write(b, 0, length);
}
fos.close();
} else {
System.out.println(item.getFieldName());
String value = item.getString();
value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
System.out.println(value);
}
}
String html = "<img width='200' height='150' src='uploaded/%s' />";
//根据临时生成的文件名,创建一个html img元素,然后通过response返回浏览器
response.setContentType("text/html");
PrintWriter pw= response.getWriter();
pw.format(html, filename);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
监听Servlet容器事件:
有时候,知道应用服务器容器(the application server container)里某些事件发生的时间是很有用的。这个概念适用于很多情况,但它通常用在开启应用时初始化应用或者关闭应用时清理应用。可以在应用里 注册一个监听器(listener)来显示应用什么时候开启或者关闭。因此,通过监听这些事件,Servlet可以在一些事件发生时执行相应的动作。
为了创建一个基于容器事件执行动作的监听器,你必须创建一个实现 ServletContextListener 接口的类。这个类必须实现的方法有 contextInitialized() 和 contextDestroyed()。这两个方法都需要 ServletContextEvent 作为参数,并且在每次初始化或者关闭Servlet容器时都会被自动调用。
为了在容器注册监听器,你可以使用下面其中一个方法:
- 利用 @WebListener 注解。
- 在web.xml应用部署文件里注册监听器。
- 使用 ServletContext 里定义的 addListener() 方法
请注意,ServletContextListener 不是Servlet API里唯一的监听器。这里还有一些其他的监听器,比如
javax.servlet.ServletRequestListener
javax.servlet.ServletRequestAttrbiteListener
javax.servlet.ServletContextListener
javax.servlet.ServletContextAttributeListener
javax.servlet.HttpSessionListener
javax.servlet.HttpSessionAttributeListener
根据你要监听的事件选择他们来实现你的监听器类。比如,每当创建或销毁一个用户session时,HttpSessionListener 就会发出通知。
传递Servlet初始化参数:
现在的大多数应用都需要设置一些在应用/控制器(controller)启动时可以传递的配置参数(configuration parameters)。Servlet同样可以接受初始化参数,并在处理第一个请求前来使用它们来构建配置参数。
<web-app>
<servlet>
<servlet-name>SimpleServlet</servlet-name>
<servlet-class>com.howtodoinjava.servlets.SimpleServlet</servlet-class>
<!-- Servlet init param -->
<init-param>
<param-name>name</param-name>
<param-value>value</param-value>
</init-param>
</servlet>
</web-app>
设置后,你就可以在代码里调用 getServletConfig.getInitializationParameter() 并传递参数名给该方法来使用参数。就像下面展示的代码一样:
String value = getServletConfig().getInitParameter("name");
为特定的URL请求添加Servlet过滤器:
Web过滤器在给定的URL被访问时对请求进行预处理并调用相应的功能是很有用的。相 比于直接调用给定URL请求的Servlet,包含相同URL模式的过滤器(filter)会在Servlet调用前被调用。这在很多情况下是很有用的。 或许最大的用处就是执行日志,验证或者其他不需要与用户交互的后台服务。
过滤器必须要实现 javax.servlet.Filter 接口。这个接口包含了init(),descriptor()和doFilter()这些方法。init()和destroy()方法会被容器调用。 doFilter()方法用来在过滤器类里实现逻辑任务。如果你想把过滤器组成过滤链(chain filter)或者存在多匹配给定URL模式的个过滤器,它们就会根据web.xml里的配置顺序被调用。
为了在web.xml里配置过滤器,需要使用和 XML元素以及相关的子元素标签。
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LogingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如果你要使用注解来为特定的servlet配置过滤器,你可以使用@WebFilter注解。
使用Servlet下载二进制文件:
为了下载一个文件,Servlet必须提供一个和下载文件类型匹配的响应类型。同样,必须在响应头里指出该响应包含附件。就像下面的代码。
String mimeType = context.getMimeType( fileToDownload );
response.setContentType( mimeType != null ? mimeType : "text/plain" );
response.setHeader( "Content-Disposition", "attachment; filename="" + fileToDownload + """ );
通过调用 ServletContext.getResourceAsStream() 方法并传递文件路径给该方法,你可以获取要下载的文件(文件保存在文件系统)的引用。这个方法会返回一个输入流(InputStream)对 象,我们可以用这个对象来读取文件内容。当读取文件时,我们创建一个字节缓存区(byte buffer)从文件里获取数据块。最后的工作就是读取文件内容并且把它们复制到输出流。我们使用while循环来完成文件的读取,这个循环直到读取了文 件的所有内容才会跳出循环。我们使用循环来读进数据块并把它写进输出流。把所有数据写进输出流后,ServletOutputStream 对象的flush方法就会被调用并且清空内容和释放资源。
private void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileToDownload) throws IOException
{
final int BYTES = 1024;
int length = 0;
ServletOutputStream outStream = response.getOutputStream();
ServletContext context = getServletConfig().getServletContext();
String mimeType = context.getMimeType( fileToDownload );
response.setContentType( mimeType != null ? mimeType : "text/plain" );
response.setHeader( "Content-Disposition", "attachment; filename="" + fileToDownload + """ );
InputStream in = context.getResourceAsStream("/" + fileToDownload);
byte[] bbuf = new byte[BYTES];
while ((in != null) && ((length = in.read(bbuf)) != -1)) {
outStream.write(bbuf, 0, length);
}
outStream.flush();
outStream.close();
}