Servlet_如何使用Servlet?

Servlet

1.1、 Servlet概念

Servlet是服务器端的一个java小程序。它用于:处理,响应请求。

Servlet概念

1.2、 Servlet的创建方式

在创建Servlet时一定要在web.xml中添加<servlet></servlet>和<servlet-mapping><servlet-mapping>标签

为什么要在web.xml中设置url-pattern访问实现Servlet接口的类?

在项目src编写的实现类,tomcat将其编译成字节码文件放在WEB-INF中,外界不能直接访问,因此通过映射路径访问。
  • 方式一:实现Servlet接口

  • 方式二:继承GenericServlet

  • 方式三:继承HttpServlet【开发中使用】

1.3、 Servlet的生命周期

创建ServletDemo类.PNG
实现Servlet接口.PNG
web.xml配置.PNG
结果.PNG
  • 实例化
  • 初始化init

      第一次请求时初始化一次。
    
  • 服务service

      每次请求都要调用一次service()方法
    
  • 销毁destroy

      服务器停止或者服务器移除该项目时销毁。
    

1.4、 Web.xml配置的背后细节

输入地址:http://localhost:8080/项目名称/映射路径

  • <servlet>与<servlet-mapping>中的两个<servlet-name>中的值要一样才能寻找到

  • <url-pattern>中的映射路径要以 / 打头,不要落了。

  • 让Servlet实现类一开始就进行实例化和初始化

      如果想让Servlet的实现类在一开始就进行实例化和初始化,只需要在```<servlet>标签中```
      添加```<load-on-startup>2</load-on-startup>```
    

背后细节:

      <!--
         这是创建的一个Servlet,Servlet的类的路径为当前项目下的Servlet包下的Servlet类,为这个Servlet起了个别名ServletDemo
        网页访问路径:http://localhost:8080/项目名称/类的映射路径
       -->
      <servlet>
        <servlet-name>ServletDemo</servlet-name>
        <servlet-class>ServletDemo.ServletDemo</servlet-class>
      </servlet>
      
      <!-- 
            两个Servlet-name必须一致,url-pattern中为servlet-class的映射路径,通过它可以访问到这个Servlet
       -->
      <servlet-mapping>
        <servlet-name>ServletDemo</servlet-name>
        <url-pattern>/demo</url-pattern>
      </servlet-mapping>


    第一步:服务器通过映射路径url-pattern的demo对应的servlet-mapping的servlet-name
    第二步:根据得到的servlet-name在web.xml中寻找和它名字相同的servlet标签下的servlet-name
    第三步:根据对应的servlet-class中真实路径寻找到Servlet实现类的全路径从而访问
Sevlet配置文件的访问细节

1.4.1、 Web.xml中映射路径的配置

  • 全路径匹配方式 : 以 / 开始

      例如:http://localhost:8080/项目名称/demo
      
      /demo   :   既是具体的全路径配置
    
  • 目录匹配方式 : 以 / 开始 ,以 * 结束

      流:http://localhost:8800/项目名称/aa/rqqhqih/whoqho
          http://localhost:8800/项目名称/aa/ss/wr/q/fg
    
      /aa/*   :   即只要在aa目录下的任意路径都可以
    
  • 扩展名匹配 : 以 * 开始 , 开头没有斜杠/

      例如:http://localhost:8080/项目名称/aa/bb/cc/*.do
    
      *.do    或者      *.(任意)  :   只要以对应扩展名结尾的都可以
    

1.5、 继承HttpServlet类创建一个Servlet

public class ServletDemo2 extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("*******doGet*******");
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("*******doPost*******");
    }
}

写完java的Servlet实现类后,完成web.xml配置。

  <servlet>
    <servlet-name>ServletDemo</servlet-name>
    <servlet-class>ServletDemo2.ServletDemo2</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/demo</url-pattern>
  </servlet-mapping>

结果:每访问一次输出一次doGet。(doGet和doPost服务器根据提交方式选择执行)

1.5.1、 HttpServlet子类调用doGet或doPost方法的背后细节

1:浏览器输入:    http://localhost:8080/项目名/demo

2: 在web.xml里面找到了ServletDemo这个类

3: 获得请求,调用ServletDemo这个类中的service方法。

4: 子类使用继承的父类HttpServlet中的Service(ServletRequest req,ServletResponse res)方法,
    

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    service(request, response);
}



5: 调用service(HttpServletRequest request, HttpServletResponse response)方法,根据提交方式调用对应的doXxx()方法

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();

        if (method.equals("GET")) {
            long lastModified = getLastModified(req);
            if (lastModified == -1L) {
                doGet(req, resp);
            }else if (method.equals("POST")){
                doPost(req, resp);
            } 
                ......


6: HttpServlet父类中有doGet()和doPost()方法,但是我们自己重写了doGet和doPost方法,
因此执行自己的doGet(),打印***doGet*****

2、 登陆案例

2.1 需求:写一个表单html登陆网页,提交账号和密码到服务器,服务器进行到数据库中进行校对,如果存在这样的用户数据,就登陆成功,反之就登录失败!

2.2 准备工作:

  • mysql驱动,C3P0的jar包,DBUtils的jar包,C3P0配置文件
  • 编写bean:User类,C3P0Utils工具类,数据库提供对应的User账号密码数据
  • 编写用户登录表单页面及处理请求Servlet登陆类
包的分类.PNG

2.3 实现步骤:

1:导入相应的jar包,并添加至构建路径

要导入的jar包.PNG

2: 编写bean:User类,C3P0Utils工具类,并创建对应的数据库user

数据库user

数据库账号密码信息表.PNG

配置c3p0-config.xml配置文件

c3p0-config.xml配置文件.PNG

编写C3P0Utils工具类

  • 提供获取连接池,获取连接及释放资源的方法
    public class C3P0Utils {
        // 提供获取连接池,获取连接和释放资源的方法
    
        // 根据c3p0-config.xml配置文件创建连接池
        private static DataSource ds = new ComboPooledDataSource();
    
        // 获取连接
        public static Connection getConnection() {
            Connection connection = null;
            try {
                connection = ds.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        }
    
        // 释放资源
        public static void release(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        
        //获取连接池的方法
        public static DataSource getDataSource() {
            return ds;
        }
    }

编写bean:User类

  • 提供与数据库列名相同的变量,提供get/set方法

      public class User {
          private String username;
          private String userpsd;
      
          public User() {
              super();
              // TODO Auto-generated constructor stub
          }
      
          public String getUsername() {
              return username;
          }
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public String getUserpsd() {
              return userpsd;
          }
      
          public void setUserpsd(String userpsd) {
              this.userpsd = userpsd;
          }
      
          @Override
          public String toString() {
              return "User [username=" + username + ", userpsd=" + userpsd + "]";
          }
      }
    

编写用户登录页面及处理请求Servlet登陆类

  • 用户提交的数据发送至服务器的Servlet处理
  • 表单标签中action属性填写提交数据的地址,我们填写Servlet实现类的映射路径

用户表单页面Login.html

表单登录页面.PNG

处理请求Servlet登陆类

  • 获取提交数据的账号和密码

  • 获取数据库中对应的信息,查询是否有符合条件的

      public class Login extends HttpServlet {
      
          // doGet()方法
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              // TODO Auto-generated method stub
              super.doGet(req, resp);
          }
      
          // doPost()
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              // 首先配置对应的xml文件,让提交的请求发送到该Servlet处理
      
              // 获取数据名为username的值
              String username = req.getParameter("username");
              // 获取数据名为userpsd的值
              String userpsd = req.getParameter("userpsd");
      
              // 使用DBUtils框架查询数据库中所有账号和密码与其输入的账号密码匹配的记录
              // 1:创建QueryRunner对象
              QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource());
              // 2:编写sql语句
              String sql = "select * from User where username=? and userpsd=?";
              // 3:执行sql语句
              Object[] params = { username, userpsd }; // 占位符问号处的值
      
              try {
                  List<User> users = runner.query(sql, new BeanListHandler<>(User.class), params);
      
                  if (users.size() > 0) {
                      resp.getWriter().println("Login Succed!");
                  } else {
                      resp.getWriter().println("Login Failed!");
                  }
              } catch (SQLException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
          }
      }
    

配置HttpServlet子类Login类的web.xml文件

  <servlet>
    <servlet-name>Login</servlet-name>
    <servlet-class>Login.Login</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>Login</servlet-name>
    <url-pattern>/login</url-pattern>
  </servlet-mapping>

将这里url-pattern的映射路径写到Login.html的表单标签的action属性中,提交的数据就能来到Login类处理

到这里,登陆案例就完成了,查看运行结果。

浏览器中输入地址:       http://localhost:8080/day14_ServletLoginDemo/login.html 。

在登陆的表单页面中填入账号密码

username : admin ,  userpsd : admin

数据库中没有这个用户的账号密码,登陆失败

username : lzl  ,   userpsd : lzl

数据库中有该用户的对应的信息,登陆成功  
账密错误演示
账密错误结果
账密正确演示
账密错误演示结果

3、ServletContext

3.1、 ServletContext概念

它是Servlet的上下文 , 每次项目加载到服务器时该项目都会实例化一个ServletContext对象,一个项目有且只有一个ServletContext对象。

3.2、 生命周期

    何时创建:

        1.服务器加载时,被服务器托管的项目都会实例化一个ServletContext对象
        2.项目发布时,实例化一个SoervletConetxt对象

3.3、 ServletContext的作用:

存/取数据

        getAttribute(String name) : 获取属性
        setAttribute(String name , Object obj) : 设置属性

        //1:存数据到关联的web.xml中
        context.setAttribute("name", "aobama");
        
        //2:获取存入到关联的web.xml中的属性
        String str = (String) context.getAttribute("name");

获取项目资源

方式一:
    获取到ServletContext对象,调用getResourceAsStream(String path)方法得到资源的流对象。
    将获取到的流对象用Properties加载出来。
    要注意的是getResourceAsStrem方法中的路径默认是在Tomcat/Webapps/项目名工程下的路径查找,而我们
    的资源被Tomcat默认放在项目工程下的的WEB-INF/classes中保存的,因此填入的路径就填相对路径就可以了

    //需求:获取该项目src下的my.properties文件

    //获取能够加载流文件的对象Properties
    Properties properties = new Properties();
    
    //获取当前项目的ServletContext对象
    ServletContext context = this.getServletContext();
    //获取my.properties文件的流对象
    InputStream inputStream = context.getResourceAsStream("WEB-INF/classes/my.properties");
    
    //将当前流对象用properties加载读取
    properties.load(inputStream);
    
    //获取my.properties文件中的值
    String username = properties.getProperty("username");
    String password = properties.getProperty("password");
    //输出值
    System.out.println("username: "+username);
    System.out.println("password: "+password);



方式二:getRealPath(String path)

    自己创建输入流读取my.properties文件

    // 获取能够加载流对象的Properties对象
    Properties properties = new Properties();

    // 因为FileInputStream中要填入全路径 , 因此调用getRealPath()方法获取文件全路径
    String path = this.getServletContext().getRealPath("WEB-INF/classes/my.properties");

    // 获取文件流对象
    InputStream is = new FileInputStream(path);

    // 加载文件并获取文件中的内容
    properties.load(is);
    String username = properties.getProperty("username");
    String password = properties.getProperty("password");

    System.out.println("username: " + username);
    System.out.println("password:  " + password);

            

方式三:类加载器读取my.properties文件

    //使用类加载器获取my.properties文件资源,这里的字节码对象可以是任意的class对象
    InputStream is = this.getClass().getClassLoader().getResourceAsStream("my.properties");

    //获取Properties对象,将该文件流对象加载
    Properties properties = new Properties();
    properties.load(is);
    
    //获取该资源中的内容
    String username = properties.getProperty("username");
    String password = properties.getProperty("password");
    
    System.out.println("username: "+username);
    System.out.println("password: "+password);


浏览器输入:http://localhost:8080/项目名/映射路径
结果为:        username: jack
            password: 123
类加载器获取流对象的默认路径直接是在WEB-INF/classes下的,因此直接在后面拼接my.properties就可以正确加载。

获取全局参数

在web.xml中在<context-param></context-param>中设置全局参数,与局部参数一样,
有<param-name></param-name>和<param-value></param-value>两个值,设置它们的值,
在与其关联的Servlet中可以通过SevletContext对象的getInitParam(String)方法获得全局参数。

在web.xml中设置全局参数:
    可以把<param-name>和<param-value>看做键值对

  <context-param>
    <param-name>姓名</param-name>
    <param-value>凌霄</param-value>
  </context-param>



ServletContextDemo中获取全局参数代码:    

//获得当前项目的ServletContext对象
ServletContext context = this.getServletContext();

//获取全局参数
System.out.println(context.getInitParameter("姓名"));

浏览器输入 : http://localhost:8080/ServletContextDemo3/demo
结果 : 凌霄

3.4、ServletContext案例一

需求:

在上面的登陆案例基础上,额外添加校对正确后,跳转到登录成功页面,显示内容:登陆成功,等待5秒后跳转。

跳转到新的页面,显示:欢迎某某,您是第n位登陆的用户!

思路:

  1. 在校对账密的Servlet中,如果登陆成功就跳转到登陆成功页面

  2. 编写登陆成功页面,实现5秒后跳转新页面的功能。(实现倒计时)

  3. 编写跳转后的页面。

     由于有计数,因此肯定要在某个地方定义一个计数器,将其定义在web.xml中,
     通过ServletContext对象可以方便的存入与取出该数据进行操作。
    

实现:

核对密码的Servlet代码:
    // 首先配置对应的xml文件,让提交的请求发送到该Servlet处理

    // 获取数据名为username的值
    String username = req.getParameter("username");
    // 获取数据名为userpsd的值
    String userpsd = req.getParameter("userpsd");

    // 获取校对后的用户
    List<User> users = Service.check(username, userpsd, resp);

    if (users.size() > 0) {
        // 计数
        // 获取ServletContext对象,并取得属性为count的值
        ServletContext context = getServletContext();
        Object obj = context.getAttribute("count");// 获得了属性为count的值

        if (obj == null) { // 证明第一个登陆,设置计数器为1
            context.setAttribute("count", 1);
        } else { // 证明不是第一个登陆,取出count的值并+1存入
            context.setAttribute("count", (Integer) obj + 1);
        }

        resp.sendRedirect("Login_Succed.html");// html页面就在项目根目录下,跳转到该页面
    } else {
        resp.getWriter().write("Login Failed");
    }
}
登陆成功页面Login_Succed.html
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script type="text/javascript">
    var count = 5;
    //完成倒计时功能
    function change() {
        setInterval(function name() {
            count--;
            //设置<span>标签内的值
            document.getElementById("span1").innerText = count;

            //5秒后跳转页面
            if (count == -1) {
                location.href = "login2";
            }
        }, 1000);
    };
</script>

</head>
<body onload="change()">
    <h3>
        登陆成功,<span id="span1">5</span>秒钟后跳转!
    </h3>
</body>
</html>
映射路径为login2的LoginSucced的Servlet类
public class LoginSucced extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 防止中文乱码,让客户端和服务端使用的码表一致
        response.setContentType("text/html;charset=UTF-8");

        // 获取ServletContext对象
        ServletContext context = getServletContext();
        // 获得当前属性值count的值
        int count = (Integer) context.getAttribute("count");

        response.getWriter().write("<h3>欢迎光临,您是第" + count + "位登陆的用户!</h3>");
    }

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

****欢迎交流~~****

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容