Servlet是什么?
Java Servlet是由Java编写的运行在服务器端上的程序,作为服务端和客户端的中间层。根据来自客户端的http请求生成动态Web内容,并由服务端将响应内容返回给客户端。通常地,Servlet作为MVC架构模式中Controller层的组件,将来自View层(html/css/jsp)的请求交给Model层处理完毕后,再将处理结果返回给View层。
Servlet体系结构
Servlet接口是所有Servlet类必须直接或间接实现的一个接口,GenericServlet抽象类是HttpServlet的父类,HttpServlet除了Servlet有的方法之外还添加了doGet、doPost、doDelete、doPut等方法处理http协议的请求。
Servlet接口
ServletConfig对象
在Servlet初始化时,Servlet容器会给Servlet的init方法传入一个ServletConfig的实例,该实例中封装了当前Servlet在web.xml
中的配置信息。通过此对象可以读取到web.xml配置文件< init-param >标签中的初始化参数,使程序更加灵活。
String getInitParameter(String name) //当前Servlet获取name参数值
String getServletName() //获取当前Servlet在web.xml中配置的名字
Enumeration getInitParameterNames() //获取当前Servlet所有初始化参数的名字组成的枚举
ServletContext getServletContext() //获取代表当前web应用的ServletContext对象
ServletRequest接口
对于每个http请求,Servlet容器都会创建一个封装了请求信息的ServletRequest对象,并把这个对象传递给service()方法。
int getContentLength(); //返回请求主体的字节数
String getContentType(); //返回请求主体的类型
String getParameter(String name); //返回请求参数的值
getParameter是ServletRequest最常用的方法,通常用来返回html表单域的值,也可以获取查询字符串的值
ServletResponse接口
表示一个Servlet响应,调用Servlet的service()方法之前由Servlet容器创建,并把ServletResponse对象作为第二个参数传给service方法。
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
void setContentType(String var);
void setCharacterEncoding(String var);
GenericServlet抽象类
GenericServlet类实现了Servlet和ServletConfig两个接口,将init方法中的ServletConfig赋给了一个内部的ServletConfig来保存ServletConfig对象,让程序员不用维护该对象可直接调用getServletConfig()方法获取。此外,该类该提供了一个不带参数的init()方法让我们使用它的继承类时候直接覆盖即可,且GenericServlet类仍然保存着ServletConfig对象,使代码更具简洁性。
void init(ServletConfig servletconfig) throws ServletException
void init() throws ServletException
ServletConfig getServletConfig()
Servlet的生命周期
Servlet的生命周期分为5个阶段:
- 加载:当Tomcat第一次访问Servlet的时候,Servlet容器会负责创建Servlet的实例。
- 初始化:当Servlet被实例化后调用init()方法初始化Servlet对象,该方法只在第一次请求Servlet时调用,Servlet容器会传入一个ServletConfig对象来对Servlet初始化,后续请求中不会再被Servlet容器调用。
- 处理服务:当服务器请求Servlet时,Servlet容器就会调用service()方法。该方法会传入一个ServletRequest对象和一个ServletResponse对象作为参数来处理请求。
- 销毁:当要销毁Servlet时,调用destory()方法让该实例释放掉所占用的资源。一个Servlet对象长期不使用也会被自动销毁。
- 卸载:Servlet调用销毁方法后,等待垃圾回收。下次需要Servlet时,重新调用init()方法进行初始化操作。
Servlet的使用
HttpServlet抽象类继承于GenericServlet抽象类,使用HttpServlet抽象类时,还需要借助分别代表Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象(由ServletRequest和ServletResponse转换而来)。
Servlet的实现方式
一般我们自定义的Servlet都是继承HttpServlet类来编写的,它实现了Servlet接口的所有方法,而且在原有接口上添加了一些与HTTP协议处理相关的方法。实现步骤有三步:
- 继承HttpServlet类
- 重写doGet()和doPost()方法
- 配置web.xml(也可以使用@WebServlet注解)
Servlet的配置
编写一个Servlet之后,我们要配置Servlet才能让Servlet容器知道怎么访问,有两种方式配置Servlet:
- web.xml中配置< servlet >和< servlet-mapping >两个标签
- 注解访问Servlet
web.xml配置
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>org.qwb.dao.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
- 当浏览器发送请求时,发现web.xml配置文件中< url-pattern >标签的映射路径;
- 根据映射路径的标签值< servlet-name >进行匹配,找到Servlet编译后class文件存放的位置;
- 从而调用并执行相应的Servlet类
注解配置
在相应的Servlet类中添加Servlet注解,当浏览器发送请求时,在当前工程路径下寻找是否存在对应url名称的@WebServlet注解,调用并执行相应的Servlet类。
Servlet其他配置
Servlet的路径映射问题
我们可以在web.xml文件配置多个映射路径来访问用一个Servlet,如下:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>org.qwb.dao.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/qwb</url-pattern>
</servlet-mapping>
< url-pattern >中映射的url路径也可以使用两种通配符()格式,一种是*.
扩展名;一种是正斜杠(/)开头并以“/”结束。由于通配符的引入有可能一个路径被多个< url-pattern >匹配,所以通配符格式访问有优先级判断:
- 哪个精确找哪个
- *.后缀的格式匹配级最低
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>org.qwb.dao.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
缺省Servlet
缺省Servlet用于处理所有其他Servlet都不处理的访问请求,将一个Servlet的< url-pattern >配置为正斜杠"/",这个Servlet就变成了缺省Servlet。第一次启动Tomcat服务器时看到的页面就是一个缺省Servlet:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Servlet初始化参数
在Servlet的配置文件中,可以使用一个或多个< init-param >标签在< servlet >标签中为Servlet配置一些初始化参数。
load-on-startup
如果在< servlet >标签中配置了< load-on-startup >,那么Web应用程序在启动时,就会装载并创建Servlet的实例、以及调用Servlet实例对象的init()方法。
Request对象
HttpServletRequest接口扩展于javax.servlet.Servlet接口,表示Http环境中的Servlet请求。
常用API:
String getContentPath(); //返回请求上下文的请求URI部分
Cookie[] getCookies(); //返回一个cookie对象数组
String getMethod(); //返回生成这个请求http的方法名称
HttpSession getSession(); //返回与这个请求相关的会话对象
获得http请求行:
StringBuffer getRequestURL(); //返回客户端发出请求时的完整URL
String getRequestURI(); //返回请求行中的资源名部分
String getQueryString(); //返回请求行中的参数部分
获得http请求头:
String getHeader(String name);
Enumeration getHeaders(String name);
Enumeration getHeaderNames();
获得http请求体:
String getParameter(String name);
String[] getParameterValues(String name);
Enumeration getParameterNames();
Map< String, String[] > getParameterMap();
HttpServletRequest应用
防盗链
可以调用request.getHeader("Referer")
方法获取来自http请求中请求头的Referer消息头,判断消息头中的url是否是特定网站,如果不是则重定向跳转到该页面,从而实现防盗链。
获取表单的提交数据
getParameter(String name)
和getParameterValues(String name)
两个方法可以用来获取到http请求中的请求参数,从而得到一个或多个值。
解决乱码问题
Tomcat服务器默认为ISO-8859-1编码格式,当浏览器使用UTF-8编码格式时,request用默认编码格式解码数据就会出现乱码的情况,可以用request.setCharacterEncoding("utf-8")
解决post方式提交的乱码问题,get提交则用parameter = newString(parameter.getbytes("iso8859-1"), "utf-8");
。
Response对象
HttpServletResponse接口扩展于javax.servlet.servletResponse接口,表示Http环境中的Servlet响应。
`常用API:
void addCookie(Cookie cookie); //给该响应添加一个Cookie
void addHeader(String var1, String var2); //给请求添加响应头
void sendRedirect(string var) throws Exception; //重定向
void setStatus(int var); //设置响应行的状态码
ServletOutputStream getOutputStream() throws IOException; //通过字节流向response写入字节并返回响应
PrintWriter getWriter() throws IOException; //通过字符流将字符串写入response返回响应
getOutputStream()和getWriter()方法
这两个方法都是用来发送响应消息体并向客户端输出数据,但是他们之间互相排斥,不可以同时使用,否则会出现异常。
getOutputStream( )
该方法获取一个ServletOutputStream字节输出流对象,为OutputStream的子类。它通过字节流的write(byte[] bytes)
向response缓冲区写入字节,getOutputStream()方法可以使用print()也可以使用write()输出数据。
**乱码问题解决:**无论使用print还是write方法,由于字节流对象输出的是二进制数据且Servlet容器的编码跟客户端浏览器的编码可能不一致,所以存在着输出乱码的问题。因此,我们可以先让服务端对response的编码方式改为utf-8,再设置http响应的消息头让浏览器自己设置utf-8的编码格式:
response.setCharacterEncoding("utf-8"); //服务端编码格式
response.setHeader("Content-Type", "text/html;charset=UTF-8"); //客户端编码格式
response.getOutputStream().write("xxxxx".getBytes("UTF-8"));
getWriter()
该方法获得一个字符流对象,通过字符流的write(String s)
将字符串设置到response缓冲区中,Servlet容器再将response缓冲区的内容组装成http响应返回给客户端。
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("xxxx");
tips:setContentType是解决中文乱码问题的第二种方式,它不仅设置浏览器用utf-8显示数据,内部还把中文转码的码表设置成utf-8。
HttpServletResponse应用
文件下载
客户端可以通过访问Servlet来实现文件下载功能,从路径获取并读取到资源后把读取到的内容通过io流发送到浏览器。
String path = this.getServletContext().getRealPath("文件路径");
FileInputStream fileInputStream = new FileInputStream(path);
String fileName = path.substring(path.lastIndexOf("\\")+1);
//设置消息头,告诉浏览器要下载的资源
response.setHeader("Content-Disposition", "attachment; filename="+fileName);
//解决文件名乱码实现方式
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
//读取到的内容回送到浏览器
int len = 0;
byte[] bytes = new byte[1024];
ServletOutputStream servletOutputStream = response.getOutputStream();
while( (len = fileInputStream.read(bytes)) > 0){
servletOutputStream.write(bytes, 0, len);
}
servletOutputStream.close();
fileInputStream.close();
实现自动刷新和自动跳转
我们可以通过修改消息头以规定的时间让页面刷新,更新资源。
response.setHeader("Refresh", "3"); //每三秒自动刷新页面一次
除了自动刷新还能实现页面的跳转,跳转到index.jsp页面:
response.setHeader("Refresh", "3;url='/index.jsp'");
ServletContext对象
Tomcat容器在启动的时候,它会为每个Web应用程序都创建一个对应的ServletContext对象,代表着当前web应用。ServletContext对象被称为域对象,可以理解为一个类似于Map集合的容器。一个web站点中的所有Servlet都共享着一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。
ServletContext的三种获取方式:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext1 = this.getServletContext();
ServletContext servletContext2 = this.getServletConfig().getServletContext();
ServletContext servletContext3 = req.getSession().getServletContext();
}
有了ServletContext对象,就可以从应用程序的所有资料访问信息,并且可以动态注册Web对象。保存在ServletContext中的对象被称作为属性。
Servlet之间实现通讯
实现Servlet之间通讯就要用到ServletContext的setAttribute(String name, Object obj)
方法,可以在整个web应用范围内共享数据。
处理属性的方法:
void setAttribute(String name, Object obj); //存数据
Object getAttribute(String name); //取数据
void removeAttribute(String name); //删除数据
Enumeration<String> getAttributeNames() //返回所有属性名的枚举类型
获取web.xml配置文件信息
使用< context-param >标签为整个web应用配置属性的话,那么所有的Servlet就能访问里面的参数(可以把数据库的配置信息放在这里)。
web.xml文件配置:
<context-param>
<param-name>name</param-name>
<param-value>abc</param-value>
<context-param>
配置信息的获取:
ServletContext servletContext = this.getServletContext();
String value = servletContext.getInitParameter("name");
加载资源文件
- 在/WEB-INF/classes目录下读取文件:
通过Servlet直接读取src目录下的文件是不行的,根据web的目录规范,Servlet编译后的class文件存放在classes文件夹内,所以要进入到classes目录中通过绝对路径读取文件,当代码模块转移需要修改代码,显得不方便。
FileInputStream fileInputStream = new FileInputStream("D:\\test\web\WEB-IN\classes\test.jpg");
``
这时,我们可以用ServletContext读取直接避免修改代码的情况,因为ServletContext对象是根据当前web站点生成的。
```java
ServletContext servletContext = this.getServletContext();
InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/classes/test.jpg");
- web目录下直接通过文件名读取
ServletContext servletContext = this.getServletContext();
InputStream inputStream = servletContext.getResourceAsStream("test.jpg");
- 通过类加载器读取资源文件,文件放在src目录下。
//获取到类加载器
ClassLoader classLoader = ServletTest.class.getClassLoader();
//通过类加载器获取到读取文件流
InputStream inputStream = classLoader.getResourceAsStream("test.jpg");