一、什么是servlet?
处理请求和发送响应的过程是由一种叫做Servlet的程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西。理解这个的前提是了解一些http协议的东西,并且知道B/S模式(浏览器/服务器)。
二、tomcat和servlet的关系
Tomcat 是Web应用服务器,是一个Servlet/JSP容器.
Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.
而Servlet是一种运行在支持Java语言的服务器上的组件.
Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品.
从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。
但是并不知道是如何转变的,只知道浏览器发送过来的请求也就是request,我们响应回去的就用response。
①:Tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,
所有的HTTP头数据读可以通过request对象调用对应的方法查询到。
②:Tomcat同时会要响应的信息封装为HttpServletResponse类型的response对象,
通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器
Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法,
还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。
所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。
三、编写Servlet代码
1、创建一个MyServlet继承HttpServlet,重写doGet和doPost方法,根据请求的方式是get还是post,然后用不同的处理方式来处理请求。
2、在web.xml中配置MyServlet,为什么需要配置?
让浏览器发出的请求知道到达哪个servlet,也就是让tomcat将封装好的request找到对应的servlet让其使用。
<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>controller.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>
按照步骤,首先浏览器通过http://localhost:8080//servlet
来找到web.xml中的url-pattern,这就是第一步,匹配到了url-pattern后,就会找到第二步servlet的名字servlet,知道了名字,就可以通过servlet-name找到第三步,到了第三步,也就能够知道servlet的位置class了。然后到其中找到对应的处理方式进行处理。
四 、 创建servlet的原理
1 、 servlet的生命周期是什么?
服务器启动时(web.xml中配置load-on-startup=1,默认为0)或者第一次请求该servlet时,就会初始化一个Servlet对象,也就是会执行初始化方法init(ServletConfig conf)。
该servlet对象去处理所有客户端请求,在service(ServletRequest req,ServletResponse res)方法中执行。
最后服务器关闭时,才会销毁这个servlet对象,执行destroy()方法。
2、servlet的生命周期中,可以看出,执行的是service方法,为什么我们就只需要写doGet和doPost方法呢?为什么创建的servlet是继承自httpServlet,而不是直接实现Servlet接口?
(1).httpServlet的继承结构
httpServlet继承GenericServlet,GenericServlet实现了Servlet接口和ServletConfig接口。
GenericServlet(通用Servlet)的作用是什么?
GenericServlet实现Servlet接口的方法,简化编写servlet的步骤。
(2).ServletConfig和ServletContext
getServletConfig()
方法来获取ServletConfig对象,ServletConfig对象可以获取到Servlet的一些信息,例如ServletName、ServletContext、InitParameter、InitParameterNames。
通过查看ServletConfig这个接口就可以知道,ServletConfig接口中ServletContext对象是servlet上下文对象,功能有很多,获得了ServletContext对象,就能获取大部分我们需要的信息,比如获取servlet的路径和servlet方法。
(3).Servlet接口中的内容和作用
Servlet生命周期的三个关键方法,init、service、destroy。
获取ServletConfig,而通过ServletConfig又可以获取到ServletContext。
而GenericServlet实现了Servlet接口后,也就说明我们可以直接继承GenericServlet,就可以使用上面我们所介绍Servlet接口中的那几个方法了,能拿到ServletConfig,也可以通过ServletConfig拿到ServletContext,不过那样太麻烦,不能直接获取ServletContext,所以GenericServlet除了实现Servlet接口外,还实现了ServletConfig接口,那样,就可以直接获取ServletContext了。因为httpServlet继承了GenericServlet,所以我们可以直接使用httpServlet。
(4).GenericServlet类的内容详解
Servlet和ServletConfig接口所实现的方法,有9个,这很正常,
但是我们可以发现,init方法有两个,一个是带有参数ServletConfig的,一个有无参的方法.
public class ServletDemo extends GenericServlet {
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
}
@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
super.init(config);
}
}
首先看init(ServletConfig config)方法,因为init(ServletConfig config)中带有ServletConfig对象,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,然后通过getServletConfig()方法就能够获取ServletConfig对象了,可以通过全局变量保存ServletConfig对象。
在GenericServlet类中增加一个init()方法,如果以后需要在init方法中需要初始化别的数据,只需要重写init()这个方法,而不需要去覆盖init(ServletConfig config)这个方法。
再来看service(ServletRequest req, ServletResponse res) 方法
public class ServletDemo extends GenericServlet {
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
在GenericServlet类中并没有实现该内容,因为在它下面还有一层,还有一个子类继承GenericServlet,并实现该方法,service方法中的参数还是ServletRequest,ServletResponse,并没有跟http相关对象挂钩。
(5).HttpServlet类详解
这个类主要的功能是实现service方法的各种细节和设计。并且通过类名可以知道,该类就跟http挂钩了。
public class ServletDemo extends HttpServlet {
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(arg0, arg1);
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.service(arg0, arg1);
}
}
五.四个重点的对象。ServletConfig、ServletContext,request、response
1.ServletConfig对象
getServletConfig(); //获取ServletConfig对象
getServletName(); //获取servlet的名称,也就是我们在web.xml中配置的servlet-name
getServletContext(); //获取ServletContext对象
getInitParameter(String); //获取在servlet中初始化参数的值。这里注意与全局初始化参数的区分。这个获取的只是在该servlet下的初始化参数
getInitParameterNames(); //获取在Servlet中所有初始化参数的名字,也就是key值,可以通过key值,来找到各个初始化参数的value值。注意返回的是枚举类型
注意:在上面我们所分析的源码过程中,我们就知道,其实可以不用先获得ServletConfig,然后在获取其各种参数,
可以直接使用其方法,比如上面我们用的ServletConfig().getServletName();可以直接写成getServletName();
而不用在先获取ServletConfig();了,原因就是在GenericServlet中,已经帮我们获取了这些数据,我们只需要直接拿就行。
2.ServletContext对象
getServletContext();
getServletConfig().getServletContext();
//这两种获取方式的区别就跟上面的解释一样,第一种是直接拿,
//在GenericServlet中已经帮我们用getServletConfig().getServletContext()拿到了ServletContext。
//我们只需要直接获取就行了,第二种就相当于我们自己在获取一遍,两种是一样的。
功能:tomcat为每个web项目都创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目中共享数据,管理web项目资源,为整个web配置公共信息。通俗点讲,就是一个web项目,就存在一个ServletContext实例,每个Servlet都可以访问到它。
1、web项目中共享数据,
setAttribute(String name, Object obj);// 在web项目范围内存放内容,以便让在web项目中所有的servlet读能访问到
getAttribute(String name);// 通过指定名称获得内容
removeAttribute(String name);// 通过指定名称移除内容
2、整个web项目的初始化参数,这个就是全局初始化参数,每个Servlet中都能获取到该初始化值
getInitPatameter(String name); //通过指定名称获取初始化值
getInitParameterNames(); //获得枚举类型
3、获取web项目资源
getServletContext().getRealPath("/WEB-INF/web.xml");//获取web项目下指定资源的路径
InputStream getResourceAsStream(java.lang.String path);//获取web项目下指定资源的内容,返回的是字节输入流
getResourcePaths(java.lang.String path);//指定路径下的所有内容。
3.request对象
request就是将请求文本封装而成的对象,所以通过request能获得请求文本中的所有内容,请求头、请求体、请求行 。
String getHeader(java.lang.String name);// 获得指定头内容String
long getDateHeader(java.lang.String name);// 获得指定头内容Date
int getIntHeader(java.lang.String name);// 获得指定头内容int
Enumeration getHeaders(java.lang.String name);// 获得指定名称所有内容
请求体的获取 – 请求参数的获取分两种,一种get请求,一种post请求
get请求参数:http://localhost:8080/MyServlet?username=123&password=123
post请求参数:<form method="post"><input type="text" name="username">
String request.getParameter(String);// 获得指定名称,一个请求参数值。
String[] request.getParameterValues(String);// 获得指定名称,所有请求参数值。例如:checkbox、select等
Map<String , String[]> request.getParameterMap();// 获得所有的请求参数
请求转发
request.getRequestDispatcher(String path).forward(request,response);
//path:转发后跳转的页面,这里不管用不用"/"开头,都是以web项目根开始,因为这是请求转发
//请求转发只局限与在同一个web项目下使用,所以这里一直都是从web项目根下开始的
特点:浏览器中url不会改变,也就是浏览器不知道服务器做了什么,是服务器帮我们跳转页面的,并且在转发后的页面,能够继续使用原先的request,因为是原先的request,所以request域中的属性都可以继续获取到。
4.response对象
response.setHeader(java.lang.String name, java.lang.String value) ;//设置指定的响应头,常用。
response.setHeader("Refresh",3);//设置每隔3秒就自动刷新一次,
重定向(页面跳转)
方式一:手动方案
response.setStatus(302); //状态码302就代表重定向
response.setHeader("location","http://www.baidu.com");
方式二:使用封装好的,通过response.sendRedirect("http://www.baidu.com");
特点:服务器告诉浏览器要跳转的页面,是浏览器主动去跳转的页面,浏览器的地址栏中url会变,是浏览器重新发起一个请求到另外一个页面,所以request是重新发起的,跟请求转发不一样,原先request域中的属性不能获取。
重定向没有任何局限,可以重定向web项目内的任何路径,也可以访问别的web项目中的路径,并且这里就用”/”区分开来。
如果使用了”/”开头,就说明我要重新开始定位了,不访问刚才的web项目,自己写项目名,可以访问服务器上的其他项目。
如果没有使用”/”开始,那么就知道是访问原来那个web项目下的servlet,就可以省略项目名了。
六. 实例代码
-->实现javax.servlet.Servlet接口 -->
-->继承javax.servlet.GenericServlet类 (适配器模式) -->
-->继承javax.servlet.http.HttpServlet类 (模板方法设计) -->
-->继承HttpServlet
最常用的就是继承HttpServlet,我们使用起来很方便
public class servletdemo extends HttpServlet{ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.write(""); out.print(""); //创建cookie, Cookie cookie = new Cookie("name", "Value"); //设置cookie的有效时间,单位是秒 cookie.setMaxAge(1); /*负数:cookie的数据存在浏览器缓存中 * 0:删除。路径要保持一致,否则可能删错人。 * 正数:缓存(持久化到磁盘上)的时间*/ //设置cookie的path cookie.setPath(request.getContextPath()); /*客户端在访问服务器另外资源时,根据访问的路径来决定是否带着Cookie到服务器 *当前访问的路径如果是以cookie的path开头的路径,浏览器就带。否则不带。 cookie.setMaxAge(0);相当于删除cookie*/ //把cookie信息写回到客户端 response.addCookie(cookie); //Cookie遍历 Cookie[] cookies = request.getCookies();//获取客户端的所有Cookie对象 for (int i = 0;cookies!=null && i < cookies.length; i++) { System.out.println(cookies[i].getName()); System.out.println(cookies[i].getValue()); System.out.println(cookies[i].getPath()); System.out.println(cookies[i].getMaxAge()); } //session request.getSession().setAttribute("",""); //得到session对象 HttpSession session = request.getSession(); //session操作 session.getAttribute(""); session.setAttribute("", ""); session.getId(); //请求转发 request.getRequestDispatcher("").forward(request, response); /*转发是服务器行为,重定向是客户端行为 1.转发在服务器端完成的;重定向是在客户端完成的 2.转发的速度快;重定向速度慢 3.转发的是同一次请求;重定向是两次不同请求 4.转发不会执行转发后的代码;重定向会执行重定向之后的代码 5.转发地址栏没有变化;重定向地址栏有变化 6.转发必须是在同一台服务器下完成;重定向可以在不同的服务器下完成 */ //请求重定向 response.setStatus(302); response.sendRedirect(""); //告知客户端不缓存 response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setDateHeader("expires", 0); System.out.println(request.getMethod());//获得请求方式 System.out.println(request.getRequestURL());//返回客户端发出请求时的完整URL System.out.println(request.getContextPath());//当前应用的虚拟目录 System.out.println(request.getRequestURI());//返回请求行中的资源名部分 System.out.println(request.getQueryString());//返回请求行中的参数部分 System.out.println(request.getHeaderNames());//得到所有头信息name //Properties //获取资源路径 //此处填写的路径一定要注意是WEB服务器部署后的路径 String path=this.getServletContext().getRealPath("/test.properties"); //创建Properties Properties pro =new Properties(); pro.load(new FileInputStream(path));//读入文件test.properties //通过调用GenericServlet的getServletContext方法得到ServletContext对象 ServletContext setServletContext=this.getServletContext(); //向ServletContext添加一个键值对 setServletContext.setAttribute("name", "value"); /*获取非表单数据,先set后get,可以remove void setAttribute(String name, Object value); Object getAttribute(String name); Void removeAttribute(String name); */ //服务器和客户端浏览器编码要统一,否则会乱码 // resp.setCharacterEncoding("UTF-8");//设置服务器编码 // resp.setHeader("content-type", "text/html;charset=UTF-8");//通过响应消息头设置客户端浏览器编码 // resp.setContentType("text/html;charset=UTF-8");//此行代码封装了上面两行代码 // PrintWriter out=resp.getWriter();//得到一个字符输出流 // out.write("Response文本信息");//向客户端响应文本内容 //获取编码 //第一种方式 // String encoding=config.getInitParameter("encoding"); // System.out.println("第一种方式"+encoding); // //第二种方式 // System.out.println("第二种方式"+this.getServletConfig().getInitParameter("encoding")); //第三种方式 // System.out.println("第三种方式"+super.getInitParameter("encoding")); } } public class DownloadFileDemo extends HttpServlet{ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //通过路径得到输出流 String path =this.getServletContext().getRealPath("/WEB-INF/classes/cn/servlet/Servlet.jpg"); FileInputStream file=new FileInputStream(path); //创建输出流 ServletOutputStream sos=resp.getOutputStream(); //得到文件名 String filename=path.substring(path.lastIndexOf("\")+1);//分割字符串,取最后一个'"\'"符号后面的1个字符 //设置文件名的编码 filename=URLEncoder.encode(filename,"UTF-8");//将编码改为UTF-8 //告知客户端下载文件 resp.setHeader("content-disposition", "attachment;filename="+filename); resp.setHeader("content-type","image/jpg"); //输出 int len=1; byte[] b=new byte[1024]; while((len=file.read(b))!=-1) { sos.write(b,0,len); } sos.close(); file.close(); } } public class ServletDemo implements Servlet{ //Servlet生命周期的方法 //在servlet第一次被访问时调用 //实例化 public ServletDemo(){ System.out.println("ServLetDemo执行了,在servlet实例化时调用"); } //Servlet生命周期的方法 //在servlet第一次被访问时调用 //初始化 public void init(ServletConfig arg0) throws ServletException { System.out.println("init执行了,在servlet第一次被访问时调用"); } //Servlet生命周期的方法 //每次访问时都会被调用 //服务 public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException { //System.out.println("hello servlet"); System.out.println("service执行了,每次访问时都会被调用"); } //Servlet生命周期的方法 //销毁 public void destroy() { System.out.println("destroy执行了,销毁"); } public ServletConfig getServletConfig() { System.out.println("getServletConfig"); return null; } public String getServletInfo() { System.out.println("getServletInfo"); return null; } }