最新资讯:JavaEE---Servlet入门教程 JavaEE操作步骤
今天来继续学习JavaWeb的相关知识,之前都是都介绍一些基本知识,从今天开始我们来说一下如何在服务器编写程序,这里就需要来介绍一下Servlet的相关知识了。Servlet就是一个能够运行在服务器端的java代码,我们从他的api开始来解读吧!
Servlet是JavaEE的13门技术中的一门,所以我们需要从JavaEE的api中寻找,下面就是Servlet的api:
其实Servlet是一个接口,他有两个实现类:GenericServlet,HttpServlet,我们可以看到Servlet接口中的五个方法:
(资料图片仅供参考)
init(ServletConfig config):是个初始化方法,这个方法是在Servlet被初始化的时候被调用,而且会有一个ServletConfig对象传递进来,至于ServletConfig对象我们后面会继续说的
getServletInfo():方法是获取这个Servlet的相关信息的。此方法用到的地方很少
getServletConfig():方法是获取一个ServletConfig对象,和init方法中传递进来的ServletConfig是同一对象
service(ServletRequest req,ServletResponse res):方法是用户处理客户机的请求的,这个方法的两个参数对应的是请求参数:req;和响应参数res。
destroy():是在servlet被销毁的时候调用。
从上面的方法中我们可以看到有三个方法是和servlet的生命周期相关的方法:init(...),service(..),destroy();
下面就来说一下servlet的生命周期的吧:
当服务器启动的时候,服务器会加载所有的web应用,当用户在浏览器中第一次请求servlet的时候,init方法就会被调用,这时候servlet就会被创建,因为servlet是用的单例模式。所以只要servlet所在的应用没有关闭或者服务器没有关闭,这个servlet始终都是在服务器的内存中的,所以当你在一次请求这个servlet的时候init方法是不会再调用的。
当用户每次请求servlet的时候,这个servlet中的service的方法都会被调用,因为他是用来处理客户机的请求的。
当该web应用被关闭或者服务器关闭了,这个servlet才会被销毁,此时destroy方法会被调用。同时这个单例的servlet也会从内存中消失。
上面介绍了servlet的接口和servlet的相关信息,下面就来看一下他的子类吧:
GenericServlet:
他是实现Servlet接口中的所有方法,同时自己也是添加了几个方法:
getInitParameter(String name):这个方法是和ServletConfig对象相关的,通过name来从ServletConfig对象中获取值value
getInitParameterNames():这个方法也是和ServletConfig对象相关的,是获取ServletConfig对象中所有的name的枚举集合
getServletContext():这个方法很重要的,是获取ServletContext对象,这个ServletContext对象我们会在后面说到
下面就来手动的书写一个Servlet,这样我们就能够深入的了解到Servlet的运行原理:
第一步:在tomcat中的webapp目录中新建一个web应用:FirstServlet,然后在应用中新建一个WEB-INF文件夹,在WEB-INF文件夹下面新建一个classes文件夹,之前说过,服务器端的程序代码文件就是放在这个文件夹下面的,然后我们就在classes文件夹下面新建一个FirstServlet.java文件:代码如下:
[java]view plain copy package com.weijia.firstservlet; import java.io.*; import javax.servlet.*; public class FirstServlet extends GenericServlet { @Override public void service(ServletRequest req,ServletResponse res) throws ServletException,java.io.IOException { res.getOutputStream().write("hello servlet".getBytes()); } } 然后我们进行编译:首先进入到当前目录:输入命令:javac -d . FirstServlet.java注意:-d参数是编译包名;.点号是指编译后的包名文件存在当前目录
编译发生错误,提示找不到javax.servlet.*这样的包,其实很简单,因为我们使用javac命令编译程序的话只导入了JavaSE的jar包,所以我们需要导入JavaEE的jar包到路径:set classpath=%classpath%;C:\Program Files\Apache Software Foundation\Tomcat 6.0\lib\servlet-api.jar
因为tomcat目录中肯定用到了javaee中的jar包,所以将这个jar包添加到classpath中即可。
这时候在执行javac命令编译没有问题了,在当前目录中产生文件了,就是包名对应的文件目录
程序编译好了,下面还需要进行配置这个Servlet的对外访问路径,这个我们之前也说过了,关于web应用的所有配置都是在web.xml文件中配置的,所以我们需要在应用FirstServlet应用文件夹中新建一个web.xml文件,这里面来配置Servlet,但是我们该如何书写呢?
这时候我们再去tomcat目录中找到web.xml文件,从这个文件中抄过来,配置如下:
[html]view plain copy在浏览器中输入:http://localhost:8080/FirstServlet/FirstServlet
效果如下:
这样我们就手动的编写一个Servlet,而且可以看到我们是继承GenericServlet的。并且将逻辑都写在了service方法中,其实GenericServlet是一个抽象类,service也是一个抽象的方法需要子类实现的。
那下面就来看看HttpServlet,其实HttpServlet是实现了GenericServlet类的。
上面就是HttpServlet的api了,实现了GenericServlet中的抽象方法,一起我们注意的是:他有好多doXXX这样的方法,其实这个和我们之前介绍Http协议的时候相联系的,HttpServlet就是针对于Http协议的Servlet,我们在之前介绍Http协议的时候说过他的请求方式其实是有七种方式的,只是get/post方式现在最常用,其他的方式被弃用了,所以我们在这里看一看到以do开头的方法其实是专门用来处理不同方式的请求的,但是我们之前说过所有的请求都会调用service方法,所以这时候我们可以看一下service中的源代码:
[java]view plain copy protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn"t support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } 其实看到源代码我们发现内部实现是很简单的,首先通过HttpRequest对象中的getMethod()方法获取客户机的请求方式,然后进行判断,执行对应的方法,所以我们在编写Http协议的Servlet的时候,只需要继承HttpServlet,实现其doGet/doPost方法即可,下面我就是用MyEclipse来开发一个HttpServlet:
首先在MyEclipse中新建一个web应用,在应用中新建包名(web应用中的程序是必须要有包名的),在包下面新建一个Servlet(这里不要在新建一个java类了,不然我们还需要手动的继承HttpServlet,配置这个类的对外访问路径,步骤是很麻烦的),如果是新建一个Servlet的话,MyEclipse会自动帮我们将这个Servlet配置好对外访问路径,而且会自动的继承HttpServlet类。这样我们就可以在doGet和doPost方法中处理客户机的请求了。
上面的内容就介绍了Servlet的相关知识,下面就来开始介绍和Servlet相关的对象:
首先来看一下ServletConfig:
之前说过:Servlet接口中有一个getServletConfig()方法,而且在init(ServletConfig config)方法中也会回传一个ServletConfig对象,那么这个对象到底有什么用呢?见名知意,是Servlet的配置信息相关的,没错,他就是将ServletConfig的配置信息封装成对象,那么Servlet的配置信息是在哪里配置,配置后之后怎么读取,什么样的信息才需要进行配置呢?
其实有很多信息我们都是需要进行配置的,比如:这个Servlet所需要的连接数据库的信息,所使用的字符集码表,下面就来个例子吧:
首先看一下怎么配置,在webx.xml文件中找到对应的Servlet的
下面在来看一下ServletContext对象,关于这个对象我们要详细的了解他,我们在之前介绍ServletConfig对象的时候,发现ServletConfig对象有一个方法:getServletContext(),通过这个方法可以获取一个ServletContext对象。当然也可以通过this.getServletContext()方法得到这个对象。ServletContext是很重要的一个对象,他同时也是四大域对象之一的context域对象,从他的名字我们就知道context是上下文的意思,(在Android中也有这样的一个概念,getApplicationContext()可以获取当前的Android应用的上下文对象)。那么这里的context就代表这个整个web应用,所以他的生命周期是:当服务器启动的时候会为每一个web应用创建一个ServletContext对象,当web应用销毁了或者关闭服务器这个对象也就随之销毁。这里我们也可以看到他的生命周期是最长的,贯彻整个web应用。下面就来看一下他的相关api(只说一些重要的,常用的方法)
getAttribute(String name)/setAttribute(String name,String value)/removeAttribute(String):是将值保存到context域中的,这些值将可以被web应用中所有的servlet和jsp访问
getInitParameter(String name)/getInitParameterNames():可以获取web应用的初始化信息
getMajorVersion()/getMinorVersion():获取Servlet的最大和最小版本
getMimeType(String fileName):通过文件名判断这个文件的mime类型
getRequestDispatcher(String path):通过path路径获取一个转发对象RequestDispatcher,可以实现转发机制
getResourceAsStream(String path):通过给定的path获取一个资源Inpustream
getRealPath(String path):通过给定的path返回这个文件所在磁盘的真实路径
下面就来通过实例来看一下上面方法的使用规则
首先来看一下getAttribute(String name)和setAttribute(String name,Object value),以及getRequestDispatcher(String path):
场景:我们在Servlet1中存入一个值,然后转发到Servlet2,取出这个值打印出来:
Servlet1:
[java]view plain copy package com.weijia.httpservlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Servlet1 extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { ServletContext context = this.getServletContext(); context.setAttribute("data", "Hello Servlet");//存储值 RequestDispatcher rd = context.getRequestDispatcher("/Servlet2"); rd.forward(request, response);//进行转发 } public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { doGet(request,response); } } Servlet2: [java]view plain copy package com.weijia.httpservlet; import java.io.IOException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Servlet2 extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { ServletContext context = this.getServletContext(); //获取属性值 String data = (String) context.getAttribute("data"); //打印 response.getWriter().write(data); } public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { doGet(request,response); } } 在浏览器中输入地址:http://localhost:8080/ServletDemo/Servlet1从Servlet1中存入的值通过转发到Servlet2中取出来进行读取显示。
下面在来看一下getInitParameter(String name)和getInitParameterNames(),我们读取全局配置参数,其实这个配置是对每个Servlet都是有效的,所以他是全局的效果,我们之前使用ServletConfig进行对某个Servlet进行初始化参数配置,数据库连接的信息这个东东是最每个Servlet都是有效的而且都是一样的配置,所以我们应该将这些信息配置到全局中ServletContext,上面配置到ServletConfig中只是为了演示ServletConfig的作用。这种全局性的信息肯定是要配置到ServletContext中的。
[java]view plain copy package com.weijia.httpservlet; import java.io.IOException; import java.util.Enumeration; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Servlet1 extends HttpServlet { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { ServletContext context = this.getServletContext(); response.setHeader("expires", "-1"); Enumeration结果:
下面在来看一下getResouceAsStream(String path)和getReal(String path)方法
这两个方法主要是用来读取web应用中的资源文件的,这个用处很大的,首先我们来看一下web应用的项目结构:
我们分别src目录中新建一个db.properties属性文件和在com.weijia.serlvetcontext包中新建一个db.properties属性文件:
先来看一下怎么读取包中的属性文件
[java]view plain copy ServletContext context = this.getServletContext(); InputStream is = context.getResourceAsStream("/WEB-INF/classes/com/weijia/servletcontext/db.properties"); 使用getResourceAsStream方法获取一个InputStream流,我们需要填写属性文件的路径path:重点在于这个path:这个path的书写是有规则的:
/WEB-INF/classes/com/weijia/servletcontext/db.properties
首先第一个"/"代表当前的web应用(这个和JavaSE中读取文件不同,JavaSE中读取文件的话是一定不能以斜杠开头的,不然就报错),然后是WEB-INF/classes,这个路径我们之前说过,一个web应用发布之后是没有src这样的目录的,我们在第一个手写Servlet的例子中我们也看到了,我们并没有新建一个src文件夹,而是新建一个classes文件夹,在这个文件下面新建一个Servlet的,这点一定要注意;然后就是包名了路径了,因为包名会被映射到文件目录。这样我们就可以读取到了属性文件。
那么读取src目录中的属性文件就更简单了:
/WEB-INF/classes/db.properties这里千万不要写成:
/src/db.properties
一定要记得web应用发布之后是没有src目录的,这里的src目录就相当于是classes目录
上面的这个方法是返回一个InputStream流的,但是如果我们现在想通过FileInputStream文件流来读取文件我们该怎么做呢?
那么我就必须要获取到文件的路径,那么我们能这样写吗:
FileInputStream fis = new FileInputStream("classes/db.properties")
这样写行不行呢?我们运行一下就知道了,我发现运行时报异常的,下面我们就来分析一下这个问题:
首先我们都知道上面的那样写法是相对路径,那么这个相对路径是相对于谁呢?
下面的代码是JavaSE:
[java]view plain copy System.out.println("UserDir:"+System.getProperty("user.dir")); File file = new File("src/com/weijia/demo/db.properties"); System.out.println("path:"+file.getPath()); System.out.println("absolutePath:"+file.getAbsolutePath()); 运行结果:我们可以看到JVM是会使用user.dir这样的系统属性来拼接相对路径得到绝对路径的。这个user.dir存储的就是当前的工作目录
所以我们在Servlet中打印一下:
System.getProperty("user.dir"):可以得到当前的工作目录:
打印结果如下:
C:\Program Files\Apache Software Foundation\Tomcat 6.0\bin
可以看到是tomcat的bin目录,那么我们使用相对路径:classes/db.properties这样就相当于绝对路径:
C:\Program Files\Apache Software Foundation\Tomcat 6.0\bin\classes\db.properties
所以我们可以到tomcat目录中的bin文件夹下面新建一个classes文件夹,里面新建一个db.properties文件,这时候在运行程序,就不会在报异常了,这样貌似我们想使用FileInputStream来读取文件的话,很是麻烦呀!
这时候我们就可以使用了getRealPath(String path)方法来获取资源的本地磁盘中的绝对路径:
打印属性地址:
[java]view plain copy System.out.println("AbsolutePath:"+context.getRealPath("/WEB-INF/classes/db.properties")); 打印结果:AbsolutePath:C:\Program Files\Apache Software Foundation\Tomcat 6.0\webapps\ServletDemo\WEB-INF\classes\db.properties
那么这时候我们就可以通过FileInputStream来读取文件了。
现在还有一个问题就是以后我们在编写Web应用的时候是要遵从多层设计思想的,而且层与层之间不能有侵入行为,层与层之间使用接口进行访问的。比如Service层和Dao层是不能有感染的,就是耦合度要很低的,Servlet就是Service层,那么现在如果想在Dao层中读取文件资源我们该怎么办呢?因为Dao层中没有Servlet,所以没有ServletContext了,那么我们该怎么获取路径呢?有人说可以通过方法的参数形式将ServletContext对象从service层传递到Dao层中,这个方法是可以的,但是这就违背了我们开始说的层与层之间的耦合和互不感染的原则了。那这时候我们该怎么办呀?这时候我们就需要另外一种读取资源的一种方式了,而且这种方式也是可以用于JavaSE中读取文件的。也是一种非常经典的读取资源的方式,这就使用类加载器来读取资源:
[java]view plain copy Properties dbConfig = new Properties(); dbConfig.load(UserDao.class.getClassLoader().getResourceAsStream("com/weijia/servletcontext/db.properties")); System.out.println("url:"+dbconfig.getProperty("url")); System.out.println("username:"+dbconfig.getProperty("username")); System.out.println("password:"+dbconfig.getProperty("password")); 这样我们就可以不使用ServletContext对象也可以读取到com.weijia.servletcontext包底下的db.properties文件资源了,同样的他也是有一个getRealPath(String path)方法的可以得到本地磁盘的绝对路径,这样我们也可以使用FileInputStream来读取资源了。其实这种读取资源的方式是:类加载器会把该资源文件和class文件等同一样加载到内存中的,所以问题就出现了:
1.首先这个资源文件肯定不能太大,因为他是和class文件一起加载到内存中,太大的话,内存就爆了
2.只要当类加载的时候这个文件才会被加载到内存中,现在假如我们修改了这个db.properties资源文件,保存,但是我们在读取的时候还是之前的资源文件中的内容,原因很简单,因为我们修改的是db.properties资源文件,而没有修改类文件,所以类加载器并不会再次加载类,那么就不会在加载这个修改过的资源文件了,那么这次修改是无效的,所以说这点我们在使用类加载器读取资源的时候一定要注意了。
下面来看一下Servlet的线程安全问题:
当多个客户端并发访问同一个servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用servlet的service方法,因此service方法如果访问了同一个资源的话,就有可能引发线程安全的问题,如果某个servlet实现了SingleThreadModel(标记接口)接口,那么servlet引擎将以单线程模式来调用其service方法。SingleThreadModel接口中没有定义任何方法,只要在servlet类的定义中增加实现SingleThreadModel接口的声明即可.对于实现了SingleThreadModel接口的servlet,servlet引擎仍然支持对该servlet的多线程并发访问,其采用的方式是产生多个servlet实例对象,并发的每个线程分别条用一个独立的servlet实例对象。实现SingleThreadModel接口并不能真正解决servlet的线程安全问题,因为servlet的引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个servlet实例对象被多个线程同时调用的问题,事实上,在servlet api2.4中,已经将SingleThreadModel标记为Deprecated(过时的).标准的解决方案是同步方式sychronized
如果线程向静态list集合中加入了数据(aaa),数据用完后,一般要移除静态集合中的数据(aaa),否则集合中的数据越来越多,就会导致内存溢出。对象销毁了,静态资源的字节码仍然驻留在内存中.
下面来看一下Servlet的对外访问路径的配置:
我们一个Servlet编写完成之后是需要对其进行配置对外访问路径的,如果我们是新建一个Servlet的话,IDE会自动的帮我们配置好这个对外访问路径,但是有时候我们需要自己进行配置已达到我们需要的效果,这时候我们知道配置应用的web.xml文件中的内容即可,首先要知道一个Servlet是可以配置多个对外访问路径的。下面就直接来一下配置路径的案例吧:
以下是Servlet对外访问路径的配置规则案例:
servlet1 映射到 /abc/*
servlet2 映射到 /*
servlet3 映射到 /abc
servlet4 映射到 *.do
问题:
1.当请求URL为"/abc/a.html","/abc/*"和"/*"都匹配,但是servlet引擎会调用servlet1
2.当请求URL为"/abc"时,"/abc/*"和"/abc"都匹配,但是servlet引擎会调用servlet3
3.当请求URL为"/abc/a.do"时,"/abc/*"和"*.do"都匹配,但是servlet引擎会调用servlet1
4.当请求URL为"/a.do"时,"/*"和"*.do"都匹配,但是servlet引擎会调用servlet2
5.当请求URL为"/xxx/yyy/a.do"时,"/*"和"*.do"都匹配,但是servlet引擎会调用servlet2
总结:谁长的最像,谁先匹配,同时*的优先级最低.
当然我们还可以配置一个默认的访问对外访问路径就是直接一个斜杠:/这个默认的对外访问路径的作用就是当一个Servlet找不到其对应的映射路径的时候回去找打这个默认的对外访问路径:
上面的图片就是tomcat中的web.xml文件中配置的默认对外访问路径,当我们访问的路径找不到都会去请求DefaultServlet。
总结:上面我们就介绍了Servlet的相关知识,以及ServletConfig和ServletContext对象,下面总结一下在开发web的时候的问题的解决:
1.首先来看怎么修改Servlet的模板,因为我们在开发一个Servlet的时候,发现在doGet/doPost方法中发现很多没有用的代码,每次都需要删除很是麻烦的,我们进入到MyEclispe的安装目录中全局搜索一个servlet.java文件,打开之后我们将doGet/doPost方法中无效的内容删除了即可(最后在修改之前备份一下这个servlet.java)
2.我们有时候在将一个web应用导入到MyEclipse中的时候项目的名称可能会修改,但是他的Context Path并没有修改,所以在通过浏览器访问的时候还是之前项目的名称路径,这时候我们要修改的话就点击项目的properties如下界面修改:
我们只需要将WebRoot从新映射到一个Context-path就可以了。
3.有时候我们在发布应用的时候会发现以下的问题:
这个问题我们知道是版本的问题,就是使用高版本的JDK去编译web应用,然后在用低版本的JVM(tomcat)去运行,这个问题发生在我们之前手动编写一个web应用FirstServlet,那时候我们是用javac命令进行编译的,这个javac命令是使用了JDK7.0版本的(我自己安装了JDK7.0),当我们打开MyEclipse的时候,我在MyEclipse中配置的是Tomcat6.0的,tomcat6.0是运行在JVM6.0的,所以会报错了,那么我们可以修改MyEclipse中的tomcat运行所依赖的JVM:
点击add可以添加我们JDK7.0版本的JVM,保存在运行就没有问题了。
Servlet监听器
有时候,知道应用服务器容器(the application server container)里某些事件发生的时间是很有用的。这个概念适用于很多情况,但它通常用在开启应用时初始化应用或者关闭应用时清理应用。可以在应用里 注册一个监听器(listener)来显示应用什么时候开启或者关闭。因此,通过监听这些事件,Servlet可以在一些事件发生时执行相应的动作。
为了创建一个基于容器事件执行动作的监听器,你必须创建一个实现 ServletContextListener 接口的类。这个类必须实现的方法有 contextInitialized() 和 contextDestroyed()。这两个方法都需要 ServletContextEvent 作为参数,并且在每次初始化或者关闭Servlet容器时都会被自动调用。
为了在容器注册监听器,你可以使用下面其中一个方法:
1) 利用 @WebListener 注解。 2) 在web.xml应用部署文件里注册监听器。 3) 使用 ServletContext 里定义的 addListener() 方法
请注意,ServletContextListener 不是Servlet API里唯一的监听器。这里还有一些其他的监听器,比如
1 2 3 4 5 6 | javax.servlet.ServletRequestListenerjavax.servlet.ServletRequestAttrbiteListenerjavax.servlet.ServletContextListenerjavax.servlet.ServletContextAttributeListenerjavax.servlet.HttpSessionListenerjavax.servlet.HttpSessionAttributeListener |
根据你要监听的事件选择他们来实现你的监听器类。比如,每当创建或销毁一个用户session时,HttpSessionListener 就会发出通知。
Servlet过滤器Web过滤器在给定的URL被访问时对请求进行预处理并调用相应的功能是很有用的。相 比于直接调用给定URL请求的Servlet,包含相同URL模式的过滤器(filter)会在Servlet调用前被调用。这在很多情况下是很有用的。 或许最大的用处就是执行日志,验证或者其他不需要与用户交互的后台服务。
过滤器必须要实现 javax.servlet.Filter 接口。这个接口包含了init(),descriptor()和doFilter()这些方法。init()和destroy()方法会被容器调用。 doFilter()方法用来在过滤器类里实现逻辑任务。如果你想把过滤器组成过滤链(chain filter)或者存在多匹配给定URL模式的个过滤器,它们就会根据web.xml里的配置顺序被调用。
为了在web.xml里配置过滤器,需要使用
1 2 3 4 5 6 7 8 |
Servlet下载文件如果你要使用注解来为特定的servlet配置过滤器,你可以使用@WebFilter注解。
几乎所有的web应用都必须有下载文件的功能。为了下载一个文件,Servlet必须提供一个和下载文件类型匹配的响应类型。同样,必须在响应头里指出该响应包含附件。就像下面的代码。
1 2 3 | 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方法就会被调用并且清空内容和释放资源。
看这段简单的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | privatevoiddownloadFile(HttpServletRequest request, HttpServletResponse response, String fileToDownload) throwsIOException{finalintBYTES = 1024;intlength = 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 = newbyte[BYTES];while((in != null) && ((length = in.read(bbuf)) != -1)) {outStream.write(bbuf, 0, length);}outStream.flush();outStream.close();} |
使用RequestDispatcher.forward()转发请求到另一个Servlet
有时候,你的应用需要把一个Servlet要处理的请求转让给另外的Servlet来处理并完成任务。而且,转让请求时不能重定向客户端的URL。即浏览器地址栏上的URL不会改变。
在 ServletContext 里已经内置了实现上面需求的方法。所以,当你获取了 ServletContext 的引用,你就可以简单地调用getRequestDispatcher() 方法去获取用来转发请求的 RequestDispatcher 对象。当调用 getRequestDispatcher() 方法时,需要传递包含servlet名的字符串,这个Servlet就是你用来处理转让请求的Servlet。获取 RequestDispatcher 对象后,通过传递 HttpServletRequest 和HttpServletResponse 对象给它来调用转发方法。转发方法负责对请求进行转发。
1 2 | RequestDispatcher rd = servletContext.getRequestDispatcher("/NextServlet");rd.forward(request, response); |
标签:
相关推荐:
精彩放送:
- []稀奇古怪乐小米是什么?关于稀奇古怪乐小米的介绍
- []北京民俗文化有哪些?关于北京民俗文化的介绍
- []天天报道:400-500分上的医科大学名单黑龙江 2023年参考医科大学录取名单
- []娱乐爆料:王丽坤、李冰冰、周杰伦、刘昊然、朱一龙
- []木格措海拔有多高?关于木格措海拔的介绍
- []最新资讯:JavaEE---Servlet入门教程 JavaEE操作步骤
- []环球热点!香茅是什么味道?香茅是什么植物?
- []焦点!版载千秋第3个隐藏任务怎么过?版载千秋答题器
- []世界时讯:苹果手机越狱后怎样恢复?苹果手机恢复回越狱前的方法
- []今日热门!使命召唤10下载地址是什么?使命召唤11中文版下载迅雷
- []最新:TSLAM9是什么?中心差分卡尔曼滤波
- []人均gdp是什么意思?人均gdp的含义
- []今日聚焦!大连市房产局官网怎么查询?大连市房产局官网
- []环球快看:搬砖是什么意思?dnf搬砖的含义
- []【环球速看料】ape音乐免费下载 ape音乐怎么下载?
- []外汇交易提醒:日元在震荡交投中回落,美元兑日元小幅反弹
- []当前播报:纹的组词有哪些?纹的组词有什么?
- []每日报道:DTA决赛放送:Z世代、元宇宙……酒旅业创新趋势都在这里 |
- []原油交易提醒:库存大降助力油价飙升超3%,警惕地缘局势不确定性风险上升
- []侨银股份预中标广州荔湾和莱州两个服务项目 总成交金额约1.79亿
- []天天播报:嘉兴嘉善2宗涉宅地6.16亿成交 三达房地产封顶价获其一
- []湖州德清一宗商住地将于明年1月11日出让 起始价3.48亿元
- []每日报道:2022年市场规模将超6000亿元 我国智能家居产业发展打开新空间
- []12月22日重点数据和大事件前瞻
- []每日热文:已有25家房企发布配股或定增 雅居乐第2次公告配售后股价跌超17%
- []湖南电力“满格” 水利显威
- []天天播报:年产首次突破50万吨 我国海上稠油热采实现“三级跳”
- []简讯:我国高端电力装备研发获重大成果
- []梅耶博格:开启29.6%效率的串联钙钛矿太阳能电池研发
- []焦点播报:EIA原油库存降幅超预期,美油短线拉升0.5美元
- []世界视点!5亿元!爱旭股份设立全资子公司投建6.5GW太阳能电池项目
- []每日速递:奥海科技:12月20日公司高管吴日诚的亲属减持公司股份合计300股
- []社保平均缴费指数怎么计算 怎么计算社保平均缴费指数
- []汉港控股3.4亿元向绿城服务租赁杭州余杭物业商业空间 租期十年
- []天天热头条丨一年扣一次大额什么意思 什么是一年扣一次大额
- []车险需要买哪些 购买一般车险要买什么险
- []世界观热点:皇庭国际第一大股东8698.59万股被司法拍卖 占总股本7.41%
- []世界即时看!金融街拟出售房山金悦嘉苑共有产权房项目,交易价22.6亿
- []天天亮点!央行:引导金融机构支持地产行业重组并购 防范化解头部房企风险
- []今日视点:央行:加大稳健货币政策实施力度 加大对民营小微企业支持力度
- []今日热议:嘉凯城聘任符谙、余薇为公司证券事务代表
- []速看:房地产开发板块跌0.75% 粤泰股份涨10.18%居首
- []当前讯息:康龙化成:12月20日公司高管楼小强减持公司股份合计4.47万股
- []每日观察!二手房周报 | 15城二手成交环比下降10%,北京规模已较高点腰斩(12.12-12.18)
- []世界滚动:宁波色母:12月20日公司高管祖万年减持公司股份合计5.25万股
- []环球观察:同有科技:12月20日公司高管杨建利减持公司股份合计7.01万股
- []环球快看点丨北京宸宇将向北辰实业与北京金隅归集资金共计2.5亿元
- []今亮点!郭施亮:为何房地产概念股走势频繁变脸?
- []环球看热讯:沈晓玲:房企年底为冲业绩,推“无理由退房”等靠谱吗?
- []明珠货运(YGMZ.US)以1500万美元收购飞鹏物流100%股权
- []每日动态!龙软科技:12月20日公司高管任永智、侯立、雷小平减持公司股份合计1.6万股
- []中科软:12月21日公司高管孙熙杰减持公司股份合计71.39万股
- []雄帝科技:公司暂时没有涉及该领域,对于新技术、新业态的发展公司会持续保持关注
- []昭衍新药:12月20日公司高管顾晓磊减持公司股份合计57.3万股
- []环球热文:宝新金融:立信德豪辞任 委任国富浩华为新核数师
- []【天天报资讯】第一服务:上海鼎晖耀家就保证回报权订立补充协议
- []新资讯:挂牌价24万/平重现,一线城市首现二手房参考价“隐退”
- []天天关注:中航机电:截至2022年12月20日,公司股东总户数为146,079户
- []当前视讯!江苏南通8宗涉宅地块46亿元成交
- []中公高科:12月20日公司高管李强减持公司股份合计3.86万股
- []昊华能源索赔案仍在征集中 涉诉金额合计已逾亿元
- []世界热点评!删除文件提示正在被另一程序使用怎么办?解决方法
- []速递!飞利浦吸尘器怎么样?维修中常见的问题
- []世界今日讯!天健集团成功发行11亿元超短期融资券 利率3.05%
- []环球关注:文本显示器的价格是多少?文本显示器的优势
- []全球快看点丨快速申请QQ靓号 教你用腾讯QQ极品靓号申请
- []环球微头条丨松下变频器怎么使用?松下变频器说明书详解
- []计算机网络虚拟局域网步骤 如何构建虚拟局域网?
- []全球看点:电脑蓝屏是什么原因?电脑蓝屏的解决方法
- []右脑记忆法的个人理解 王峰、袁文魁等记忆大师的通用方法
- []小学生电脑学习机有哪些?读书郎学生电脑主要功能
- []天天快看:什么是umd漫画制? umd漫画制作工具详情介绍
- []韩国泛泰手机怎么样?韩国泛泰手机参数配置如何?
- []联想轻薄笔记本怎么样?联想ThinkPad E325多少钱?
- []环球快讯:德国坦克声卡怎么样?德国坦克声卡质量好不好?
- []全球看热讯:CAD怎么建立三维模型? CAD的建模方法
- []什么是IGBT?IGBT是大功率元器件 属于绝缘型晶体管
- []环球资讯:解决win10邮箱无法登陆 163企业邮箱常见问题
- []股权投资基金的内部管理
- []环球热门:微信公众号的消息免打扰怎么打开?微信公众号的消息免打扰打开方法
- []信息:python十大培训有哪些?python十大培训详情介绍
- []环球观焦点:饮水机什么牌子质量好?饮水机品牌推荐
- []快资讯丨网络基础知识有哪些?网络基础知识大全
- []win7系统如何关闭系统默认共享文件夹?关闭系统默认共享文件夹方法
- []快资讯丨紫光电子平板电脑怎么样?紫光电子平板电脑如何刷机?
- []焦点资讯:预告片下载网站 如何在预告片电影中添加预告片?
- []环球资讯:生活真的不过如此吗? 听“体育评书”
- []世界观热点:iframe标签已经不见执行 如何解决?
- []小飞人熨斗怎样?小飞人熨斗特点介绍
- []全球观热点:OpenStreetMap Google 百度 Bing arcgis瓦片地图服务以及瓦片计算
- []【新视野】d3dx9_43.dll是什么丢失了怎么办?解决办法
- []【世界速看料】二手电视机有哪些分类?二手电视机分类介绍
- []如何清除AcadDoc.lsp病毒?AcadDoc.lsp病毒清理步骤
- []全球快资讯丨TP-LINK 忘记密码怎么办?恢复出厂设置
- []全球速讯:linux关闭tomcat日志打印 linux下打开与关闭tomcat
- []世界实时:C++扑克牌类的设计 C++扑克牌类怎么设计?
- []全球报道:基金收益怎么计算?基金收益计算器
- []一加手机怎么开启手电筒?一加手机开启手电筒操作步骤
- []产品报价单模板 制作报价时格式和细节
- []【天天新要闻】工程师必知:IP协议和IP地址是什么关系?
- 【全球速看料】2021年江苏高考成绩查询网址及查分方式
- 世界看点:立方体的体积怎么计算?计算方法
- 弘扬雷锋精神走进新时代 携手走向中华民族的伟大复兴
- 证监会:允许房地产和建筑等密切相关行业上市公司实施涉房重组
- 热点聚焦:德信中国2.68亿股配售事项及认购事项已达成 所得款项净额2.31亿港元
- 新消息丨德信地产集团目前已交付25个批次 总计交付近2万套
- 看点:德展健康:公司及控股子公司主营产品为心脑血管领域,目前公司在销产品中不包括上述产品
- 【环球聚看点】建发股份:厦门钟宅畲族社区旧村改造部分土地已出让 总价21亿
- ST华英:根据中国登记结算公司主动下发的数据,截止2022年12月20日公司股东户数为32,811户
- 今日热讯:丰原药业:产品价格受市场供求变化影响。公司子公司利康制药扑热息痛当前价格虽有上升,但没有较大变化
- 上实城开:收购跃成全部发行股本先决条件均已达成
- 焦点观察:盛和资源:公司与VHM公司签署了谅解备忘录,但尚未达到应予披露的标准
- 每日讯息!中国交通建设200亿元公司债券已获受理
- 环球热点评!滔搏:第三季度零售及批发业务总销售金额同比录得10%-20%下跌
- 华峰铝业:华青铝业为公司控股股东华峰集团参与投资的企业
- 热议:南极电商:公司授权有口罩,冰贴,消毒液等品类
- 报道:顾家家居股东持有的431万股股份解除质押 另有355万股股份质押
- 世界快资讯丨美亚光电:谢谢提问。根据中登结算公司下发的相关文件,截至12月20日收盘,公司股东数为22,823户
- 世界微动态丨“21长沙高新MTN001A”利率到期调整 由3.76%上调至4.90%
- 当前聚焦:青岛四批集中供地:40宗地全部出让收金约142亿元
- 哪些纸尿裤品牌值得购买呢,看看国际妈咪怎么说?
- 天津公积金新政:二套房贷款首付比例降至40%
- 【全球快播报】福建东百集团发行1亿元超短期融资券 利率5.40%
- 粤泰股份:关于上海宗美,目前公司共收到430万元回款,公司正向当地法院申请执行其部分资产
- 全球最资讯丨广东东莞国金大厦地块调整批前公示 将增加8.25万平米住宅供地等
- 世界微速讯:电投能源:发行价格将根据国资委和证监会有关规定确定,获得批复后,公司会努力早日完成发行
- 爱与责任同行,做难而正确的事!太平洋房屋荣膺“年度社会责任奖”!
- 当前焦点!金融街控股拟22.6亿元转让孙公司北京融嘉100%股权予华融基础
- 华融化学:公司不直接从事异质结电池业务
- 昊志机电:公司2022年度业绩情况以及相关案件进展情况请以公司后续披露的相关公告为准
- 1099元酒店隔离套票:无法随意出门,只能点外卖
- 大理遭遇大面积客房退单,春节预定量从80%下降到不足30%
- 焦点速讯:酒店业变革趋势:人工智能支持下的数字营销演化逻辑
- Cirium:航空市场复苏遇“气流颠簸”
- 全球动态:获年度品牌影响力大奖 合生创展的品牌力启示录
- 晨化股份:截止2022年12月20日公司股东人数约为1.78万人
- 12月21日煌上煌涨停分析:休闲食品,社区团购,新零售概念热股
- 12月21日南岭民爆涨停分析:民爆,湖南国企改革,国企改革概念热股
- 天天资讯:山东省:房企不得使用商业承兑汇票等非现金方式支付工程款
- 每日消息!12月21日人民网涨停分析:传媒,直播/短视频,彩票概念热股
- 恒大公布债务重组进展:分歧收窄 偿债资源是否能产生预期价值存在较大不确定性
- 天津公积金二套贷款首付由60%降至40%
- 天天关注:广东惠州:高层次人才公积金贷款额度可达100万元
- 焦点播报:12月21日友阿股份涨停分析:新零售,免税店概念,长寿药NMN概念热股
- 冠珠和金舵瓷砖哪个好?产品和服务给出答案
- 甜啦啦以鲜果茶,迎战全民大健康
- 浙东“绿色充电宝”宁海抽水蓄能电站双坝完工
- 长安汽车与宁德时代将组建动力电池合资公司
- 精选!比亚迪中标山东电工电气东营储能示范项目设备采购!
- 世界观点:锂电85%,液流电池70%!江西省能源局发文加强新型储能项目全过程管理
- 今日快讯:甘肃武威市与中国三峡新能源(集团)股份有限公司签署甘肃黄羊抽水蓄能电站项目投资开发协议
- 平安基金中标西安高新区保障房公募REITs基金管理服务项目
- 环球速看:城投鹏基拟于新疆参股成立城市运营管理公司 注册资本100万元
- 中原内配:截止2022年12月20日,公司股东总户数为59,962户
- 昆明垠创地产名下经典尚城二期法拍以底价约3.65亿成交
- 视焦点讯!“20红星05”持有人会议:将延长至2023年2月26日付息
- 消息!悦心健康:截止至2022年12月20日,公司股东总户数77,166户,机构总户数为792户
- 奕东电子:公司持续保持与客户的沟通,积极推进市场开发工作
- 当前焦点!百联东方商厦嘉定店因经营调整需要将于12月30日闭店
- 浔兴股份: 公司生产基于车间管理数据采集、处理辅助决策,提升管理效率
- 天天热议:容量补偿0.35元/度!补偿期不超过10年!内蒙古发文支持新型储能发展!
- 【天天播资讯】梅西为什么搭乘经济舱回国?
- 宁吉喆:明年必将迎来旅游业的恢复性增长热潮
- 两天销售额1.57亿元 宝企便携储能出海动能强劲
- 每日速递:多地更新峰谷电价政策 价差扩大打开储能等产业空间
- 环球百事通!国家电投发布900MWh储能设备电商化采购招标公告
- 亿嘉居住房地产底价1.34亿元竞得宜兴市一宗商住地
- 今日报丨强制转让!中国恒大减持7亿股恒大物业股权
- 全球关注:市值仅15亿 控股股东免费送7亿现金!上市公司再现花式“保壳” 交易所火速关注
- 全球今亮点!长江健康:阿胶是我国古老的名贵药材,具有补血滋阴,润燥,止血
- 滚动:普洛斯与中国联通加深战略合作 将深耕数字化基础设施发展
- 天天热点评!亿田智能:截止2022年12月20日股东总户数为8796户
- 【播资讯】脂肪肝“神药”来了,一个全新的百亿美元赛道?
- 当前最新:外汇交易提醒:日本央行意外调整国债收益率范围,日元暴涨近4%创四个月新高
- 【播资讯】河北石家庄投入超亿元资金促消费惠民生 提振消费信心
- 全球头条:酒店业备战元旦、春节传统旺季
- 全球热点!住房消费限制性政策被点名 限购、限贷松绑呼声最高
- 【全球速看料】顺腾国际控股拟1.25亿港元收购香港九龙物业
- 【全球热闻】12月21日重点数据和大事件前瞻
- 热资讯!11月武汉二手房价9连跌 10重点城市仅1地环比上涨
- 广州第四批集中供地收官:5宗地揽金191亿,保利95.8亿拿地
- 世界快看:深圳首个不限购“住宅”入市:每平方米近9万元,剩44年产权
- 全球热消息:油价“三连跌”收官 加一箱油将少花约18.5元
- 环球观热点:国家发改委:全国能源供需总体平稳有序
- 简讯:国际油价19日 上涨
- 公积金如何取出来用 如何将公积金取出来用
- 农行保险理财产品五年定期可以退吗 可以退农行保险理财产品五年定期吗
- 看热讯:学平险多久能理赔下来 学平险理赔要多久才能到账
- 全球播报:今日24时起 汽、柴油每吨降低480元和460元
- 当前信息:网上退保怎么办理流程 怎么办理网上退保
- 焦点讯息:大象转身!油气巨头收购7700MW风光项目!
- 最资讯丨中炬高新:控股股东中山润田所持1087万股因无人报价流拍
- 天天微资讯!美股异动 | 短视频营销服务商宝盛(BAOS.US)涨超21% 总市值约为832万美元
- 如何查询车辆保险是否到期 车辆保险是否到期如何查询
- 越秀房托1.26亿港元向越秀地产收购香港两项商业物业
- 头条焦点:百果园上市申请已通过聆讯 一个月前重新递交招股书
- ST开元收关注函 5000万元增资碳酸锂生产项目是否存在炒作
- 当前最新:松绑后的厦门2022年土拍收官战 揽金80亿与华润、建发夺地
- 青岛第四批供地收官:39宗地收金约135亿元,1宗流拍
- 当前热文:武商集团:公司皇经堂拆迁事宜尚在洽商中