Servlet

开发工具

Tomcat

是一个轻量级的JSP/Servlet容器,可以用于中小型项目中的开发和调试,也有用于生产环境的

下载地址https://tomcat.apache.org/download-10.cgi

Tomcat10下载

下载完成后直接解压即可使用

Tomcat的目录

bin:tomcat启动、关闭之类的脚本文件

  • 启动服务器 startup.bat
  • 关闭服务器 shutdown.bat
  • 启动服务器后则可以在浏览器地址栏中输入localhost:8080,如果没有报错,可以看到一个页面则表示运行正常

conf:tomcat各种配置文件。其核心配置文件有:

  • server.xml 配置与server相关信息
  • web.xml是web应用(一个web应用相当于一个站点)的父配置文件
  • tomcat-users.xml 配置tomcat相关信息(管理tomcat服务器的用户名、密码和权限等)

lib:tomcat启动所依赖的jar包

logs:tomcat的系统日志

temp:tomcat运行时产生的临时文件

webapps:web应用所在的目录,即提供外界访问的web资源存放目录。

work:tomcat工作目录,主要用于存放jsp被访问后生成对应的servlet文件以及class文件

Tomcat端口号的配置

conf/server.xml中查找有效的连接子配置

 <Connector port="8080端口号" protocol="HTTP/1.1所支持的协议版本" connectionTimeout="20000连接超时配置,单位为ms" redirectPort="8443" />

Maven

使用的是最基本的功能,即依赖管理

  • 添加依赖需要知道对应依赖的坐标值GAV

    • 通过中央库mvnrepository.com提供的web页面可以查询对应的依赖GAV坐标

      <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.23</version>
      </dependency>
      
    • 将GAV坐标值拷贝到当前项目的pom.xml配置文件中,注意位置

      <dependencies>
              <dependency>
                  <groupId>javax.servlet</groupId>
                  <artifactId>javax.servlet-api</artifactId>
                  <version>4.0.1</version>
                  <scope>provided</scope>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.23</version>
              </dependency>
      </dependencies>
      

    添加依赖配置后,默认会自动从中央库中下载对应的jar包。一般建议修改配置使用ali的镜像库进行下载,不使用国外站点。如果下载失败则会报错

    C:\Users\Administrator\.m2目录下添加settings.xml,然后重启IDEA即可

    <mirror>
    		<id>aliyunmaven</id>
    		<mirrorOf>central</mirrorOf>
    		<name>aliyun maven</name>
    		<url>https://maven.aliyun.com/repository/public</url>
    </mirror>
    
  • 需要遵循一般的约定

    • 源代码在src/main/java目录下
    • 配置文件位于src/main/resources目录下
    • 页面文件位于src/main/webapp目录下
    • 测试源代码文件位于src/test/java目录下
    • 测试配置文件位于src/test/resources目录下
  • 三大核心生命周期

Servlet组件

什么是servlet

Servlet是一种用于扩展服务器功能的服务器端组件技术,用于实现动态网页编程

  • 是直接或者间接实现Servlet接口的类
  • 有三种:Servlet Filter xxxListener

Hello Servlet

Servlet接口不在JavaSE中,需要因为servlet-api依赖

 <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>javax.servlet-api</artifactId>
     <version>4.0.1</version>
     <scope>provided</scope>  scope=provider表示该jar包在编译阶段使用,并不会打包到应用中
</dependency>

1、定义一个类实现Servlet接口

public class HelloServlet implements Servlet {  //所谓的Servlet就是直接或者间接的实现Servlet接口
    @Override
    public void init(ServletConfig config) throws ServletException {
    }
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {   // Servlet组件用于对外提供服务的方法
        //接收用户提交数据,不管是使用get还是post提交
        String username=req.getParameter("username");
        if(username==null ||username.trim().length()<1)
            username="Servlet";
        String message="Hello "+username+"!";
        //使用rep生成针对客户端请求的响应信息
        PrintWriter pw=res.getWriter();
        pw.println("<h2>"+message+"</h2>");//生成html文档
        pw.flush();
        pw.close();
    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public void destroy() {
    }
}

service方法实际上是由tomcat服务器负责调用执行,服务器调用方法时会传入两个参数,一个req用于封装所有的用户请求信息,一个res用于封装服务器向客户端的响应信息

  • req.getParameter用于获取用户提交的数据,例如get请求 hello.do?username=zhangsan

  • res.setContentType用于告知客户端的浏览器,如何处理响应信息

    • res.setContentType(“text/html;charset=utf-8”);
    • 遵循一个MIME协议(多用途互联网邮件协议),其中text表示是一个文本文档,html表示是一个html格式的文档文档
  • res.getWriter用于获取向客户端输出数据的输出流,在编码中向输出流中写出数据,实际上就是向客户端输出的内容

2、在web应用的核心配置文件web.xml中将Servlet和一个URL地址建立对应关系,当用户请求对应的URL地址时则触发Servlet运行

  • Servlet类中没有main方法,所以程序的执行是由服务器按照规则进行调用的
  • 由客户端对特定路径发起请求触发执行
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 将Servlet类和一个名称建立对应关系 -->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.yan.action.HelloServlet</servlet-class>
    </servlet>
    <!-- 将一个地址和名称建立对应关系 -->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello.do</url-pattern>
    </servlet-mapping>
</web-app>

3、启动Tomcat服务器

4、在浏览器中输入请求URL地址 http://localhost:8080/demo1_war/hello.do

5、使用get提交数据 http://localhost:8080/demo1_war/hello.do?username=zhangsan

IDEA控制台提示乱码

在Tomcat服务器下conf/logging.properties是日志输出的相关配置

java.util.logging.ConsoleHandler.encoding = GBK

Servlet的特点

Servlet用MIME过滤数据,由javax.servlet和javax.servlet.http两个包组成Java Servlet API框架

编写Servlet必须直接或间接实现作为Servlet的核心javax.servlet.Servlet接口,通常是实现javax.servlet.http.HttpServlet的子类,可以覆盖继承doGet()或者doPost()方法,两者分别对应于Http(s)中的Get请求和Post请求。

优点
  • 可以移植性:由于Servlet是用Java语言编写的,因此它可以在不同的操作系统和服务器上移植
  • 安全:Servlet具有类型检查特征,并利用Java的垃圾收集和没有指针的设计,使得Servlet避免了内存管理等问题
  • 高效:Servlet加载执行后会常驻服务器内存中,当再次受到客户端的请求时,服务器会产生新的线程而不是进程为客户端服务,这样就提高了响应速度。 – Java是最差的内存性价比

缺点

  • 不能所见即所得开发
  • 生成html文档需要通过一系列的输出完成,非常的繁琐低效
应用场景

主要是处理客户端的请求并将其结果发送到客户端 ,由于生成html文档繁琐低效,所以一般不建议使用Servlet直接生成响应页面。建议考虑使用JSP

Servlet开发

Servlet编程

  • 直接实现Servlet接口,需要实现5个方法

    public interface Servlet {
       public void init(ServletConfig config) throws ServletException;  //servlet对象创建后执行初始化操作,例如通过config读取配置信息
       public ServletConfig getServletConfig();  //获取当前Servlet对象的相关ServletConfig对象。ServletConfig对象用于封装当前Servlet对象的配置信息
       public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;  //对外提供服务。Servlet针对多用户会采用单实例多线程的方式对外提供服务
       public String getServletInfo();  //获取当前Servlet的说明信息,一般供工具使用
       public void destroy(); //在当前Servlet实例被GC回收前所运行的方法,一般用于资源回收
    }
    
  • 为了简化开发,所以Sun提供了一个通用抽象父类GenericServlet。针对大部分方法提供了默认的空实现,只需要Servlet提供service方法的实现即可,一般用于非通用协议开发中。采用的是适配器模式

    public class Hello3Servlet extends GenericServlet {
        @Override
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    
        }
    }
    
  • 为了进一步简化开发,提供针对http协议的支持,Sun提供了一个GenericServlet的子类HttpServlet,这个父类专门用于http协议的应用开发中,是模板设计模式

    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
            String method = req.getMethod();   //获取客户端的请求方法,例如get/post之类
            if (method.equals(METHOD_GET)) {  //如果是GET请求则调用doGet方法
               ...
                    doGet(req, resp);   //转向调用自己的doGet方法
            } else if (method.equals(METHOD_HEAD)) {
                doHead(req, resp);
            } else if (method.equals(METHOD_POST)) {
                doPost(req, resp);
            } else if (method.equals(METHOD_PUT)) {
                doPut(req, resp);
            } else if (method.equals(METHOD_DELETE)) {
                doDelete(req, resp);
            } else if (method.equals(METHOD_OPTIONS)) {
                doOptions(req,resp);
            } else if (method.equals(METHOD_TRACE)) {
                doTrace(req,resp);
            } else {  //如果是其它请求方法则报错
                String errMsg = lStrings.getString("http.method_not_implemented");
                Object[] errArgs = new Object[1];
                errArgs[0] = method;
                errMsg = MessageFormat.format(errMsg, errArgs);
                resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
            }
        }	
    

默认的doGet方法实现

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException  {
        String protocol = req.getProtocol();  //获取请求所使用的协议,例如http/1.1
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

###Servlet的三生命周期

  • 默认servlet不会自动加载,只有当第一次访问时才会被服务器自动加载,一般Servlet会采用单实例多线程的方式对外提供服务

  • 加载完成立即执行初始化操作(new操作),构建对象完成后立即执行init方法,同时服务器会将当前Servlet的配置信息封装在一个ServletConfig对象中 。在一个Servlet的生命周期过程中init方法执行且只执行一次

    <servlet>
            <servlet-name>hello2Servlet</servlet-name>
            <servlet-class>com.yan.action.Hello2Servlet</servlet-class>
            <!--初始化配置参数胡-->
            <init-param>
                <param-name>name</param-name>
                <param-value>lisi</param-value>
            </init-param>
    </servlet>
    
  • 针对用于请求信息以多线程的方式运行service方法,同时服务器会自动封装request和response对象传入方法中

  • 请求处理完毕,Servlet对象常驻内存。

  • 只有当关闭服务器或者由于内存不足导致当前Servlet对象被调度销毁对象时,才会执行destroy方法,在一个Servlet的生命周期过程中destroy方法执行且只执行一次

###练习:用户登录

流程:

地址栏中输入/login.do打开输入页面,供用户输入对应的用户名和口令,再点击提交按钮则再服务器中执行数据库查询验证,成功则显示欢迎页,否则报错

  • 地址栏中输入地址----Get请求
  • 点击提交按钮,因为涉及敏感数据—Post提交

开发:

public class LoginServlet extends HttpServlet {
    //针对get请求的处理,需要生成一个带有form表单的输入页面
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out=resp.getWriter();
        out.println("<html><head><title>用户登录</title></head><body>");
        out.println("<form><table>");
        out.println("<tr><td>用户名称:</td><td><input name=‘username’/></td></tr>");
        out.println("<tr><td>用户口令:</td><td><input type='password' name=‘passwird’/></td></tr>");
        out.println("<tr><td colspan=2><input type='submit' value='登录系统'/><input type='reset' value='重置数据'/></td></tr>");
        out.println("</table></form>");
        out.println("</body></html>");
    }
}

配置web.xml

    <servlet>
        <servlet-name>login</servlet-name>
        <servlet-class>com.yan.action.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login.do</url-pattern>
    </servlet-mapping>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bKHtzz9Y-1617958848777)(images/login页面.png)]

点击提交按钮的处理

  • 如果针对<form>表单不设置method,则默认采用get请求;

    • http://localhost:8080/demo1_war/login.do?username=adfasd&password=adfasd
    • 如果定义一个<form>没有action,则提交到当前页面
  • 如果需要使用post提交,则必须设置method=post

package com.yan.action;

import com.yan.util.JdbcUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;

public class LoginServlet extends HttpServlet {
    //针对get请求的处理,需要生成一个带有form表单的输入页面
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {   //对应get请求的具体处理
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out=resp.getWriter();
        out.println("<html><head><title>用户登录</title></head><body>");
        out.println("<form method='post'><table>");
        out.println("<tr><td>用户名称:</td><td><input name='username'/></td></tr>");
        out.println("<tr><td>用户口令:</td><td><input type='password' name='password'/></td></tr>");
        out.println("<tr><td colspan=2><input type='submit' value='登录系统'/><input type='reset' value='重置数据'/></td></tr>");
        out.println("</table></form>");
        out.println("</body></html>");
    }
    //针对post请求的处理,需要调用数据库查询判断用户提交的数据是否正确
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  //针对post请求的具体处理,目前可以实现post请求的方法只有一种<form method=post>
        String username=req.getParameter("username");  //获取用户在<input name="username"/>输入框中输入的数据
        String password=req.getParameter("password");
        boolean bb=false;
        //这里实际上就是登录处理逻辑,直接写在Servlet类中是不合理的,应该参照JavaEE的三层开发的方式来实现
        Connection conn=null;
        ResultSet rs=null;
        JdbcUtil ju=JdbcUtil.getInstance();
        try{
            conn= ju.getConnection();
            String sql="select * from t_users where username=? and password=?";
            rs=ju.executeQuery(conn,sql,username,password);
            bb=rs.next();
        }catch(Exception e){
            throw new ServletException(e);
        }finally{
            try {
                ju.close(rs,null,conn);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out=resp.getWriter();
        if(bb){
            //成功页
            out.println("success!");
        }else{
            //失败页
            out.println("failure!");
        }
        out.flush();
        out.close();
    }
}

注意:需要在MySQL中创建一个数据库和对应的数据表,表中包含数据才能登录成功

Servlet技术

Servlet规范是JavaEE规范中的一个部分,是用于扩展服务器功能的运行在服务器中的小型Java程序(即服务器端小应用程序),是实现了Servlet接口通过http接收和响应web客户端请求的类

静态和动态

  • 因人因时因地而发生变化

服务器

  • web服务器:定位资源—Apache
  • jsp/servlet容器:运行jsp或者servlet程序—Tomcat

Servlet概述

Servlet是一种用于扩展服务器功能的服务器端组件技术

  • 要求直接或者间接的实现Servlet接口
  • 运行在服务器端,执行结果是一个html文档

Servlet的特征

  • 一般采用单实例多线程的方式对外提供服务,一个客户请求对应一个线程
    • 如果在Servlet类中定义属性,则需要考虑线程安全问题

Servlet的优点

1.高效。在服务器上仅有一个Java虚拟机在运行,它的优势在于当多个来自客户端的请求进行访问时,Servlet为每个请求分配一个线程而不是进程。

  • 面试题:Servlet和CGI的区别
    • 早期的CGI采用的是一个用户对应一个线程的方式对外提供服务,所以比较浪费资源。而Servlet采用的就是多线程的方式对外提供服务,所以比较节约资源,而且方便相互通讯

2.方便。Servlet提供了大量的实用工具例程,例如处理很难完成的HTML表单数据、读取和设置HTTP头、处理Cookie和跟踪会话等。

3.跨平台。Servlet是用Java类编写的,它可以在不同的操作系统平台和不同的应用服务器平台下运行。

4.灵活性和可扩展性。采用Servlet开发的Web应用程序,由于Java类的继承性、构造函数等特点,使得其应用灵活,可随意扩展。

6.共享数据。Servlet之间通过共享数据可以很容易地实现数据库连接池。它能方便地实现管理用户请求,简化Session和获取前一页面信息的操作。而在CGI之间通信则很差。由于每个CGI程序的调用都开始一个新的进程,调用间通信通常要通过文件进行,因而相当缓慢。同一台服务器上的不同CGI程序之间的通信也相当麻烦。

7.安全。有些CGI版本有明显的安全弱点。即使是使用最新的标准和PERL等语言,系统也没有基本安全框架。而Java定义有完整的安全机制,包括SSL\CA认证、安全政策等规范。

Servlet缺陷

1、不能所见即所得的实现页面的开发和开发测试不方便。解决方案是JSP

##Servlet开发流程

1、定义Servlet类

  • 直接实现Servlet接口

    public class MyServlet implements Servlet{
        //需要定义5个Servlet接口中声明的方法
    }
    
  • 为了简化开发,针对非标准协议,提供了一个抽象父类GenericServlet

    • 采用的是适配器模式
    • 一般用于游戏的服务器端开发
    public class MyServlet extends GenericServlet{
        public void service(ServletRequest request,ServletResponse response)throws ServletException,IOException {
            
        }
    }
    
  • 为了进一步简化开发,针对http协议,提供了一个父类HttpServlet

    • 采用的是模板模式,主要提供的service方法的实现,所以一般不允许覆盖定义service
    • 内部实现实际上就是通过request.getMethod():String获取请求方法,然后根据对应的请求方法转发给对应的doXxxx方法
      • 例如/add.do?id=124&name=lisi
        • get请求使用?作为地址和参数的分割,例如add.do是请求地址,?之后的内容就是请求串
        • 多个请求参数之间采用&符号作为分割
        • 参数的格式为[名称=值]
    public class MyServlet extends HttpServlet{
        public void doXxx(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
            ...
        }
    }
    

2、使用web应用的核心配置文件/WEB-INF/web.xml映射当前Servlet

  • Servlet类中没有main方法,也就是说具体调用由服务器负责。具体执行时机默认为第一次用户请求时

    <servlet>
    	<servlet-name>名称</servlet-name>
        <servlet-class>对应的类的全名</servlet-class>
    </servlet>
    <servlet-mapping>
    	<servlet-name>对应上面定义的名称</servlet-name>
        <url-pattern>/请求地址.do</url-pattern>
    </servlet-mapping>
    

3、当用户在浏览器中输出xml中配置的请求路径,则由服务器负责按照一定的标准调用Servlet类中的定义方法

###Servlet常见错误

404错误:资源未找到

  • 在请求地址中的servlet的别名书写错误
  • 虚拟项目名称拼写错误

500错误:内部服务器错误

  • ClassNotFoundException,检查web.xml中Servlet类的全限定类名称拼写是否正确
  • 因为service方法体的代码执行错误导致的,可以根据提示信息对方法体中的代码进行更改

405错误:请求方式不支持

  • 请求方式和servlet中的方法不匹配所导致的

Caused by: java.net.BindException: Address already in use: JVM_Bind

  • 多次重复启动服务器,导致服务器端口号冲突

####Servlet与JSP区别

简单的说,SUN首先发展出Servlet,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。 后来SUN推出了类似于ASP的镶嵌型的JSP,把JSP标签镶嵌到HTML语句中,这样,就大大简化和方便了网页的设计和修改。很多网络语言如ASP,PHP,JSP都是镶嵌型的SCRIPT语言

1、JSP在本质上就是SERVLET,但是两者的创建方式不一样

2、Servlet完全是JAVA程序代码构成,擅长于流程控制和事务处理,通过Servlet来生成动态网页很不直观

3、JSP由HTML代码和JSP标签构成,可以方便地编写动态网页

因此在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层.

1、JSP是Servlet技术的扩展,本质上就是Servlet的简易方式

2、JSP编译后是“类servlet”

3、Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP是Java和HTML组合成一个扩展名为.jsp的文件

4、JSP侧重于视图,Servlet主要用于控制逻辑

Servlet实例对象的生命周期

Servlet遵循严格的生命周期,在每个Servlet实例的生命中有三种类型的事件,这三种事件分别对应于由Servlet引擎所唤醒的三个方法:

  • init()。当Servlet第一次被装载时,Servlet引擎调用这个Servlet的init()方法,只调用一次。如果某个Sevlet需要特殊的初始化需要。那么Servlet编写人员可以重写该方法来执行初始化任务。这是一个可选的方法。如果某个Servlet不需要初始化,那么默认情况下将调用它父类的init方法。系统保证,在init方法成功完成以前,是不会调用Servlet去处理任何请求的
  • service()。这是Servlet最重要的方法,是真正处理请求的地方。对于每个请求,Servlet引擎将调用Servlet的service方法,并把Servlet请求对象和Servlet响应对象最为参数传递给它
  • 常驻内存
  • destroy()。这是相对于init的可选方法,当Servlet即将被卸载时由Servlet引擎来调用,这个方法用来清除并释放在init方法中所分配的资源。

###Servlet技术的要点:

  • 当前应用中的所有Servlet接口实现类的实例对象,只能由服务器负责创建,开发人不能不能手动创建

  • 默认情况下以单实例多线程的方式对外提供服务

  • Servlet实例一旦创建则常驻内存,只有当服务器资源不足而导致当前Servlet对象被调度或者服务器关闭时才会被销毁

  • 默认情况下,服务器接收到对于当前Servlet接口实现类的第一次请求时自动创建这个Servlet接口实现类的实例对象

    • 可以手动配置要求服务器在启动时自动创建某个Servlet接口实现类对象

##核心对象

API和SPI 分散关注

ServletConfig对象

对应的是Servlet的配置参数信息,由服务器提供对应的具体实现。具体实现是在服务器调用init方法时传入到Servlet实例中

public void init(ServletConfig config) throws ServletException;

config对象由服务器提供实现,并在服务器调用该方法时传入Servlet实例

public interface ServletConfig {
    public ServletContext getServletContext(); //用于获取Servlet上下文对象
    public String getInitParameter(String name);  //用于获取当前Servlet对象的配置参数
    public Enumeration<String> getInitParameterNames();//用于获取当前Servlet对象的所有配置参数的迭代器
}
Servlet的配置 web.xml
<servlet>
        <servlet-name>showServlet</servlet-name>
        <servlet-class>com.yan.action.ShowServlet</servlet-class>
    <!-- 初始化参数 -->
        <init-param>
            <param-name>name</param-name>
            <param-value>yanjun</param-value>
        </init-param>
        <init-param>
            <param-name>age</param-name>
            <param-value>123</param-value>
        </init-param>
</servlet>

在servlet中获取初始化参数

init方法定义

init(ServletConfig)是Servlet接口中定义的方法,需要自行编程处理config对象,因为Servlet接口中还有一个方法时和config对象相关的public ServletConfig getServletConfig();

public class MyServlet implements Servlet{
	private ServletConfig config;
	public void init(ServletConfig config){
		this.config=config;
	}
	public ServletConfig getServletConfig(){
		return config;
	}
	......
}

实际上HttpServlet已经提供了以上的实现

public abstract class HttpServlet extends GenericServlet {
	private transient ServletConfig config;  //父类提供
	
	public void init(ServletConfig config) throws ServletException {
		this.config = config;
		this.init();   //提供一个扩展点
    }
    public void init() throws ServletException {

    }

自定义Servlet接收初始化配置参数

    @Override
    public void init() throws ServletException {
        //对应web.xml中init-param标签中定义的参数
        String ss = this.getServletConfig().getInitParameter("name");  //根据预先知道的名称获取对应的配置参数值
        /*
         <init-param>  配置初始化参数,在一个servlet标签中可以定义多个
            <param-name>name</param-name>   参数名称
            <param-value>yanjun</param-value>  参数值
        </init-param>
         */
        System.out.println(ss);
        //迭代访问所有的配置参数
        Enumeration<String> en = this.getServletConfig().getInitParameterNames();
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            String val = this.getServletConfig().getInitParameter(key);
            System.out.println(key + ":" + val);
        }
    }

配置自动加载

默认情况下,当第一次客户端访问对应地址时,Servlet才会被加载并执行初始化操作。也可以考虑到执行效率要求服务器启动时自动加载并初始胡特定的Servlet

    <servlet>
        <servlet-name>showServlet</servlet-name>
        <servlet-class>com.yan.action.ShowServlet</servlet-class>
       <load-on-startup>1</load-on-startup>  应该是大于等于0的整数,值越小越优先加载
    </servlet>

###ServletRequest对象

在servlet接口中所定义的service方法中出现

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
  • 用于封装客户端的请求信息
  • 是由服务器提供对应的实现类

当服务器调用Servlet实例的service方法时传入对应的接口实现的实例,其中包含服务器所接受到的所有客户端请求信息。一般在具体开发中针对http协议使用的是ServletRequest接口的子接口HttpServletRequest

public interface HttpServletRequest extends ServletRequest
主要方法

1、请求参数

get:使用地址的方式传递数据,实际上使用的是协议头,上限2k

  • http://localhost:8080/test/del.do?id=123&name=lisi其中id=123&name=lisi就是请求参数

post:使用协议体传递数据,默认格式还是id=123&name=lisi,只是不在地址栏中出现,一般用于敏感数据传递或者上传大于2k的数据

String username = request.getParameter("username");

按照名称可以获取对应的传递数据,获取的数据都是String或者String[]类型

使用一个名称传递多个数据

/abc.do?hobby=111&hobby=333&hobby=444

<input type="checkbox" name="hobby" value="111">足球
<input type="checkbox" name="hobby" value="222">篮球
<input type="checkbox" name="hobby" value="333">弹球
<input type="checkbox" name="hobby" value="444">其它

服务器获取数据的方法:

String[] hobby=request.getParameterValues("hobby");
  • 如果使用getParameter则只能获取第一个数据,后续数据丢失

注意:实际上使用一个名称传递一个值,也可以使用getParameterValues,只是获取的是一个长度为1的字符串数组

特殊方法:一般很少用,除非开发表现层框架

Map<String, String[]> map= request.getParameterMap();  //获取所有的请求参数,其中请求参数名称为key,对应的值为String[]

Map<String,String[]> map=request.getParameterMap();
        for(Map.Entry<String,String[]> en:map.entrySet()){
            System.out.println(en.getKey());
            for(String temp:en.getValue()){
                System.out.println("\t\t"+temp);
            }
        }
        
        
Enumeration<String> names=request.getParameterNames();   //获取所有请求参数名称的迭代器
        while(names.hasMoreElements()){  
            String key=names.nextElement();   //通过迭代器对象获取每个请求参数名称
            String value=request.getParameter(key);  //根据名称获取对应的值
            System.out.println(key+"--->"+value);
        }       

2、以流的方式处理请求信息

InputStream is=request.getInputStream();
BufferedReader br=request.getReader();

上传文件

1、要求使用post提交数据,必须设置enctype=“multipart/form-data”

<form action="test.do" method="post" enctype="multipart/form-data"> 注意表单中的post必须设置,因为默认为get;必须设置enctype,因为默认值application/x-www-form-urlencoded
    <input type="file" name="photo"/>
    <input type="submit" value="上传文件"/>
</form>

2、jspSmartUp组件是一种早期流行使用的上传下载工具,适合于上传小型的文件,具有灵活性简单高效。

 @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter pw=response.getWriter();

        SmartUpload su=new SmartUpload();//创建上传组件对象
        su.initialize(this.getServletConfig(),request,response);//初始化上传组件
        su.setMaxFileSize(2*1024*1024);  //设置允许上传的文件最大大小,单位字节,一般设置为2M
        su.setAllowedFilesList("jpg,png,gif");//设置允许上传的文件后缀类型
        try {
            su.upload();//上传文件,将文件从客户端上传到服务器,如果上传文件不符合要求,则包异常
            //SmartUpload组件支持多文件上传
            Files files=su.getFiles();//获取所有的上传文件
            if(files!=null && files.getCount()>0){//判断上传文件个数是否大于0
                File file=files.getFile(0);//获取第一个上传文件
                System.out.println(file.getFileName());

                String ss=request.getRealPath("");  //获取当前目录的绝对路径,一般推荐通过application对象获取,不建议使用request对象获取
                System.out.println(ss);

                file.saveAs("upload/"+file.getFileName());  //使用的是相对路径,相对于ss的路径

            }
            pw.println("上传上传,点<a href='abc.html'>这里</a>继续上传")
        } catch (Exception e){
            e.printStackTrace(pw);
            pw.println("<a href='abc.html'>重新上传</a>");
        }
        pw.flush();
        pw.close();
    }

下载处理

1、列表显示所有的可下载内容

 @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out=response.getWriter();
        String ss=request.getRealPath("upload/");  //获取upload文件夹的绝对路径
        File ff=new File(ss);
        if(ff.exists()){
            File[] fs=ff.listFiles();
            if(fs!=null && fs.length>0){
                for(File tmp:fs)
                	out.println("<a href='down.do?fname="+tmp.getName()+"'><img src='upload/"+tmp.getName()+"' width='80' height='60'></a>");
            }
        }
        out.flush();
        out.close();
    }

2、下载处理要点:

告知浏览器以附件的方式打开响应数据

响应头的名称Content-Disposition,对应的值为attachment;filename=FileName.txt

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fname=request.getParameter("fname");
        String ss=request.getRealPath("upload/");
        File ff=new File(ss,fname);
        InputStream is=new BufferedInputStream(new FileInputStream(ff));

        response.setHeader("Content-Disposition","attachment;filename=abc.png");
        OutputStream outputStream=response.getOutputStream();

        byte[] buffer=new byte[8192];
        int len=0;
        while((len=is.read(buffer))>0)
            outputStream.write(buffer,0,len);
        outputStream.close();
        is.close();
    }

3、请求头参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMAU1mFN-1617958861914)(images/request请求.png)]

编程实现获取请求头数据

String val=request.getHeader("Accept");  //根据名称获取指定的请求头数据,数据类型一般有3种,默认String,还有int和date
        //获取所有的请求头名称的迭代器
Enumeration<String> names= request.getHeaderNames();
while(names.hasMoreElements()){
            String name=names.nextElement();
            String value=request.getHeader(name);
            System.out.println(name+"-->"+value);
}

实际显示内容

host-->localhost:8080
connection-->keep-alive
upgrade-insecure-requests-->1
user-agent-->Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 Edg/89.0.774.45
accept-->text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site-->none
sec-fetch-mode-->navigate
sec-fetch-user-->?1
sec-fetch-dest-->document
accept-encoding-->gzip, deflate, br
accept-language-->zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
cookie-->JSESSIONID=C25DED076B79C8246177CF7E2C937089

ServletResponse对象

用于封装服务器的响应信息

1、设置响应头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P0Lg0k1R-1617958861918)(images/response响应.png)]

是由服务器传递给客户端浏览器的额外数据

response.setHeader("Content-Disposition","attachment;filename=abc.png");  //告知浏览器,以附件的方式打开响应数据

Expires: 日期数据   用于设置响应内容类型的缓存过期时间
Date now=new Date();
        //例如允许缓存1个月
now=new Date(now.getTime()+1*30*24*60*60*1000);
response.setDateHeader("Expires",now.getTime());

//是否开启缓存
响应头名称Cache-Control,值no-cache表示不缓存数据
写法2:Pragma的值为no-cache时表示禁用缓存,这是一个逐步需要放弃的写法

//禁用缓存
response.setHeader("Pragma","no-cache");  //针对http1.0禁用缓存
response.setHeader("Cache-Control","no-cache"); //针对http1.1禁用缓存
response.setDateHeader("Expires",0);  //表示立即过期
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值