Servlet
即用于处理HTTP请求的类,该部分的类都处于javax.servlet
下。
页面跳转方式
分为服务器跳转和客户端跳转,主要区别就是地址栏发生改变(前者不变,后者改变)
Servlet的三种创建方式
1.实现Servlet接口
主要实现里面的service
方法,当客户端发起请求,就会执行该方法,举例:
package com.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class ServletTest implements Servlet {
public void destroy() {
// 结束
}
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
// 初始化
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println("aaa");
}
}
2.继承GenericServlet类
该类实现了servlet
接口的大部分方法,并提供了一些新方法,只需重写service
即可,举例:
package com.servlet;
import java.io.IOException;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class GenericTest extends GenericServlet {
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
System.out.println("service");
}
}
3.继承HttpServlet类
该类继承于GenericServlet
类,并提供了对于各种请求方式的处理方法,主要如下:
(1)doGet()
:处理get请求,是HTTP缺省方法
(2)doPost()
:处理post请求
(3)doPut()
:处理put请求
(4)doHead()
(5)doOptions()
(6)doDelete()
(7)doTrace()
一般都用前两种方法就行,举例:
package com.aaa;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Test extends HttpServlet {
//处理get方法
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("this is get method...");
out.flush();
out.close();
}
//处理post方法
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("this is post method...");
out.flush();
out.close();
}
}
WEB路由映射
servlet要想被外部浏览器访问到,则需要配置路由映射地址,主要有两种方式:传统的是通过配置WEB-INF
下的web.xml
文件;另一种是使用Servlet 3.0(Java EE 6.0)提供的@WebServlet()
注解
1.web.xml路由配置
基本的配置格式如下:
//注册servlet
<servlet>
<servlet-name>servlet名称</servlet-name>
<servlet-class>servlet类的位置</servlet-class> //该句本质是利用了反射机制(Class.forName("")来实例化)
<load-on-startup>2</load-on-startup> //可选,设置服务器启动时就实例化该servlet,里面是优先级数值,越小越优先,0是服务器启动的优先级
</servlet>
//设置servlet映射
<servlet-mapping>
<servlet-name>和上面注册的servlet相同的名称</servlet-name>
<url-pattern>/路由映射</url-pattern>
</servlet-mapping>
举例:
<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-class>com.mvc.servlet.Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/Servlet</url-pattern>
</servlet-mapping>
设置映射路由时注意点:
(1)必须要在前面加/
,有一种例外:*.xxx
,此时前面不加/
,代表只要结尾是.xxx
请求都能够访问(其中/
的优先级高于这种例外情况)
(2)一个servlet可以配置多个映射路由
(3)可以使用通配符,比如路由后面是xxx/*
,则代表路由的最后面是xxx/
加任意内容都能够映射
举例:
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/Test</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>*.done</url-pattern>
</servlet-mapping>
依次对应三种映射访问下面网址可以发现访问成功:
http://127.0.0.1:8080/XXX/Test
http://127.0.0.1:8080/XXX/servlet/asfa
http://127.0.0.1:8080/XXX/das.done
2.使用WebServlet注解
通过给要配置路由映射的Servlet加上@WebServlet(映射路由)
的注解即可进行访问,举例:
@WebServlet("/abc")
public class Test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
ServletOutputStream out = response.getOutputStream();
out.print("/aaa");
}
}
此时即可访问/abc
的路由,如果要配置多个路由,可以传入一个数组,举例:
@WebServlet({"/abc", "/ccc"})
注:
查看源码可发现其本质也是加入了web.xml中需要配置的一些属性的注解
生命周期
servlet的生命周期遵循步骤:实例化(构造方法) -> init()
-> doGet()
/doPost()
-> destory()
,其中init()
仅在第一次访问时该servlet时执行(即初始化),doGet()
/doPost()
在每次通过get
/put
方法访问该servlet时执行,destory()
则在服务器关闭或者长时间不使用该servlet时执行,举例:
package com.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LifeCycle extends HttpServlet {
public void init() throws ServletException {
System.out.println("初始化...");
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println("处理get请求");
out.flush();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("处理post请求");
}
public void destroy() {
System.out.println("结束");
super.destroy();
}
}
在web.xml中配置如下:
<servlet>
<servlet-name>LifeCycle</servlet-name>
<servlet-class>com.servlet.LifeCycle</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LifeCycle</servlet-name>
<url-pattern>/servlet/LifeCycle</url-pattern>
</servlet-mapping>
线程安全问题
不要使用全局变量,使用局部变量
配置文件操作
对于配置文件web.xml,可以使用ServletConfig
来操作,其下提供方法:getInitParameterNames()
获取所有配置信息名称,getInitParameter()
来获取配置信息内容。其中配置信息在web.xml的servlet里通过<init-param>
里配置<param-name>
和<param-value>
即可,举例:
web.xml中:
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>com.Test</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</servlet>
在servlet中操作时有以下几种方式:
(1)通过重写继承的public void init(ServletConfig config) throws ServletException
方法获取config对象,从而进行调用,举例:
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Test extends HttpServlet {
private ServletConfig config;
public void init(ServletConfig config) throws ServletException {
this.config = config; //重写方法,获取config对象
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(config.getInitParameter("encoding")); //utf-8
}
}
(2)通过super获取父类的config对象,从而调用方法,举例:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(super.getInitParameter("encoding")); //调用父类的config,输出utf-8
}
}
(3)通过调用自身的getServletConfig()
方法获取config对象,然后调用方法,举例:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(this.getServletConfig().getInitParameter("encoding")); //输出utf-8
}
}
ServletContext
代表整个应用,域对象为当前应用,可以使多个servlet共享数据,通过getServletContext()
获取
1.获取全局配置信息:getInitParameter()
在ServletContext中使用该方法能够获取全局配置信息,即所有Servlet中的配置信息。在web.xml中配置全局信息通过在<context-param>
标签里设置<param-name>
和<param-value>
即可。举例:
在web.xml中:
<context-param>
<param-name>name</param-name>
<param-value>aaa</param-value>
</context-param>
在Servlet中:
public class Test2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(this.getServletContext().getInitParameter("name"));
}
}
2.获取资源路径:通过getRealPath(path)
能够得到一个当前项目的绝对路径+path的路径,从而可以读取该项目下的文件内容,举例:
String path = this.getServletContext().getRealPath("/WEB-INF/1.txt");
//得到路径:C:\Program Files\Apache Software Foundation\Tomcat 7.0\webapps\NewTest\WEB-INF\1.txt
FileReader fr = new FileReader(new File(path));
int i;
while((i = fr.read())!=-1)
System.out.print((char)i);
3.全局键值对配置:通过setAttribute()
添加键值对,getAttribute()
获取键值对,removeAttribute()
删除键值对,举例:
在Servlet1中:
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 Test extends HttpServlet {
int i = 0;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletContext application = this.getServletContext(); //获取ServletContext对象,如果没有该属性值则为null
application.setAttribute("name", i++); //设置属性
}
}
在Servlet2中:
public class Test2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(this.getServletContext().getAttribute("name")); //获取全局的name属性
}
}
Servlet转发
通过ServletContext
下的getRequestDispatcher(路由地址)
方法来获取一个RequsetDisapatcher
对象,然后通过该对象的forward()
方法进行转发,举例:
ServletContext application = this.getServletContext();
RequestDispatcher rd = application.getRequestDispatcher("/Test"); //转发到/Test路由的对象
rd.forward(request, response); //将前面的请求和响应一起转发
转发和重定向区别
转发是直接从一个Servlet跳转到另一个Servlet,所以地址栏不变,并且可以传输数据,但是只能在该应用中转发,无法转发到其他如百度这样的地址中;而重定向是将要访问的地址返回给客户端让其再次去访问新地址,即两次都是客户端进行访问的,所以地址栏会发生变化,并且无法传输数据,但是没有地址限制
请求包含
通过include()
可以实现请求包含,即把被包含的Servlet合并到原来的Servlet里一起执行,举例:
request.setAttribute("name", "aaa");
request.getRequestDispatcher("/Test2").include(request, response);
HttpResponse
对客户端请求的响应结果,由响应行、响应头、响应正文组成
响应行
里面由协议信息和响应状态码组成,举例:
HTTP/1.1 200 OK
其中可以通过setStatus(int)
方法设置响应状态码
响应头
即服务端返回的response,主要提供了以下方法:
(1)sendRedirect(路由)
:请求重定向
(2)addHeader(name, value)
:添加响应头信息,因为响应头的键名可以重复,所以即使添加的信息名已存在,也不会被覆盖,举例:
response.addHeader("aaa", "111");
response.addHeader("aaa", "222");
结果返回的响应头如下:
aaa:111
aaa:222
(3)setHeader(name, value)
:设置响应头信息,和上面的不同,这个会将原来的内容覆盖,当不存在该键名时则会添加一个,举例:
response.setHeader("content-type", "text/html; charset=utf-8");
此时打开页面会默认用utf-8
编码。
(4)addCookie()
:添加cookie信息
(5)Refresh()
:刷新页面
响应正文
即页面展示的内容,主要提供了以下方法:
(1)getWrite()
:字符输出流,返回一个PrintWriter
对象
(2)getOutputStream()
:字节输出流,返回一个ServletOutputStream
对象,和上面一个流对象不能并存,否则报错,使用举例:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = this.getServletContext().getRealPath("/img/1.jpg");
FileInputStream in = new FileInputStream(new File(path));
ServletOutputStream out = response.getOutputStream();
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
}
结果会将图片WebRoot/img/1.jpg
展示在页面中。
注:
向上面两个流对象里面写入的数据将存到response正文当中,最终被Servlet引擎读取并输出到客户端。并且service方法结束后,Servlet引擎也会检查是否调用了close()
方法,如果没有则会自动将其关闭
(3)setCharacterEncoding()
:设置服务器编码
(4)setContentType()
:设置页面内容,这个方法和上面一个方法结合起来就相当于:
response.setHeader("content-type", "text/html; charset=编码"); //设置浏览器编码
常用响应头
文件下载
如果要下载文件则设置content-disposition
响应头,举例:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = this.getServletContext().getRealPath("/img/1.jpg");
FileInputStream in = new FileInputStream(new File(path));
ServletOutputStream out = response.getOutputStream();
byte[] b = new byte[1024];
int len = 0;
String filename = URLEncoder.encode("图片1.jpg", "utf-8"); //避免文件名编码问题
response.setHeader("content-disposition", "attachment;filename=" + filename); //设置该响应头后会自动下载文件
response.setHeader("content-type", "image/jpeg"); //页面类型为图片
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
}
不使用缓存
设置pragma
响应头和cache-control
响应头,举例:
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setHeader("expires", "0");
这三种分别对应不同浏览器使用
定时刷新页面
设置refresh
响应头,举例:
response.setHeader("refresh", "1"); //1秒自动刷新一次
如果需要刷新后跳转到某个页面,可以再设置第二个url值,举例:
response.setHeader("refresh", "1;url=http://www.baidu.com"); //1秒后跳转到别的页面
重定向
过程:
假如客户端发出访问地址1的请求,但是实际上资源在地址2,此时地址1则会返回重定向的地址2,然后客户端接收到了地址2以后再次发出访问地址2的请求。从中可以看出整个过程中地址1和地址2都是由客户端去访问的,而不是地址1去访问的地址2,所以在地址1返回重定向地址前,其会将所有的内容执行完。
实现:
设置响应状态码为302
,并设置location
响应头,举例:
Servlet中:
response.setStatus(302);
response.setHeader("location", "/NewTest/Test"); //跳转到url
System.out.println("这句会执行完再返回重定向地址");
HttpRequest
客户端发起的请求,由请求行、请求头和请求正文组成
请求行
(1)getMethod()
:获取请求方法
(2)getRequestURL()
:返回客户端发出请求的url
(3)getRequestURI()
:返回请求行的资源名称部分
(4)getContextPath()
:返回当前应用的虚拟目录
(5)getQueryString()
:返回请求行的参数部分
请求头
(1)getHeader(name)
:返回请求信息的值
(2)getHeaders(name)
:和上面功能一样,但是对于请求名对应多个值的上面的方法只能返回第一个,而这个方法能全部返回,是枚举类型
(3)getHeaderNames()
:返回请求头的所有name,是Enumeration对象,举例:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Enumeration<String> x = request.getHeaderNames();
while(x.hasMoreElements()){
String e = x.nextElement();
System.out.println(e + ":" + request.getHeader(e));
}
(4)getAttribute(name)
:获取非表单属性,设置属性用setAttribute(name, value)
,删除属性用removeAttribute(name)
,一般用于Servlet转发的时候,举例:
Servlet1中:
request.setAttribute("name", "aaa");
request.getRequestDispatcher("/Test").forward(request, response);
Servlet2中:
System.out.println(request.getAttribute("name"));
(5)getParameter(name)
:获取表单的参数值,对于重名的(比如复选框)使用getParameterValues()
,返回String数组
(6)getParameterNames()
:获取所有参数名,是Enumeration对象
(7)getParameterMap()
:获取所有的参数键值对,是Map<String, String[]>类型,举例:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Map<String, String[]> map = request.getParameterMap();
for(Map.Entry<String, String[]> m: map.entrySet()){
System.out.println(m.getKey() + ":" + Arrays.toString(m.getValue()));
}
(8)getRemoteAddr()
:获取客户端ip
请求数据编码问题
对于post和get请求的数据,往往需要设置编码来防止乱码,但两种请求对应的设置方法不同:
(1)post方法
通过setCharacterEncoding()
方法解决post方法的编码问题,举例:
(2)get方法
通过将接受的数据通过String(name.getBytes(原编码), 转编码)
解决,举例:
String name = request.getParameter("name");
String name = new String(name.getBytes(原编码), 转编码);
Cookie
客户端技术,其将数据存在客户端本地,但只能存字符串,且不能为中文。通过HttpRequest
对象下的getCookies()
方法可以获取Cookie对象,通过HttpResponse
对象下的addCookie()
方法可以添加cookie
常用方法
(1)getName()
:获取cookie名字
(2)setValue()
:修改内容(不能为中文)
(3)setMaxAge()
:设置保存时间(正数是秒,负数代表浏览器缓存,0代表删除),如果没设置保存时间,则关闭浏览器后即被删除
(4)setPath()
:设置路径,此时该cookie只有在该路径下才能读取,删除前也需要设置正确路径才能删除成功
使用举例
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Cookie cs[] = request.getCookies();
for (Cookie c : cs) {
System.out.println(c.getName() + ":" + c.getValue());
}
Cookie c = new Cookie("name", "aaa");
c.setMaxAge(3600); //1个小时有效
response.addCookie(c); //添加cookie
}
Session
服务端技术,在一个浏览器中独占一个Session,关闭浏览器即消失,可以存对象。通过HttpRequest
对象下的getSession()
方法可以获取HttpSession对象
常用方法
(1)setAttribute()
/getAttribute()
/removeAttribute()
:操作session里的属性
(2)setMaxInactiveInterval()
:设置有效时间,单位是秒,不设置默认30分钟后销毁
(3)invalidate()
:使会话无效,并取消任何和该对象的绑定
(4)isNew()
:是否为一个新的session
(5)getId()
:获取session的id
getSession()内部原理
(1)获取cookie中键为JSESSIONID
的值
(2)若不存在则创建一个HttpSession
对象,并分配一个唯一的SessionId
,并添加一个键为JSESSIONID
值为SessionId
的cookie
(3)如果存在该cookie的值,则获取值以后从服务器内存中根据ID寻找该HttpSession对象,找到则继续服务,找不到(如执行了invalidate()
或者session过期的情况)则继续执行(2)
注:
从上面可以看出session是基于cookie,所以如果把cookie禁用,那么一般session也就无法使用了
Session失效
(1)默认超过半小时自动过期
(2)执行invalidate()
方法强制销毁
(3)超过setMaxInactiveInterval()
方法设置的有效时间
(4)在web.xml中通过<session-config>
和<session-timeout>
标签设置失效时间(单位为分钟),举例:
<session-config>
<session-timeout>1</session-timeout> //1分钟后过期
</session-config>
Session数据保存
当服务器重启或者关闭时原来的Session内容将会消失,为了避免这种情况,可以给对应要保存状态的类继承Serializable
接口,即可序列化,此时在重启前服务器会自动将数据进行序列化保存,从而避免了数据丢失的问题
网友评论