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;
            }
    }
  • 相关阅读:
    ZOJ Problem Set–2417 Lowest Bit
    ZOJ Problem Set–1402 Magnificent Meatballs
    ZOJ Problem Set–1292 Integer Inquiry
    ZOJ Problem Set–1109 Language of FatMouse
    ZOJ Problem Set–1295 Reverse Text
    ZOJ Problem Set–1712 Skew Binary
    ZOJ Problem Set–1151 Word Reversal
    ZOJ Problem Set–1494 Climbing Worm
    ZOJ Problem Set–1251 Box of Bricks
    ZOJ Problem Set–1205 Martian Addition
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/9373625.html
Copyright © 2011-2022 走看看