zoukankan      html  css  js  c++  java
  • tomcat和servlet的关系

    一、什么是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;
            }
    }
  • 相关阅读:
    Spring 控制器层如何调用DAO层
    Spring 工程分层
    spring boot工程如何启用 热启动功能
    Spring 视图层如何显示验证消息提示
    Sping POJO中如何添加验证规则和验证消息提示
    Spirng 分层,增加数据访问对象层
    Spring A 标签链接使用
    Spring 控制器重定向
    课程详情页之后台
    课程详情页之前台
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/9373625.html
Copyright © 2011-2022 走看看