servlet时访问动态资源的技术,由web服务器创建并调用。
快速入门
1.servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<!--编译和测试有效,运行环境无效。因为打包发布到的tomcat中也有servlet的jar包,运行会jar包冲突-->
<scope>provided</scope>
</dependency>
2.创建Servlet接口的是实现类
在main的java目录中创建类实现Servlet接口,并使用@WebServlet("/servletStart")注解配置该servlet的访问路径
package com.kdy.web;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebServlet("servletStart")
//@WebServlet(urlPatterns = "/servletStart",loadOnStartup = 0)
/*loadOnStartup不加默认第一次访问该servlet执行时创建servlet对象并初始化
loadOnStartup=负整数时同上
loadOnStartup为0或正整数时,web服务器启动时创建servlet对象,数字越小,优先级越高
*/
public class ServletStart implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("servlet init hello!");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet service hello!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("servlet destroy hello!");
}
}
3、启动Tomcat
run maven->tomcat7 run或idea集成tomcat部署后启动
访问http://localhost:8080/MavenWeb3_war/servletStart即可
init()方法仅在创建servlet对象时调用一次,service()方法每次访问该servlet时调用。
destroy()方法:idea下方terminal中dir找到该模块名,cd 目标模块名进入目录后,mvn tomcat7:run可在终端运行,Ctrl+C模拟web服务器正常关闭,会调用一次destory()方法
HttpServlet
Servlet->GenericServlet(抽象实现类)->HttpServlet(对Http协议封装的实现类)
原理
根据不同的请求方式调用不同的doXxx()方法,类似下面我们自己写的MyHttpServlet,继承Servlet后在service()方法中进行根据请求方法分别处理。之后这个MyServlet就可拿来用了。
@Override
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
System.out.println("servlet service hello!");
HttpServletRequest request = (HttpServletRequest)req;
String method = request.getMethod();
if("GET".equals(method)){
//get方式的逻辑处理
doGet(req,resp);
}else if ("POST".equals(method)){
//post方式的逻辑处理
doPost(req,resp);
}
}
private void doGet(ServletRequest req, ServletResponse resp) {
}
private void doPost(ServletRequest req, ServletResponse resp) {
}
HttpServlet使用步骤
继承HttpServlet,重写doGet和doPost
@javax.servlet.annotation.WebServlet("/httpServlet")
public class WebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do get ...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do post ...");
}
}
启动tomcat,浏览器访问该servlet路径,默认get方法走doGet方法。
写一个html表单:
<form action="/MavenWeb3/httpServlet" method="post">
<!--action:由于和servlet在同一项目中,就直接项目根路径和servlet路径-->
用户名:<input name="username" type="text"/>
<input name="submit" type="submit" value="提交">
</form>
重新启动tomcat后,先访问http://localhost:8080/MavenWeb3/html/a.html表单页面,提交后以post请求方式访问http://localhost:8080/MavenWeb3/httpServlet,走doPost()方法。
Servlet访问路径urlPattern
匹配多个
@WebServlet(urlPatterns = {"/servletStart","/servletStart2"})
1.精确匹配
@WebServlet(urlPatterns = "/servlet/servletStart")
2.目录匹配
@WebServlet(urlPatterns = "/servlet/*")
3.扩展名匹配
@WebServlet(urlPatterns = "*.jsp")
4.任意匹配
"/"(其他urlPattern都对不上才会走这个)和"/*"(匹配任意)。不推荐使用。
XML文件方式配置Servlet路径和资源
不使用@WebServlet("")注解了,使用web.xml进行配置,在<web-app>里加上以下代码:
<servlet>
<servlet-name>webServlet</servlet-name>
<servlet-class>com.kdy.web.WebServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>webServlet</servlet-name>
<url-pattern>/httpServlet</url-pattern>
</servlet-mapping>
Request与Response
Requst
RequestFacade(tomcat的实现类)->HttpServletRequest(http封装的请求接口)->ServletRequest
请求行、请求头
请求行:GET URI?参数 HTTP版本
请求头:A:B
@javax.servlet.annotation.WebServlet("/httpServlet")
public class WebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = request.getSession(); //request也能getSession
System.out.println("do get ...");
String method = req.getMethod();//GET
String contextPath = req.getContextPath();//虚拟目录(项目访问路径) /MavenWeb3
String requestURL = req.getRequestURL().toString();//统一资源定位符 http://localhost:8080/MavenWeb3/httpServlet
String requestURI = req.getRequestURI();//统一资源标识符 /MavenWeb3/httpServlet
String queryString = req.getQueryString();//请求参数 name=%E5%BC%A0%E4%B8%89&pwd=zhangsan
//获取请求头
String header1 = req.getHeader("Cookie");
String header2 = req.getHeader("User-Agent");
System.out.println(method+"\n"+contextPath+"\n"+requestURL+"\n"+requestURI+"\n"+queryString
+"\n"+header1+"\n"+header2);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do post ...");
}
}
请求体
使用html的form表单发送post请求
<form action="/MavenWeb3/httpServlet" method="post">
<!--action:由于和servlet在同一项目中,就直接项目根路径和servlet路径-->
用户名:<input name="username" type="text"/> <br/>
密码:<input name="password" type="text"/>
<input name="submit" type="submit" value="提交">
</form>
servlet中doPost()方法去接收:
@javax.servlet.annotation.WebServlet("/httpServlet")
public class WebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do get ...");
String method = req.getMethod();//GET
String contextPath = req.getContextPath();//虚拟目录(项目访问路径) /MavenWeb3
String requestURL = req.getRequestURL().toString();//统一资源定位符 http://localhost:8080/MavenWeb3/httpServlet
String requestURI = req.getRequestURI();//统一资源标识符 /MavenWeb3/httpServlet
String queryString = req.getQueryString();//请求参数 name=%E5%BC%A0%E4%B8%89&pwd=zhangsan
//获取请求头
String header1 = req.getHeader("Cookie");
String header2 = req.getHeader("User-Agent");
System.out.println(method+"\n"+contextPath+"\n"+requestURL+"\n"+requestURI+"\n"+queryString
+"\n"+header1+"\n"+header2);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader br = req.getReader();
String line = br.readLine();
System.out.println(line);//username=%E5%BC%A0%E4%B8%89&password=zhangsan&submit=%E6%8F%90%E4%BA%A4
}
}
通用方式获取请求参数
由于GET请求参数获取为req.getQueryString();POST请求参数获取方式为req.getReader().readLine(),可以根据req.getMethod()进行判断GET还是POST。
Request提供了以下三种方法
Map<String ,String[]> getParameterMap():获取所有参数的Map集合
String [] getParammeterValues(String name):根据名称获取参数值(数组)
String getParameter(String name):根据名称获取参数值(单个值)
post请求的表单
<form action="/MavenWeb3/httpServlet" method="post">
<!--action:由于和servlet在同一项目中,就直接项目根路径和servlet路径-->
用户名:<input name="name" type="text"/> <br/>
密码:<input name="pwd" type="text"/>
<input name="submit" type="submit" value="提交">
</form>
servlet演示
@javax.servlet.annotation.WebServlet("/httpServlet")
public class WebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do get ...");
System.out.println("req.getParameterMap()---------------------------------");
Map<String, String[]> map = req.getParameterMap();
Set<Map.Entry<String, String[]>> entries = map.entrySet();
for (String key:map.keySet()) {
System.out.print(key+":");
for (String value:map.get(key)) {
System.out.print(value+" ");
}
System.out.println();
}
System.out.println("req.getParameterValues()-----------------------------");
String[] names = req.getParameterValues("name");
String[] pwds = req.getParameterValues("pwd");
for (String name:names) {
System.out.println(name);
}
for (String pwd:pwds){
System.out.println(pwd);
}
System.out.println("//req.getParameter()------------------------------------");
String name= req.getParameter("name");//如果有多个叫name的参数,仅获取第一个
String pwd = req.getParameter("pwd");//如果有多个叫pwd的参数,仅获取第一个
System.out.println(name);
System.out.println(pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do post...");
doGet(req,resp);
}
}
解决中文乱码问题
在idea中通过集成tomcat或tomcat插件方式启动上述项目就会出现中文乱码问题。可通过以下方式解决:
POST请求参数中文乱码:设置req.setCharacterEncoding("UTF-8");
Tomcat7的GET请求参数中文乱码:name = new String(name.getBytes(StandardCharacters.ISO_8859_1),StandardCharsets.UTF_8);
@javax.servlet.annotation.WebServlet("/httpServlet")
public class WebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");//POST请求的参数的中文乱码解决方式
//Tomcat7的GET请求参数的中文乱码解决方式为 name = new String(name.getBytes(StandardCharacters.ISO_8859_1),StandardCharsets.UTF_8);
System.out.println("do get ...");
System.out.println("req.getParameterMap()---------------------------------");
Map<String, String[]> map = req.getParameterMap();
Set<Map.Entry<String, String[]>> entries = map.entrySet();
for (String key:map.keySet()) {
System.out.print(key+":");
for (String value:map.get(key)) {
System.out.print(new String(value.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8)+" ");
}
System.out.println();
}
System.out.println("req.getParameterValues()-----------------------------");
String[] names = req.getParameterValues("name");
String[] pwds = req.getParameterValues("pwd");
for (String name:names) {
System.out.println(new String(name.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8));
}
for (String pwd:pwds){
System.out.println(new String(pwd.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8));
}
System.out.println("//req.getParameter()------------------------------------");
String name= req.getParameter("name");//如果有多个叫name的参数,仅获取第一个
String pwd = req.getParameter("pwd");//如果有多个叫pwd的参数,仅获取第一个
System.out.println(new String(name.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8));
System.out.println(new String(pwd.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("do post...");
doGet(req,resp);
}
}
注:如果上述servlet演示代码的项目maven clearn后maven package,将target中生成的该模块的文件或war放在tomcat的webapp目录下,重命名MavenWeb31,且conf/loggings.properties设置java.util.logging.ConsoleHandler.encoding = GBK,通过bin/startup.bat启动, 以http://localhost:8080/MavenWeb31/httpServlet?name=张三pwd=zhangsan&name=王五&pwd=wangwu访问,在tomcat的窗口日志展示中没有中文乱码。
Response
ResponseFacade(tomcat定义的实现类)->HttpServletResponse(http封装的请求对象)->ServlertResponse
响应行:协议版本 状态码 状态描述 void setStatus(int sc)设置响应状态码
响应头:key:value void setHeader():设置响应头键值对
响应体:<html><head><body> PrintWriter getWriter()获取字符输出流 ServletOutputStream getOutputStream()获取字节输出流
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//resp.setHeader("content-type","text/html");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.write("中文响应内容输出到页面上");
writer.write("<h1>AAA</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注:tomcat7即使设置了上述utf-8依然会出现浏览器端的响应中文乱码问题。
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
FileInputStream fis = new FileInputStream("D://a.jpg");
ServletOutputStream os = resp.getOutputStream();
/*byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff))!=-1){
os.write(buff,0,len);
}*/
/* 上面代码替换为IOUtils.copy(fis,os);
需添加依赖,使用import org.apache.commons.io.IOUtils;中的IOUtils.copy();
* <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
*/
IOUtils.copy(fis,os);
fis.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
请求转发与重定向
请求转发forward
服务器内部资源共享方式,通过request对象进行转发间的资源共享,同一个web服务器内部servlet转发,浏览器地址栏不发生变化。
request.setAttribute(String name ,Object o); requestgetAttribute(String name);
request.removeAttribute(String name);
request.getRequestDispatcher("/servletB").forward(request,response);
创建两个servlet类演示如下:
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletA...");
req.setAttribute("attribute","attributeObjectString");
req.getRequestDispatcher("/servletB").forward(req,resp);//服务器内部资源调用,不需要加虚拟目录
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/servletB")
public class servletB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletB...");
Object attribute = req.getAttribute("attribute");
System.out.println(attribute);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
启动tomcat后,访问:http://localhost:8080/MavenWeb3/servletA
结果,先访问了servletA又请求转发到servletB,且浏览器地址栏没有发生变化。
重定向redirect
可重定向到任意位置资源(web服务器内外部均可),不能用request共享数据,浏览器地址栏发生变化。
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletA...");
resp.setStatus(302);
resp.setHeader("Location","/MavenWeb3/servletB");//uri:contextPath+servlet路径
//resp.sendRedirect(req.getContextPath()+"/servletB");
resp.sendRedirect("/MavenWeb3/servletB");//将资源发给浏览器,让浏览器去再次访问该路径,要加虚拟目录
//同一服务内部资源可用uri,访问其他服务资源可用url
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
@WebServlet("/servletB")
public class servletB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletB...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
启动tomcat,访问http://localhost:8080/MavenWeb3/servletA浏览器地址栏会跳到http://localhost:8080/MavenWeb3/servletBhttp://localhost:8080/MavenWeb3/servletA会跳到
路径问题
服务端使用,不需要加虚拟目录contextPath,如请求转发。
浏览器端使用,需要加虚拟目录,如重定向发给浏览器由浏览器再发请求,再如超链接、form表单action路径都需加虚拟目录。
虚拟目录context:idea集成tomcat部署项目时的application context,或pom.xml中引入tomcat7插件配置的configuration里的path(不加默认为项目名)。可通过req.getContextPath()动态获取。