Javaweb基础之Servlet详解
Servlet是什么
servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它那五个方法,其中最主要的是两个生命周期方法 init()和destroy(),还有一个处理请求的service(),也就是说,所有实现servlet接口的类,或者说,所有想要处理网络请求的类,都需要思考问题:初始化时要做什么,销毁时要做什么,接受到请求时要做什么。servlet不会直接和客户端打交道!那请求怎么来到servlet呢?答案是servlet容器,比如我们最常用的tomcat。tomcat才是与客户端直接打交道的家伙,他监听了端口,请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,tomcat再把这个response返回给客户端。
IDEA配置开发环境
https://www.cnblogs.com/javabg/p/7976977.html
Servlet使用流程
- 创建普通的Java类,继承HttpServlet
- 重写service方法,在其中书写逻辑
- web.xml文件中配置servlet
Servlet生命周期
- 从第一次调用到服务器关闭
- 如果servlet在web.xml文件中配置了load-on-startup生命周期为从服务器开启到服务器关闭
- init方法是对servlet初始化的方法,在servlet第一次加载进入内存时执行
- destroy方法在servlet销毁时执行,也就是服务器关闭时
package com.bjsxt.servlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import java.io.IOException; public class MyServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("servlet初始化完成"); } @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { // 终端输出 System.out.println("first servlet"); // 浏览器返回 res.getWriter().write("hello"); } @Override public void destroy() { System.out.println("我被销毁了"); } }
web.xml
load-on-startup的数字表示加载顺序
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>HelloWorld</servlet-name> <servlet-class>com.bjsxt.servlet.MyServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>HelloWorld</servlet-name> <url-pattern>/HelloWorld</url-pattern> </servlet-mapping> </web-app>
Servlet的doGet和doPost方法
- Service方法:可以处理post和get请求,如果servlet中有Service方法,会优先调用service方法对请求进行处理
- doGet方法:处理get方式的请求
- doPost方法:处理post方式的请求
注意:如果覆写的service方法中调用父类的service方法,则service方法处理完后,会再次根据请求方式响应的doGet和doPost方法执行,如果没有复写doGet和doPost方法,会报405错误,所以一般不在service中调用父类的service方法去避免405错误,而是在service中判断。
public class MyServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("servlet初始化完成"); } @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { // 终端输出 System.out.println("first servlet"); // 浏览器返回 res.getWriter().write("hello"); super.service(req, res); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是get"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是post"); } @Override public void destroy() { System.out.println("我被销毁了"); } }
Servlet的Request对象
浏览器发起请求到服务器,会遵循http协议将请求数据发送给服务器。服务器每次接受一个请求,就创建一个Request对象专门的存储此次请求的数据,服务器在调用Servlet时会将创建的Request对象作为实参数传递给Servlet的方法,比如service
public class MyServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("servlet初始化完成"); } @Override public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { //获取请求头数据 //获取请求方式 String method = req.getMethod(); //获取请求url StringBuffer url = req.getRequestURL(); String uri = req.getRequestURI(); //获取请求协议 String h = req.getScheme(); //-------------------------- //获取请求行数据 // 获取指定请求行数据 String value = req.getHeader("User-Agent"); // 获取所有请求行的枚举 Enumeration e = req.getHeaderNames(); while (e.hasMoreElements()) { String name = (String)e.nextElement(); String value2 = req.getHeader(name); System.out.println(name+":"+value2); } //-------------------------- // 获取用户提交数据 get post都这个方法,没有为Null String name = req.getParameter("username");//不能获取多项数据checkbox String[] favs = req.getParameterValues("fav"); // 多项 if (favs != null) { for (String fav:favs) { System.out.println(fav); } } //获取所有请求数据的键的集合req.getParameterNames() } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是get"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是post"); } @Override public void destroy() { System.out.println("我被销毁了"); } }
Servlet的Response对象
响应数据到浏览器的一个对象,设置响应头,响应实体比较关键,先设置响应编码格式
public class MyServlet2 extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("MOUSE", "TWO BIRDS");//同键,会覆盖 resp.addHeader("key", "thinkpad");// 同键,不会覆盖,而是追加 resp.addHeader("key", "thinkpad2"); //设置响应状态码 resp.sendError(404,"not found"); //设置响应实体 resp.getWriter().write("hello"); //设置响应编码格式 // resp.setHeader("content-type", "text/html;charset=utf-8"); resp.setContentType("text/plain;charset=utf-8");//text/plain,text/xml } }
Servlet请求中文乱码解决
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=utf-8"); String uname = req.getParameter("uname"); // 浏览器默认编码iso8859-1 下面万能方式改变编码,但是每个都要转很麻烦 uname = new String(uname.getBytes("iso8859-1"), "utf-8"); }
第二种解决方式,
如果是POST请求,在service方法中req.setCharacterEncoding(“utf-8”),get不行,因为数据在url上
如果是GET请求,不仅要加上req.setCharacterEncoding(“utf-8”),还要配置
在tomcat服务器目录下的conf/server.xml文件,如下配置
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" useBodyEncodingForURI=true/>
Servlet的请求转发
在服务器接收到浏览器的请求后,仅仅使用一个Servlet进行请求处理,会造成不同的Servlet逻辑代码冗余,Servlet的职责不明确,解决使用请求转发,实现多个servlet联动操作处理请求。
@Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //执行了登录逻辑。。。。 //servletname可以相对路径,直接写servlet别名也可以 jsp也可以 req.getRequestDispatcher('servletName').forward(); }
Request对象的作用域
使用请求转发后,不同的servlet之间要怎么共享数据呢?或者说数据(请求数据+其他数据)怎么从一个servlet流向另外一个servlet呢?
解决:使用Request对象的作用域,
req.setAttribute(object name, object value); req.getAttribute(object obj);
解决了一次请求中不同servlet数据共享的问题,注意,使用Request对象进行数据转,数据只在一次请求内有效。
Servlet的重定向
使用请求转发会造成表单数据重复提交的问题,这时就要使用重定向来处理,不仅如此,还可以让其他servlet来处理当前servlet处理不了的问题。
两次请求,第二次302跳转重定向,
resp.sendRedirect("servlet_name");// /project/servlet_name
Servlet的Cookie使用
设置cookie
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置请求编码格式 req.setCharacterEncoding("utf-8"); //设置响应编码格式 resp.setContentType("text/html;charset=utf-8"); // 创建cookie对象 Cookie c1 = new Cookie("cookie_name", "cookie_value"); Cookie c2 = new Cookie("cookie_name2", "cookie_value2"); // 注意,一个cookie对象存储一条数据,多条数据可以创建多个cookie对象来存储 // 响应cookie信息到客户端,临时存储,浏览器关闭就没了,浏览器内存中 resp.addCookie(c1); // 定时存储客户端硬盘,秒为单位,每次请求都会携带,除非设置有效路径,指定路径才会携带 c2.setMaxAge(3*24*3600);//设置有效路径 c2.setPath("/cookie/abc") resp.addCookie(c2); }
获取cookie
Cookie[] cks = req.getCookies(); if (cks != null) { for (Cookie c:cks) { String name = c.getName(); String value = c.getValue(); System.out.println(name+":"+value); } }
Servlet的Session使用
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置请求编码格式 req.setCharacterEncoding("utf-8"); //设置响应编码格式 resp.setContentType("text/html;charset=utf-8"); String name = "张三"; //存在则获取,否则创建,失效也会重新创建,一句搞定 //JSESSIONID存储在Cookie的临时存储空间,浏览器关闭会失效,默认存储时间30分,可以修改tomcat的web.xml的session_config配置 HttpSession hs = req.getSession(); hs.setMaxInactiveInterval(3600);//session时间 单位秒,一直用,会重新计时 System.out.println(hs.getId()); //session强制失效退出 //hs.invalidate(); // SESSION存储数据,共享 hs.setAttribute("name", name); //其他请求servlet获取,返回的为object对象,解决了不同请求数据共享的为题,只要sessionid不失效 hs.getAttribute("name"); //session失效处理 //将用户请求重的sessionid和后台获取到的session对象的sessionid对比, }
Servlet的ServletContext对象
Request对象解决了一次请求内的数据共享问题,session解决了用户不同请求数据共享的问题,不同的用户数据共享该怎么办呢?
ServletContext对象原理:
ServletContext对象由服务器创建,一个项目只有一个对象,不管在项目的任意位置进行获取得到的都是同一个对象,那么不同用户发起的请求获取的也是同一个对象了。
数据共享
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 获取ServletContext对象的三种方式,获取到的都是同一个对象 ServletContext sc1 = this.getServletContext(); // 常用 ServletContext sc2 = req.getSession().getServletContext(); //常用 ServletContext sc3 = this.getServletConfig().getServletContext(); System.out.println(sc1 == sc2); System.out.println(sc1 == sc3); //设置要共享的数据 sc1.setAttribute("str", "共享的数据"); // 其他servlet中获取,返回Object类型,不存在为null 先获取对象 sc2.getAttribute("str"); }
获取项目web.xml的全局配置数据
<!--配置全局数据,一个context-param只能配置一个键值对> <context-param> <param-name>name</param-name> <param-value>zhangsan</param-value> </context-param>
sc.getInitParameter("name");//不存在返回Null,返回String类型 sc.getInitParamterNames();//返回键名的枚举
作用:将静态数据和代码解耦
获取webroot下的资源的绝对路径
String path = sc.getRealPath("/doc/1.txt");
获取的路径为项目的根目录,path参数为项目根目录中的路径。
获取webroot下的资源的流对象
InputStream is = sc.getResourceAsStream(String path);
注意,此种方式只能获取项目根目录下的资源流对象,class文件的流对象需要使用类加载器
使用ServletContext对象完成网页计数器
思路:在用户登录校验中创建计数器并自增,然后存储到ServletContext对象中,在主页面里取出计数器数据并显示给用户
Servlet的ServletConfig对象
使用ServletContext对象可以获取web.xml中的全局配置文件,在web.xml中,每个Servlet也可以单独的配置,就需要用ServletConfig对象来获取这些单独的配置信息了。
作用:ServletConfig对象是Servlet的专属配置对象,每个Servlet都单独拥有一个ServletConfig对象,用来获取web.xml中的单独配置信息。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置全局数据,一个context-param只能配置一个键值对>--> <context-param> <param-name>name</param-name> <param-value>zhangsan</param-value> </context-param> <servlet> <servlet-name>HelloWorld</servlet-name> <servlet-class>com.bjsxt.servlet.MyServlet</servlet-class> <load-on-startup>1</load-on-startup> <!-- 单独的servlet配置--> <init-param> <param-name>config</param-name> <param-value>utf-8</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloWorld</servlet-name> <url-pattern>/HelloWorld</url-pattern> </servlet-mapping> </web-app>
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取ServletConfig对象 ServletConfig sc = this.getServletConfig(); //获取web.xml中单独的配置 String code = sc.getInitParameter("config"); }
web.xml文件使用总结
存储项目相关的配置信息,保护servlet,解耦一些数据对程序的依赖。
web项目下面的web.xml文件为局部配置,针对本项目的位置
tomcat下的web.xml文件为全局配置,配置公共信息。
核心组件:
- 全局上下文配置
- Servlet配置
- 过滤器配置
- 监听器配置
配置顺序无先后,服务器启动的加载有顺序
web容器会按照ServletContext->context-param->listener->filter->servlet这个顺序来加载组件
发表评论