Servlet

Servlet概述

什么是Servlet

    就是一个运行在WEB服务器上的小的Java程序,用来接收和响应从客户端发送过来的请求,通常使用HTTP协议。
    Servlet就是SUN公司提供的一个动态网页开发技术。

Servlet的作用

    用来处理从客户端浏览器发送的请求,并且可以对请求作出响应.

使用Servlet

    编写一个类实现Servlet接口。
    将编写的这个类配置到服务器中。

编写类:
public class ServletDemo1 implements Servlet{

    @Override
    /**
     * 为用户处理请求和响应的方法.
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.getWriter().println("Hello Servlet...");
    }
...
}

配置:
  <!-- 配置Servlet -->
  <servlet>
    <!-- Servlet的名称 -->
    <servlet-name>test1</servlet-name>
    <!-- SErvlet的全路径 -->
    <servlet-class>com.cnblogs.a_servlet.ServletDemo1</servlet-class>
  </servlet>
  
  <!-- Servlet的映射 -->
  <servlet-mapping>
    <!-- Servlet的名称 -->
    <servlet-name>test1</servlet-name>
    <!-- Servlet的访问路径 -->
    <url-pattern>/ServletDemo1</url-pattern>
  </servlet-mapping>

访问:http://localhost:8080/day09/ServletDemo1
servlet简单编写

使用ServletRequest接收参数

String getParameter(String name);         ---用于接收一个名称对应一个值的数据.
String[] getParameterValues(String name); ---用于接收一个名称对应多个值的数据.
Map getParameterMap();                    ---用于接收表单中的所有的数据,Map的key是表单提交的参数名称,Map的value是提交参数的值.
Enumeration getParameterNames()           ---用于获取表单中提交的所有的参数的名称.

Servlet的访问流程

1、http://localhost:8080/ 找到对应地址的 tomcat,tomcat 解析 url 中的 day09 就找到 部署的 day09 应用。
2、找到 day09 之后,就可以找到 day09/WEB-INF/web.xml。
3、在web.xml 中 tomcat 找 servlet-mapping下的 url-pattern,把/ServletDemo1 和 url-patten 的内容比较。
4、找到对应的 url-patten,就可以找到 servlet-name 标签,读取 servlet-name 里面的内容。再依照 servlet-name 寻找 servlet 标签对应的 servlet-calss。
5、tomcat 实例化 servlet-class 路径的对象。然后调用改 servlet 对象的 service 方法。

Servlet的实现的关系

Servlet:接口
   |
GenericServlet:通用的Servlet
   |
HttpServlet:HttpServlet
  编写一个类继承HttpServlet,重写doGet和doPost方法.
  配置

Servlet 的运行过程

Servlet 程序是由 WEB 服务器调用,web 服务器收到客户端的 Servlet 访问请求后:
  ① Web 服务器首先检查是否已经装载并创建了该 Servlet 的实例对象。如果是,则直接执行第④步,否则,执行第②步。
  ② 装载并创建该 Servlet 的一个实例对象。 
  ③ 调用 Servlet 实例对象的 init() 方法。
  ④ 创建一个用于封装 HTTP 请求消息的 HttpServletRequest 对象和一个代表 HTTP 响应消息的 HttpServletResponse 对象,然后调用 Servlet 的 service() 方法并将请求和响应对象作为参数传递进去。
  ⑤ WEB 应用程序被停止或重新启动之前,Servlet 引擎将卸载 Servlet,并在卸载之前调用 Servlet 的 destroy() 方法。

servlet 的生命周期

Servlet 生命周期:Servlet 从创建到销毁的过程。
  何时创建:用户第一次访问 Servlet 创建 Servlet 的实例。
  何时销毁:当项目从服务器中移除的时候,或者关闭服务器的时候。
用户第一次访问 Servlet 的时候,服务器会创建一个 Servlet 的实例,那么 Servlet 中 init 方法就会执行。任何一次请求服务器都会创建一个新的线程访问 Servlet 中的 service 的方法。在 service 方法内部根据请求的方式的不同调用doXXX的方法 。(get 请求调用 doGe,post 请求调用 doPost)。当 Servlet 中服务器中移除掉,或者关闭服务器,Servlet 的实例就会被销毁,那么 destroy 方法就会执行。

Servlet 的相关的配置

 3.1 启动时创建 Servlet

Servlet 默认是在第一次访问的时候创建的。现在让 Servlet 在服务器启动的时候创建好,对 Servlet 进行配置,
在 web.xml 中在 <servlet></servlet> 标签中配置:
<load-on-startup>2</load-on-startup>  --- 传入正整数,整数越小,被创建的优先级就越高。

3.3 url-pattern 的配置

url-pattern 配置方式共有三种:

完全路径匹配 :以 / 开始。                      例如:    /ServletDemo4 , /aaa/ServletDemo5 , /aaa/bbb/ServletDemo6
目录匹配:以 / 开始 需要以 * 结束。     例如: /* ,/aaa/* ,/aaa/bbb/*
扩展名匹配 :不能以 / 开始 以 * 开始的。  例如: *.do , *.action 

完全路径匹配 > 目录匹配 > 扩展名匹配

3.5 ServletContext 对象

Servlet 对象继承 HttpServlet/GenericServlet,可以用 this.getServletContext() 获得 ServletContext 对象。它可作为域对象,用来存储数据。例:this.getServletContext().setAttribute("count", count); ServletConfig 对象中维护了 ServletContext 对象的引用,也可以这样 this.getServletConfig().getServletContext().setAttribute();

void setAttribute(String name, Object object)  --向ServletContext中存入数据
Object getAttribute(String name)  --从ServletContext中获取数据
void removeAttribute(String name)  --从ServletContext中移除数据

3.6 ServletContext 的作用

1.用来获得全局初始化参数。
2.用来获得文件的MIME的类型。
3.作为域对象存取数据。
  ServletContext 是一个域对象。
    * 作用范围:整个 web 工程
    * 创建:服务器启动的时候,tomcat 服务器为每个 web 项目创建一个单独 ServletContext 对象
    * 销毁:服务器关闭的时候,或者项目从服务器中移除的时候。
4.用来读取 web 项目下的文件。

/**
 * 登录代码的Servlet
 */
public class UserCountServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void init() throws ServletException {
        // 初始化一个变量count的值为0.
        int count = 0;
        // 将这个值存入到ServletContext中.
        this.getServletContext().setAttribute("count", count);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            response.setContentType("text/html;charset=UTF-8");
            // 1.接收表单提交的参数.
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            // 2.封装到实体对象中.
            User user = new User();
            user.setUsername(username);
            user.setPassword(password);
            
            // 3.调用业务层处理数据.
            UserService userService = new UserService();
        
            User existUser = userService.login(user);
            // 4.根据处理结果显示信息(页面跳转).
            if(existUser == null){
                // 登录失败
                response.getWriter().println("<h1>登录失败:用户名或密码错误!</h1>");
            }else{
                // 登录成功
                
                // 记录次数:
                int count = (int) this.getServletContext().getAttribute("count");
                count++;
                this.getServletContext().setAttribute("count", count);
                
                response.getWriter().println("<h1>登录成功:您好:"+existUser.getNickname()+"</h1>");
                response.getWriter().println("<h3>页面将在5秒后跳转!</h3>");
                response.setHeader("Refresh", "5;url=/day09/CountServlet");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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


public class CountServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获得Count的值。
        response.setContentType("text/html;charset=UTF-8");
        int count = (int) this.getServletContext().getAttribute("count");
        response.getWriter().println("<h1>您是第"+count+"位登录成功的用户!</h1>");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
servletContext记录访问人次

ServletContext 读取 WEB 项目文件

ServletContext context = this.getServletContext();
InputStream is = context. getResourceAsStream("/WEB-INF/classes/db.properties");

ServletContext context = this.getServletContext();
String realPath = context.getRealPath("/WEB-INF/classes/db.properties");
InputStream is = new FileInputStream(realPath);

    /**
     * 使用ServletContext中的getResourceAsStream读取.
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void test2() throws FileNotFoundException, IOException {
    
        // 获得ServletContext:
        ServletContext context = this.getServletContext();
        InputStream is = context. getResourceAsStream("/WEB-INF/classes/db.properties");
        Properties properties = new Properties();
        properties.load(is);
        
        String driverClass = properties.getProperty("driverClass");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        
        System.out.println(driverClass);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
    }

    /**
     * 使用ServletContext中的getRealPath读取.
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void test3() throws FileNotFoundException, IOException {
        // 获得ServletContext:
        ServletContext context = this.getServletContext();
        String realPath = context.getRealPath("/WEB-INF/classes/db.properties");
        // 获得该文件的磁盘绝对路径.
        System.out.println(realPath);
        InputStream is = new FileInputStream(realPath);
        
        Properties properties = new Properties();
        properties.load(is);
        
        String driverClass = properties.getProperty("driverClass");
        String url = properties.getProperty("url");
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        
        System.out.println(driverClass);
        System.out.println(url);
        System.out.println(username);
        System.out.println(password);
    }
servletContext读取web文件

类加载器读取文件:
InputStream is = ReadFileUtils.class.getClassLoader().getResourceAsStream("db.properties");

Servlet 的线程安全问题 

一种做法是给 Servlet 对象加了一把锁,这种做法虽然解决了线程安全问题,但是必须按先后顺序排队轮流访问,效率极低,不可能使用。

一种是让 Servlet 去实现一个 SingleThreadModel 接口,如果某个 Servlet 实现了 SingleThreadModel 接口,那么 Servlet 引擎将以单线程模式来调用其 service 方法。
  查看 Sevlet 的 API 可以看到,SingleThreadModel 接口中没有定义任何方法和常量,在 Java 中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是"Serializable",这个接口也是没有定义任何方法和常量的,标记接口在 Java 中有什么用呢?主要作用就是给某个对象打上一个标志,告诉 JVM,这个对象可以做什么,比如实现了"Serializable"接口的类的对象就可以被序列化,还有一个"Cloneable"接口,这个也是一个标记接口,在默认情况下,Java 中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了"Cloneable"接口,那么对象就可以被克隆了。

  让 Servlet 实现了 SingleThreadModel 接口,只要在 Servlet 类的定义中增加实现 SingleThreadModel 接口的声明即可。  
  对于实现了 SingleThreadModel 接口的 Servlet,Servlet 引擎仍然支持对该 Servlet 的多线程并发访问,其采用的方式是产生多个 Servlet 实例对象,并发的每个线程分别调用一个独立的 Servlet 实例对象。
  实现 SingleThreadModel 接口并不能真正解决 Servlet 的线程安全问题,因为 Servlet 引擎会创建多个 Servlet 实例对象,而真正意义上解决多线程安全问题是指一个 Servlet 实例对象被多个线程同时调用的问题。事实上,在 Servlet API 2.4 中,已经将 SingleThreadModel 标记为 Deprecated(过时的)。

 最合适的一种方式是,和 threadlocal 一起来处理,链接中的三篇皆为采用此方式的处理例子示例。不再赘述。

https://blog.csdn.net/enterys/article/details/49465295

https://blog.csdn.net/yx0628/article/details/21172473

https://blog.csdn.net/u014421677/article/details/51757707

原文地址:https://www.cnblogs.com/boomoom/p/10134887.html