JavaWeb Servlet

作者: dawsonenjoy | 来源:发表于2018-12-28 11:45 被阅读0次

    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接口,即可序列化,此时在重启前服务器会自动将数据进行序列化保存,从而避免了数据丢失的问题

    相关文章

      网友评论

        本文标题:JavaWeb Servlet

        本文链接:https://www.haomeiwen.com/subject/vsrslqtx.html