zoukankan      html  css  js  c++  java
  • Servlet编程

    Servlet编程

    1. servlet概念及相关接口简介

    java Servlet是运行在web服务器或应用服务器上的程序,他是作为来自web浏览器或其他HTTP客户端的请求和HTTP服务器山的数据库或应用程序之间的中间层
    什么是Servlet
    是一个java类,继承自HttpServlet类
    这个类在服务器端运行,用以处理客户端的请求
    主要作用
    1.读取客户端(浏览器)发送来的显式数据和隐式的HTTP请求数据,包括html文件,cookies,HTTP请求等信息
    2.处理数据被返回生成结果。
    3.发送显式数据和隐式HTTP响应到客户端

    2.开发一个Servlet流程

    步骤:
    1.编写java类,继承自HttpServlet类
    2.重写doGet和doPost方法
    3.Servlet程序交给tomcat服务器运行
    3.1.servlet程序的class码拷贝到WEB-INF/calsses目录,如果是IDE,会自动拷贝
    3.2.在web.xml文件中进行配置
    3.3.配置的另一种方法是在servlet程序开头写上这样的语句,表示访问路径@WebServlet("/first")
    先看servlet的程序

    /**
     * 运行在tomcat服务器端的servlet类
     */
    @WebServlet(name = "FirstServlet")
    public class FirstServlet extends HttpServlet {
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //向浏览器输出内容
            response.getWriter().write("This is first servlet!");
            //向控制台打印信息
            System.out.println("helloWorld");
        }
    }
    

    servlet程序写完之后还需要在WEB-INF目录下配置web.xml
    配置如下:

        <!--配置一个servelt-->
        <!--servlet的配置-->
        <servlet>
            <!--servlet的内部名称,名字可以自定义,但是最好有意义-->
            <servlet-name>FirstServlet</servlet-name>
            <!--servlet的类全名:包名+简单类名-->
            <servlet-class>servers.FirstServlet</servlet-class>
        </servlet>
    
        <!--servlet的映射配置-->
        <servlet-mapping>
            <!--servlet的内部名称,一定要和上面的内部名称保持一致-->
            <servlet-name>FirstServlet</servlet-name>
            <!--servlet的映射路径(访问servlet的名称)-->
            <url-pattern>/first</url-pattern>
        </servlet-mapping>
    

    3.servlet路径映射

    当我们访问 http://localhost:8080/sayhello/first路径时,是如何调用servlet呢?
    xml路径开始之前:
    tomcat服务器启动时,首先加载webapps中的每一个web应用的web.xml配置文件
    http:// :http协议
    localhost : 在本地的hosts文件中查找是否存在该域名对应的IP地址
    8080 : 找到tomcat服务器
    /sayhello : 在tomcat的webapps目录下找到sayhello目录
    /first 资源名称
    访问到/first时开始执行:
    1.在sayhello的web.xml中查找是否有匹配的url-pattern的内容(/first)
    2.如果找到匹配的url-pattern,则使用当前的servlet-name的名称到web.xml文件中查询是否有相同的servlet配置
    3.如果找到,则取出对应的servlet配置信息中的servlet-calss内容
    4.通过反射:
    4.1.构造FirstServlet的对象
    4.2.然后调用FirstServlet里面的方法

    Servlet的映射路径
    路径有两种精准匹配和模糊匹配两种方式
    精确匹配:

    /first 访问 http://localhost:8080/sayhello/first

    模糊匹配:

    1. /* http://localhost:8080/sayhello/任意路径
      2./itcast/* http://localhost:8080/day10/sayhello/任意路径
      3.*.后缀名 例:×.do http://localhost:8080/sayhello/任意路径.do

    注意:
    1.url-pattern要么以 / 开头,要么以开头。 例如, itcast是非法路径。
    2.不能同时使用两种模糊匹配,例如 /itcast/
    .do是非法路径
    3.当有输入的URL有多个servlet同时被匹配的情况下:
    3.1 精确匹配优先。(长的最像优先被匹配)
    3.2 以后缀名结尾的模糊url-pattern优先级最低!!!

    4.缺省servlet

    servlet的缺省路径(/)是在tomcat服务器内置的一个路径,该路径对应的是一个DefaultServlet(缺省Servlet)。这个缺省的Servlet的所用是用于解析web应用的静态资源文件

    案例:URL输入http://localhost:8080/sayhello/index.html 如何读取文件????
    1.首先到sayhello应用下的web.xml文件查找是否有匹配的url-pattern
    2.如果没有匹配的url-pattern,则交给tomcat的内置DefaultServlet处理
    3.DefaultServlet程序到sayhello应用的根目录下查找是否存在一个名称为index.html的静态文件
    4.如果找到静态文件,则读取该文件内容,返回给浏览器
    5.如果找不到该文件,则返回404错误页面
    结论:先找动态资源,再找静态资源,不要轻易在web.xml使用/*,如果要使用,推荐加上后缀

    5.servlet生命周期(重点)

    5.1.引入

    在之前接触的类中,可以使用new方法来创建一个对象,可以调用对象的方法,可以使对象等于null来消除对象。但是在servlet这个类中,我们只是重写了doGet等方法,并没有自己实现这个类的对象创建,初始化,调用,消除。
    那么servlet这个类的生命周期是怎么样的呢?创建,初始化,调用,消除又是怎么实现的呢?

    5.2.Servlet重要的四个生命周期方法

    构造方法:创建servlet对象的时候调用。默认情况下,第一次访问servlet的时候创建servlet对象。只调用1次,证明servlet对象在tomcat是单实例的
    init方法:创建完servlet对象的时候调用。只调用1次
    service方法:每次发出请求时调用。请求几次调用几次
    destory方法:销毁servlet对象的时候调用。停止服务器或者重新部署web应用时销毁servlet对象,只调用1次

    代码示例:

    /**
     * 测试servlet的生命周期的四个重要方法
     */
    @WebServlet(name = "LifeDemo")
    public class LifeDemo extends HttpServlet {
        //1.构造方法
        public LifeDemo(){
            System.out.println("1.LifeDemo被创建了");
        }
        
        //2.inti方法
        @Override
        public void init(ServletConfig config) throws ServletException {
            System.out.println("2.init方法被调用");
        }
        
        //3.service方法
        @Override
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            System.out.println("3.service方法被调用");
        }
        
        //4.destory方法
        @Override
        public void destroy() {
            System.out.println("4.servlet对象销毁");
        }
    }
    

    5.3.伪代码演示servlet的生命周期

    servlet是通过反射来调用的,这里使用伪代码模拟过程
    tomcat内部代码运行
    1.通过映射找到servlet-class的内容(字符串:servers.FirstServlet)
    2.通过反射构造FirstServlet对象
    2.1.得到字节码对象
    Class clazz = class.forName("servers.FirstServlet");
    2.2.调用无参的构造方法来构造对象
    Object obj = clazz.newInstance(); ---1.servlet的构造方法被调用
    3.创建ServletConfig对象,通过反射调用init方法
    3.1.得到方法对象
    Method m = clazz.getDeclareMethod("init",ServletConfig.class);
    3.2.调用方法
    m.invoke(obj,config); --2.servlet的init方法被调用
    4.创建request,response对象,通过反射调用service方法
    4.1 得到方法对象
    Methodm m =clazz.getDeclareMethod("service",HttpServletRequest.class,HttpServletResponse.class);
    4.2 调用方法
    m.invoke(obj,request,response); --3.servlet的service方法被调用
    5.当tomcat服务器停止或web应用重新部署,通过反射调用destroy方法
    5.1 得到方法对象
    Method m = clazz.getDeclareMethod("destroy",null);
    5.2 调用方法
    m.invoke(obj,null); --4.servlet的destroy方法被调用

    5.4.用时序图来演示servlet的生命周期

    5.5.Servlet的自动加载

    默认情况下,第一次访问servlet的时候创建servlet对象。如果servlet的构造方法或init方法中执行了比较多的逻辑代码,那么导致用户第一次访问sevrlet的时候比较慢。为了解决这个问题,我们可以设置在web应用第一次被访问的时候,就执行servlet的构造方法和init方法
    改变servlet创建对象的时机: 提前到加载web应用的时候!!!
    方法:
    在servlet的配置信息中,加上一个即可!!

    <servlet>
        <servlet-name>LifeDemo</servlet-name>
        <servlet-class>gz.itcast.c_life.LifeDemo</servlet-class>
        <!-- 让servlet对象自动加载 -->
        <load-on-startup>1</load-on-startup> 
      </servlet>
    

    注意: 整数值越大,创建优先级越低!!

    6.有参的init方法和无参的init方法

    有参的init方法是tomcat的生命周期方法。
    无参的init方法使用提供给开发者重写该方法的地方
    不要覆盖有参的方法,只能覆盖无参的方法

    /**
     * init有参和无参方法的区别
     */
    @WebServlet(name = "InitDemo")
    public class InitDemo extends HttpServlet {
        /**
         * 有参数的init方法
         * 该方法是servlet的生命周期方法,一定会被tomcat服务器调用
         * 这个init方法不能被覆盖
         * @param config
         * @throws ServletException
         */
        @Override
        public void init(ServletConfig config) throws ServletException {
            System.out.println("有参的init方法");
        }
        
        /**
         * 无参数的init方法
         * 该方法是servlet的编写初始化代码的方法
         * 是sun公司设计出来专门给开发这进行覆盖的,然后在里面编写servlet的出事逻辑代码方法
         * @throws ServletException
         */
        @Override
        public void init() throws ServletException {
            System.out.println("无参的init方法");
        }
    }
    

    7.Servlet线程安全

    从上面的示例可以知道,Servlet是单实例,但是servlet又能被多客户端访问,所以又是一个多线程的。所以servlet是一个单实例多线程的

    如果多线程同时访问了servlet对象的共享数据(成员变量)可能会引发线程安全问题
    解决方法
    1.把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)
    2.建议在servlet类中尽量不要使用成员变量,如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围(建议哪里使用成员变量,就同步哪里!!),以避免因为同步导致并发效率降低

    /**
     * servlet的多线程并发问题
     */
    @WebServlet("/thread")
    public class ThreadDemo extends HttpServlet {
    
        int count=1;
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html;charset=utf-8");
    
            //多线程锁
            synchronized (ThreadDemo.class) {//锁对象必须唯一,建议使用字节码
                resp.getWriter().write("你现在是当前网站的第" + count + "个访客");
    
                count++;
            }
        }
    }
    

    Servlet中的对象
    HttpServletRequest 请求对象:获取请求信息
    HttpServletResponse 响应对象: 设置响应对象
    ServletConfig对象 servlet配置对象
    ServletContext对象; servlet的上下文对象

    8.servletConfig对象

    8.1.作用

    servletConfig对象:主要是用于加载servlet的初始化对象

    8.2.对象创建和得到

    创建时机:在创建完servlet对象之后,在调用init方法之前
    得到对象:直接从有参数的init方法得到

    8.3.servlet的初始化参数配置

    ServletConfig的API:
    java.lang.String getInitParameter(java.lang.String name) 根据参数名获取参数值
    java.util.Enumeration getInitParameterNames() 获取所有参数
    ServletContext getServletContext() 得到servlet上下文对象
    java.lang.String getServletName() 得到servlet的名称

    配置文件:

        <servlet>
            <servlet-name>ServletConfigDemo</servlet-name>
            <servlet-class>servers.ServletConfigDemo</servlet-class>
            <!-- 初始参数: 这些参数会在加载web应用的时候,封装到ServletConfig对象中 -->
            <init-param>
                <param-name>name</param-name>
                <param-value>rocco</param-value>
            </init-param>
        </servlet>
    

    ServletConfig的代码

    /**
     * 测试ServletConfig对象
     */
    @WebServlet(name = "ServletConfigDemo")
    public class ServletConfigDemo extends HttpServlet {
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //读取servlet的初始参数
            String name = this.getServletConfig().getInitParameter("name");
    
            response.getWriter().write("hello");
            System.out.println(name);
    
        }
    }
    

    9. ServletContext对象

    9.1.引入

    ServletContext对象,叫做Servlet的上下文对象。表示一个当前的web应用环境,一个web应用中只有一个ServletContext对象

    9.2.对象创建和得到

    创建时间:加载web应用时创建该对象
    得到对象:从ServletConfig的getServletContext方法得到

    9.3.ServletContext对象的核心API

    得到ServletContext对象:
    ServletContext context = this.getServletContext();

    得到web应用路径
    String getContextPath(); 得到当前web的应用路径

    得到web应用的初始化参数(全局)
    config参数是当前servlet的参数,只能由当前servlet对象获得
    Context参数是全局参数,通过ServletContext对象,所有的都可以获得
    全部参数的写法:

        <!--配置web应用参数-->
        <context-param>
            <param-name>AAA</param-name>
            <param-value>AAA's value</param-value>
        </context-param>
        <context-param>
            <param-name>BBB</param-name>
            <param-value>BBB's value</param-value>
        </context-param>
        <context-param>
            <param-name>CCC</param-name>
            <param-value>CCC's value</param-value>
        </context-param>
    
    

    String getInitParameter(String name); 得到web应用指定的初始化参数
    Enumeration geetInitParameterNames();得到web应用所有的初始化参数

    域对象有关的方法
    域对象:作用是用于保存数据,获取数据。可以在不同的动态资源之间共享数据
    servlet是运行在服务器上的程序,如何在不同的servlet的程序之间传递参数的呢?常规做法是通过重定向后面附带参数来传递,例如:response.sendRedirect("/Servlet2?name=eric") 但是这样有一个问题,就是只能传递字符串的参数,且参数被暴露

    最好的解决方案是使用域对象,域对象可以共享任何类型的数据
    ServletContext其实就是一个域对象
    ServletContext域对象:作用范围在整个web应用中有效!!!

    域对象的一些操作
    void setAttribute(java.lang.String name, java.lang.Object object) --保存数据
    Object getAttribute(java.lang.String name) --获得数据
    void removeAttribute(java.lang.String name) --删除数据

    所有域对象:
    HttpServletRequet域对象
    ServletContext域对象
    HttpSession 域对象
    PageContext域对象

    转发
    转发和重定向的效果是一样的

    RequestDispatcher getRequestDispatcher(java.lang.String path) --转发(类似于重定向)

    转发和重定向的区别
    1.转发:

    • a.地址栏不会改变
    • b.转发只能转发到当前web应用内的资源
    • c.在转发过程中,可以把数据保存到request域对象中

    2.重定向

    • a.地址栏会改变,变成重定向到的新地址
    • b.重定向可以跳转到当前web应用,或其他web应用,甚至是外部域名网站
    • c.不能再重定向的过程中,把数据保存到request中

    结论:如果要使用request域对象进行数据共享,只能使用转发技术

    其他一些方法
    通常在一个java路径中,我们用 . 表示项目文件所在的地址,但是在java web项目中,点 . 表示tomcat下bin里的startup.sh所在的目录。所以在java web中不能使用点 . 来查找文件
    但是可以使用 “/” 来查找, / 表示WEB-INF所在的目录地址,通过这个找到资源文件

    java.lang.String getRealPath(java.lang.String path) --得到web应用的资源文件
    java.io.InputStream getResourceAsStream(java.lang.String path) 得到资源文件,但是返回的是一个输入流,不需要再转换

    /**
     * 测试ServletContext对象的核心API
     */
    @WebServlet(name = "ServletContextDemo")
    public class ServletContextDemo extends HttpServlet {
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //1.得到ServletContext对象
            //下面两种方法都可以,推荐使用第二种
    //        ServletContext context = this.getServletConfig().getServletContext();
            ServletContext context = this.getServletContext();//推荐使用
    
            //2.得到web应用路径
            //web应用路径:部署到tomcat服务器上运行的web应用名称
            String contextPath = context.getContextPath();
            System.out.println(contextPath);
    
            //案例:请求重定向
            response.sendRedirect(contextPath+"/index.html");
    
            //3.得到web应用参数
            String aaa = context.getInitParameter("AAA");//获取指定的一个
            Enumeration<String> enums = context.getInitParameterNames();//得到所有
    
            //4.域对象的一些操作
            //保存到域对象中
            context.setAttribute("name","tom");
            //从域对象中获得数据
            Object name = context.getAttribute("name");
            //从域对象中删除数据
    
            //5.转发,效果就是跳转页面
            //不能转发到项目以外的路径
            RequestDispatcher rd = context.getRequestDispatcher("/index.html");
            rd.forward(request,response);
            
                    /**
             * 6.读取web应用下的资源文件
             */
    
            //在java项目中,点 . 表示java项目所在的地址
            //java web项目中,点 . 表示tomcat的bin目录,所以不能使用这种相对路径
    //        File file = new File("./src/db.properties");
    //        FileInputStream in = new FileInputStream(file);
    
            //正确获取
            //方法1.getRealPath
            // “/”表示WEB-INF所在目录地址
            String path = this.getServletContext().getRealPath("/WEB-INF/classes/db.properties");
            File file = new File(path);
            FileInputStream in = new FileInputStream(file);
    
            //方法2: 得到资源文件,返回的是输入流
            InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
    
            Properties prop = new Properties();
            //读取资源文件
            prop.load(in);
    
            String user = prop.getProperty("user");
            String password = prop.getProperty("password");
            System.out.println(user+"="+password);
        
        }
    }
    
    
    
  • 相关阅读:
    C# 两个窗体中相互切换的方法
    Linq对DataTable数据的分组统计
    如何让窗体大小随着控件的大小变化而变化
    两张表解决用户自定义数据库之思路
    picturebox显示用字符串代表图片名称的(已导入资源的)图片
    如何将access高版本数据库保存为低版本的?
    Microsoft.Jet.OLEDB.4.0和Microsoft.ACE.OLEDB.12.0的区别
    条件编译解决AutoCAD多版本问题
    一个爬取股票信息的爬虫程序
    Python爬虫抓取东方财富网股票数据并实现MySQL数据库存储
  • 原文地址:https://www.cnblogs.com/cenyu/p/6151589.html
Copyright © 2011-2022 走看看