servlet

链接: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()方法来表示

  1. 在Servlet生命周期的初始化阶段,web容器通过调用init()方法来初始化Servlet实例,并且可以传递一个实现 javax.servlet.ServletConfig 接口的对象给它。这个配置对象(configuration object)使Servlet能够读取在web应用的web.xml文件里定义的名值(name-value)初始参数。这个方法在Servlet实例的生命周期里只调用一次。
  2. 初始化后,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容器时都会被自动调用。
为了在容器注册监听器,你可以使用下面其中一个方法:

  1. 利用 @WebListener 注解。
  2. 在web.xml应用部署文件里注册监听器。
  3. 使用 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();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值