Servlet总结

Servlet

Servlet是Java规范(可以理解成接口),定义了Java类被浏览器访问到(也就是被tomcat识别到)的规则

例子

1.建一个JavaEE WEB项目
2.

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

3.web.xml配置

<!--配置servlet-->
<servlet>
  <servlet-name>demo1</servlet-name>
  <servlet-class>cn.example.servlet.ServletDemo1</servlet-class>
</servlet>
<!--资源路径-->
<servlet-mapping>
  <servlet-name>demo1</servlet-name>
  <url-pattern>/demo1</url-pattern>
</servlet-mapping>

4

public class ServletDemo1 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init-demo1");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("hello");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

5 配置tomcat
6 浏览器方法

浏览器访问http://localhost:8888/demo1就能自动执行到ServletDemo1中的service方法的原理是:
请求先通过这个xml,找到url-pattern为demo1的资源名称,即demo1,然后去找哪个servlet的name是demo1,然后取对应的class,即cn.example.servlet.ServletDemo1。tomcat将这个全类名对应的字节码文件加载进内存(class.forName()),创建对象(cls.newInstance()),调用service方法,因为我们实现了Servlet接口,所以tomcat就能知道这个类中肯定有service方法(这里就能理解为什么说Servlet是规则)。
所以Servlet的运行需要依赖web容器,也就是tomcat,去完成对象加载、创建、方法调用过程。

Servlet执行原理:

  1. 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,找对应的<url-pattern>标签
  3. 然后找<servlet-class>中配置的全限定类名
  4. tomcat对这个类进行加载、创建对象、调用方法

Servlet生命周期

public class ServletDemo2 implements Servlet {
    /**
     * 初始化方法,Servlet创建的时候执行
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init-demo2");
    }

    /**
     * 获取Servlet配置对象
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 提供服务的方法
     */
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("hello demo2");
    }

    /**
     * 获取Servlet信息,版本、作者等
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 在服务器正常关闭时,执行一次
     */
    @Override
    public void destroy() {
		System.out.println("destroy");
    }
}

Servlet生命周期:

  • 创建:执行init方法,只执行一次
  • 提供服务:执行service方法,执行多次
  • 销毁:执行destroy方法,只执行一次

Servlet被创建时机:

  • 第一次被访问的时候
  • 自定义配置:
<servlet>
  <servlet-name>demo2</servlet-name>
  <servlet-class>cn.example.servlet.ServletDemo2</servlet-class>
  <!--负数:表示第一次访问时Servlet被创建;正数或0:服务器启动的时候Servlet被创建-->
  <load-on-startup>1</load-on-startup>
</servlet>

Servlet是单例的,所以最好不用在Servlet类中定义成员变量,否则导致多线程安全问题

Servlet注解配置

Servlet3.0+支持注解配置,直接用@WebServlet注解

//@WebServlet(urlPatterns="/demo3")
//@WebServlet("/demo3")
//@WebServlet({"/d4","/dd4","/ddd4"})
//@WebServlet("/user/demo4")
//@WebServlet("/user/*")
//@WebServlet("/*") 通配符
@WebServlet("*.do")
public class ServletDemo3 implements Servlet {
//...
}

Servlet继承体系

public interface Servlet {}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
	// 只对service()方法声明为抽象的,对其他Servlet中的方法做了默认实现
}

HttpServlet 是对HTTP协议的封装,在service方法中判断请求方式,然后分派到不同的方法中,所以我们使用的时候只需要重写相关的doGet()、doPost()等方法即可

public abstract class HttpServlet extends GenericServlet {
	//...
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    }
}

servletRequest和servletResponse

servletRequest和servletResponse是service()中的两个参数,加上这两个参数,在细说下Servlet执行原理:

  1. 当服务器接收到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
  2. 查找web.xml文件,找对应的<url-pattern>标签
  3. 然后找<servlet-class>中配置的全限定类名
  4. tomcat对这个类进行加载、创建对象
  5. tomcat会创建request和response对象,request对象中封装请求消息数据,response用来设置响应消息
  6. tomcat将request和response两个对象传递给service方法,并且调用service方法
  7. 我们在可以通过request对象获取请求消息数据,然后通过response对象设置响应消息数据
  8. 服务器在给浏览器做出响应之前,会从response对象中拿我们设置的响应消息数据
servletRequest对象的方法

获取请求行数据

GET /day14/demo1?name=zhangsan HTTP/1.1

  1. 获取请求方式 :GET
    String getMethod()
  2. 获取虚拟目录:/day14
    String getContextPath()
  3. 获取Servlet路径: /demo1
    String getServletPath()
  4. 获取get方式请求参数:name=zhangsan
    String getQueryString()
  5. 获取请求URI:/day14/demo1
    String getRequestURI(): /day14/demo1
    StringBuffer getRequestURL() :http://localhost/day14/demo1
  6. 获取协议及版本:HTTP/1.1
    String getProtocol()
  7. 获取客户机的IP地址:
    String getRemoteAddr()

获取请求头数据

  1. String getHeader(String name):通过请求头的名称获取请求头的值
  2. Enumeration< String > getHeaderNames():获取所有的请求头名称

获取请求体数据,先要获取流对象,再从流对象中拿数据。获取流对象的方法如下两种(用在post请求方式中):

  1. BufferedReader getReader():获取字符输入流,只能操作字符数据
  2. ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据(比如上传的文件)

其他功能
(1)获取请求参数的通用方法(get和post方式都能使用)

  • String getParameter(String name):根据参数key获取value,形如username=z&password=1
  • String[] getParameterValues(String name):根据key获取参数数组 ,形如复选框hobby=a&hobby=b
  • Enumeration< String > getParameterNames():获取所有参数key-value
  • Map<String, String[]> getParameterMap():获取所有参数的map集合

Enumeration< String > getParameterNames() 用法举例

<form action="/day14/requestDemo7" method="post">
    <input type="text" placeholder="请输入用户名" name="username"><br>
    <input type="text" placeholder="请输入密码" name="password"><br>

    <input type="checkbox" name="hobby" value="game">游戏
    <input type="checkbox" name="hobby" value="study">学习
    <br>

    <input type="submit" value="注册">
</form>
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()){
    String name = parameterNames.nextElement();
    System.out.println(name);
    String value = request.getParameter(name);
    System.out.println(value);
    System.out.println("----------------");
}

注意:对于复选框,用getParameter()方法只能获取到一个值
在这里插入图片描述

Map<String, String[]> getParameterMap()用法举例

Map<String, String[]> parameterMap = request.getParameterMap();
//遍历
Set<String> keyset = parameterMap.keySet();
for (String name : keyset) {
    //获取键获取值
    String[] values = parameterMap.get(name);
    System.out.println(name);
    for (String value : values) {
        System.out.println(value);
    }

    System.out.println("-----------------");
}

可以拿到复选框的所有值
在这里插入图片描述
(2)解决post方式中文请求参数乱码问题
对于get方法的请求,tomcat8已经解决了中文请求参数乱码问题
post方式的解决办法是通过request对象设置流的编码:request.setCharacterEncoding("utf-8");

(3)请求转发 forward
请求转发是一种在服务器内部的资源跳转方式,步骤是通过request对象获取请求转发器对象,然后执行forward()方法转发:

RequestDispatcher requestDispatcher = request.getRequestDispatcher("/requestDemo9");  
requestDispatcher.forward(request,response);
//合成一个链式调用
request.getRequestDispatcher("/requestDemo9").forward(request,response);

特点:

  • 浏览器显示地址栏地址不变
  • 转发只能访问当前服务器下的资源
  • 转发是一次请求,所以可以使用request对象来共享数据
//通过request对象设置域对象,域的作用范围就是同一次请求中,用于请求转发共享数据
setAttribute(String name, Object obj) // 存储数据到域中
getAttribute(String name) // 获取共享数据
removeAttribute(String name) // 移除数据
servletResponse对象的方法
  1. 设置状态码:setStatus(int sc)
  2. 设置响应头:setHeader(String name, String value)
  3. 设置响应体,先要获取输出流:
    字符输出流:PrintWriter getWriter()
    字节输出流:ServletOutputStream getOutputStream()

设置响应体的例子

public class ResponseDemo4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //解决乱码方式一:获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
        //缺点:限制了浏览器只能用GBK编码
        // response.setCharacterEncoding("utf-8");

        //解决乱码方式二:告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
        //通过设置响应头content-type,告诉浏览器此次编码方式,并且这种方式还指定了获取流的编码方式
        //response.setHeader("content-type","text/html;charset=utf-8");

        //解决乱码方式三:下面这种写法是方式二的简化写法
        response.setContentType("text/html;charset=utf-8");

        //1.获取字符输出流
        PrintWriter pw = response.getWriter();
        //2.输出数据
        pw.write("<h1>hello response</h1>"); //英文可以正常显示
        pw.write("你好"); //中文会乱码,response是tomcat返回过来的,流对象也是从tomcat中获取到的,它里边设置的字符集和浏览器默认字符集编码不一样导致了乱码

		//1.获取字节输出流
        ServletOutputStream sos = response.getOutputStream();
        //2.输出数据
        sos.write("字节流一般用来输出图片等类型的数据".getBytes("utf-8"));
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}

4. 重定向 redirect
特点:

  • 地址栏地址发生变化
  • 重定向可以定向到其他站点(跨域)
  • 重定向是两次请求,不能使用request对象来共享数据

重定向是将新的URL发送给浏览器,浏览器再去请求这个URL:

方式一:
//1.设置状态码为302
response.setStatus(302);
//2.设置响应头location,day15是虚拟目录
response.setHeader("location","/day15/responseDemo2");

方式二:
response.sendRedirect("/day15/responseDemo2");
相对路径和绝对路径

相对路径:
找到当前资源和目标资源之间的相对位置关系,比如当前资源http://localhost/day15/html/responseDemo2,目标资源http://localhost/day15/responseDemo1,则用相对路径href="../responseDemo1"。day15是虚拟目录。

绝对路径:
判断定义的路径是给谁用的,给客户端浏览器使用,如重定向,则需要加虚拟目录,建议虚拟目录动态获取:request.getContextPath();给服务器使用,不需要加虚拟目录,如在服务器端转发:request.getRequestDispatcher("/XX[不需要虚拟目录]").forward(request, response);

ServletContext

ServletContext代表整个web应用,可以和程序的容器(服务器)来通信
获取方式:

  • 通过request对象获取:request.getServletContext();
  • 通过HttpServlet获取:this.getServletContext();

功能:

  1. 获取MIME类型
    MIME类型是在互联网通信过程中定义的一种文件数据类型,格式:大类型/小类型 text/html 、image/jpeg
    方法:String getMimeType(String file)
  2. 获取文件的真实(服务器)路径
    方法:String getRealPath(String path)
    String b = context.getRealPath("/b.txt");//web目录下资源访问
    String c = context.getRealPath("/WEB-INF/c.txt");//WEB-INF目录下的资源访问
文件下载案例
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取请求参数,文件名称
        String filename = request.getParameter("filename");
        //2.使用字节输入流加载文件进内存
        //2.1找到文件服务器路径
        ServletContext servletContext = this.getServletContext();
        String realPath = servletContext.getRealPath("/img/" + filename);
        //2.2用字节流关联
        FileInputStream fis = new FileInputStream(realPath);

        //3.设置response的响应头
        //3.1设置响应头类型:content-type
        String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
        response.setHeader("content-type",mimeType);
        //3.2设置响应头打开方式:content-disposition

        //解决中文文件名问题
        //1.获取user-agent请求头、
        String agent = request.getHeader("user-agent");
        //2.使用工具类方法编码文件名即可
        filename = DownLoadUtils.getFileName(agent, filename);

        response.setHeader("content-disposition","attachment;filename="+filename);
        //4.将输入流的数据写出到输出流中
        ServletOutputStream sos = response.getOutputStream();
        byte[] buff = new byte[1024 * 8];
        int len = 0;
        while((len = fis.read(buff)) != -1){
            sos.write(buff,0,len);
        }

        fis.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request,response);
    }
}
public class DownLoadUtils {
    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}

Session和Cookie

Cookie

设置Cookie:

 //1.创建Cookie对象
 Cookie c = new Cookie("msg","hello");
 //2.发送Cookie
 response.addCookie(c);

获取Cookie:

//3. 获取Cookie
 Cookie[] cs = request.getCookies();
 //获取数据,遍历Cookies
 if(cs != null){
     for (Cookie c : cs) {
         String name = c.getName();
         String value = c.getValue();
         System.out.println(name+":"+value);
     }
 }

一次可以发送多个Cookie:

Cookie c1 = new Cookie("msg","hello");
Cookie c2 = new Cookie("name","zhangsan");
//2.发送Cookie
response.addCookie(c1);
response.addCookie(c2);

Cookie在浏览器中保存的时间:

  • 默认是,当浏览器关了,Cookie就没了
  • 设置Cookie的生存时间
//1.创建Cookie对象
Cookie c1 = new Cookie("msg","hello");
//2.设置cookie的存活时间
c1.setMaxAge(30);//正数:将cookie持久化到硬盘,30秒后会自动删除cookie文件
//c1.setMaxAge(-1);//负数:默认值
//c1.setMaxAge(0);//删除cookie信息

Cookie存储中文

  • tomcat8之前不能存中文,一般采用URL编码对中文进行转码
  • tomcat之后可以

Cookie共享问题:

  • 在一个tomcat服务器中部署了多个web项目,cookie在这些项目下默认是不同享的
  • 设置成cookie.setPath("/");表示同一tomcat下的项目共享cookie
  • cookie.setDomain(String path) 如果设置一级域名相同,那么多个服务器之间cookie可以共享,如setDomain(".baidu.com"),那么tieba.baidu.com和zhidao.baidu.com共享cookie
Session

Session只能在一次会话的多次请求间共享数据,在一次会话范围内,多次获取Session是同一个对象。

设置Session:

 //1.获取session
 HttpSession session = request.getSession();
 //2.存储数据
 session.setAttribute("msg","hello session");

获取Session:

//1.获取session
HttpSession session = request.getSession();
//2.获取数据
Object msg = session.getAttribute("msg");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值