【Java WEB】Servlet&注解开发

个人主页:Hello Code.
本文专栏:《Java WEB从入门到实战》
Java WEB完整内容请点击前往Java WEB从入门到实战 查看
如有问题,欢迎指正,一起学习~~



Servlet

概述

  • Servlet是运行在Java服务器端的程序,用于接收和响应来自客户端基于HTTP协议的请求
  • 是一个接口(javax.servlet.Servlet),常用实现类:GenericServlet、HttpServlet(继承自GenericServlet)
  • 所有的客户端请求都会经过service方法进行处理
  • 初始化会调用init方法,销毁时会调用destroy方法

执行过程

  1. 客户端浏览器向Tomcat服务器发起请求
  2. Tomcat服务器解析请求地址(URL)
  3. Tomcat根据地址找到对应的项目
  4. 找到项目中的web.xml文件
  5. 解析请求资源地址(URI)
  6. 找到项目的资源(对应的Java类)
  7. 执行service方法,响应给客户端浏览器

关系视图

在这里插入图片描述

实现方式

  1. 实现Servlet接口,实现所有的抽象方法,该方式支持最大程度的自定义
  2. 继承GenericServlet抽象类,必须重写service方法,其他方法可选择重写。该方式让我们开发servlet变得简单。但是这种方式与HTTP协议无关
  3. 继承HttpServlet抽象类,需要重写doGetdoPost方法。该方式表示请求和响应都需要和HTTP协议相关

生命周期

  • 对象的生命周期,就是对象从出生到死亡的过程。即:出生 =》活着 =》死亡。官方说法是对象创建到销毁的过程
  • 出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,将对象放到内存中
  • 活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行service方法
  • 死亡:当服务器停止时,或者服务器宕机时,对象死亡

出生对应的是init方法
活着对应的是service方法(doGetdoPost方法)
死亡对应的是destroy方法

结论: Servlet对象只会创建一次,销毁一次。所以Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么就称他为单例模式

线程安全问题

  • 由于Servlet采用的是单例设计模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全
  • 模拟用户登录功能来查看Servlet线程是否安全

结论: 一个浏览器代表一个线程,多个浏览器代表多个线程。按理说应该是每个浏览器查看的都是自己的信息。但结果浏览器中数据混乱。因此,我们可以认为Servlet是线程不安全的!

解决: 定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其它时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全的问题了,可以将其定义到doGet或doPost方法内或者使用同步功能即可

映射方式

  1. 具体名称的方式。访问的资源路径必须和映射配置完全相同 【常用】

    <servlet>
    	<servlet-name>Demo</servlet-name>
    	<servlet-class>study.servlet.Demo</servlet-class>
    </servlet>
    <servlet-mapping>
    	<servlet-name>Demo</servlet-name>
    	<url-pattern>/Demo</url-pattern>
    </servlet-mapping>
    
  2. / 开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么

    <servlet>
            <servlet-name>Demo2</servlet-name>
            <servlet-class>study.servlet.Demo2</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>Demo2</servlet-name>
            <url-pattern>/Demo2/*</url-pattern>
        </servlet-mapping>
    
  3. 通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径

    <servlet>
            <servlet-name>Demo2</servlet-name>
            <servlet-class>study.servlet.Demo2</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>Demo2</servlet-name>
            <url-pattern>*.test</url-pattern>
        </servlet-mapping>
    

注意: 优先级问题。越是具体的优先级越高,越是模糊的 优先级越低。第一种 > 第二种 > 第三种

多路径映射

  • 我们可以给一个Servlet配置多个访问映射,从而根据不同的请求路径来实现不同的功能
  • 场景分析
    如果访问的资源路径是/vip,商品价格打9折
    如果访问的资源路径是/vvip,商品价格打5折
    如果访问的资源路径是其它,商品价格不打折
  • 采用第二种映射方式实现多路径映射(/ + 通配符)
package study.servlet;

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;

public class Demo3 extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取浏览器路径
        String requestURI = req.getRequestURI();
        // 分隔路径
        String path = requestURI.substring(requestURI.lastIndexOf("/"));
        // 路径判断,区分价格
        if(path.equals("/vip")){
    
    
            System.out.println("100元");
        }else if(path.equals("/vvip")){
    
    
            System.out.println("200元");
        }else System.out.println("300元");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        doGet(req,resp);
    }
}

创建时机

  1. 第一次访问时创建
    • 优势:减少对服务器内存的浪费。提高了服务器启动的效率
    • 弊端:如果有一些要在应用加载时就做的初始化操作无法完成
  2. 服务器加载时创建
    • 优势:提前创建好对象,提高了首次执行的效率。可以完成一些应用加载时要完成的初始化操作
    • 弊端:对服务器内存占用较多,影响了服务器启动的效率
  3. 修改Servlet创建时机。在<servlet>标签中,添加<load-on-startup>标签
    <load-on-startup>1</load-on-startup>
    正整数代表服务器加载时创建,值越小,优先级越高。负整数或不写代表第一次访问时创建

默认Servlet

默认Servlet是由服务器提供的一个Servlet。它配置在 Tomcat 的 conf 目录中的 web.xml 中

在这里插入图片描述

  • 它的映射路径是<url-pattern>/<url-pattern>
  • 我们在发送请求时,首先会在我们项目中的web.xml 中查找映射配置,找到则执行。
  • 但是当找不到对应的Servlet 路径时,就去找默认的 Servlet ,由默认Servlet 处理。所以,一切都是Servlet

ServletConfig

  • ServletConfig 是Servlet 的配置参数对象,在Servlet 的规范中,允许为每一个Servlet 都提供一些 初始化的配置。所以,每个Servlet 都有一个自己的ServletConfig
  • 作用:在Servlet 的初始化时,把一些配置信息(键值对的形式)传递给Servlet
  • 生命周期:和Servlet 相同

配置方式

  • <servlet>标签中,通过 <init-param>标签来配置。有两个子标签
    • <param-name>:代表初始化参数的key
    • <param-value>:代表初始化参数的value
<servlet>
	<servlet-name>servletConfigDemo</servlet-name>
	<servlet-class>study.servlet.servletConfigDemo</servlet-class>
      
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
	<init-param>
		<param-name>desc</param-name>
		<param-value>This is ServletConfig</param-value>
	</init-param>
</servlet>

常用方法

返回值 方法名 说明
String getInitParameter(String name) 根据参数名称获取参数的值
Enumeration getInitParameterNames() 获取所有参数名称的枚举
String getServletName() 获取Servlet的名称
ServletContext getServletContext() 获取ServletContext对象
  • 通过init方法,来对ServletConfig对象进行赋值
    private ServletConfig config;
    public void init(ServletConfig config) throws ServletException{
          
          
        this.config = config;
    }
    
  • 枚举项遍历
    Enumeration<String> keys = config.getInitParameterNames();
    while(keys.hasMoreElements()){
          
          
        String key = keys.nextElement();
        String value = config.getInitParameter(key);
        System.out.println(key + "--" + value);
    }
    
// ServletConfigDemo测试代码
package study.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.util.Enumeration;

public class ServletConfigDemo extends HttpServlet {
    
    
    private ServletConfig config;
    // 通过init方法对config赋值,获取ServletConfig对象
    @Override
    public void init(ServletConfig config) throws ServletException {
    
    
        this.config = config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 根据name获取value
        String encoding = config.getInitParameter("encoding");
        System.out.println("encoding:" + encoding);

        // 获取所有name并遍历
        Enumeration<String> names = config.getInitParameterNames();
        while(names.hasMoreElements()){
    
    
            String name = names.nextElement();
            String value = config.getInitParameter(name);
            System.out.println(name + "---" + value);
        }
        // 获取Servlet-name
        String sname = config.getServletName();
        System.out.println("Servlet-name:" + sname);
        // 获取ServletContext对象
        ServletContext servletContext = config.getServletContext();
        System.out.println(servletContext);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        doGet(req,resp);
    }
}

ServletContext

  • ServletContext 是应用上下文对象(应用域对象)。每一个应用中只有一个 ServletContext 对象
  • 作用:可以配置和获得应用的全局初始化参数,可以实现Servlet 之间的数据共享
  • 生命周期:应用一加载则创建,应用被停止则销毁

域对象

  • 域对象指的是对象有作用域,也就是作用范围。域对象可以实现数据的共享。不同作用范围的域对象,共享数据的能力也不一样
  • 在Servlet 规范中,一共有4个域对象。ServletContext 就是其中的一个。他也是web 应用中最大的作用域,也叫 application 域。它可以实现整个应用之间的数据共享

配置方式

  • <web-app>标签中,通过<context-param>标签来配置。有两个子标签
  • <param-name>:代表全局初始化参数的key
  • <param-value>:代表全局初始化参数的value
<context-param>
	<param-name>globalencoding</param-name>
	<param-value>UTF-8</param-value>
</context-param>
<context-param>
	<param-name>globaldesc</param-name>
	<param-value>This is ServletContext</param-value>
</context-param>

常用方法

返回值 方法名 说明
String getInitParameter(String name) 根据名称获取全局配置的参数
String getContextPath() 获取当前应用的访问虚拟目录
String getRealPath(String path) 根据虚拟目录获取应用部署的磁盘绝对路径

HttpServlet类继承自GenericServlet
GenericServlet类中有getServletContext方法,可以直接获取ServletContext对象

返回值 方法名 说明
void setAttribute(String name, Object value) 向应用域对象中存储数据
Object getAttribute(String name) 通过名称获取应用域对象中的数据
void removeAttribute(String name) 通过名称移除应用域对象中的数据
package study.servlet;

import javax.servlet.ServletContext;
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.util.Enumeration;

public class ServletContextDemo extends HttpServlet{
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        ServletContext servletContext = req.getServletContext();
        Enumeration<String> names = servletContext.getInitParameterNames();
        while(names.hasMoreElements()){
    
    
            String name = names.nextElement();
            String value = servletContext.getInitParameter(name);
            System.out.println(name + "====" + value);

        }
        resp.setContentType("text/html;charset=UTF-8");
        String contextPath = servletContext.getContextPath();
        String realPath = servletContext.getRealPath(contextPath);
        PrintWriter pw = resp.getWriter();
        pw.write("虚拟目录为:" + contextPath + "<br>");
        pw.write("真实目录为:" + realPath);

        servletContext.setAttribute("use","lisi");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        doGet(req,resp);
    }
}

注解开发

Servlet3.0 规范

  • 我们使用的是Tomcat 9版本。JavaEE规范要求是8.对应的Servlet 版本应该是4.x 版本。但是,在企业开发中,稳定要远比追求新版本重要。所以我们会降低版本使用,用的是Servlet 3.1版本。
  • 其实我们之前的操作全部是基于 Servlet 2.5版本规范的,也就是借助于配置文件的方式。后来随着软件开发逐步的演变,基于注解的配置开始流行。而Servlet 3.0版本也就开始支持注解开发了
  • Servlet 3.0版本既保留了2.5版本的配置方式,同时有支持了全新的注解配置方式。它可以完全不需要 web.xml 配置文件,就能实现 Servlet 的配置,同时还有一些其他的新特性。

自动注解开发

实现步骤

  1. 创建一个 web 项目
  2. 定义一个类,继承HttpServlet
  3. 重写 doGet 和 doPost方法
  4. 在类上使用@WebServlet 注解并配置Servlet
  5. 部署并启动项目
  6. 通过浏览器测试
@WebServlet("/servletDemo")
public class ServletDemo extends HttpServlet{
    
    
    
}

在这里插入图片描述

手动创建容器(了解)

  • Servlet 3.0 规范除了使用自动注解的配置方式外,还支持手动创建 Servlet 容器的方式。如果使用必须遵循其编写规范。在3.0 版本加入了一个新的接口:ServletContainerInitializer,需要重写onStartup方法

步骤

  1. 定义一个类,继承HttpServlet

  2. 重写 doGet 和 doPost方法

  3. 定义一个类,实现ServletContainerInitializer接口

  4. 在src 目录下创建一个META-INF的包

  5. 在 META-INF 包下创建一个services 的包

  6. 在 services 包下创建一个 javax.servlet.ServletContainerInitializer 的文件

  7. 文件中的内容为容器实现类的全类名

  8. 在容器实现类中的 onStartup 方法中完成注册 Servlet

    public void onStartup(Set<Class<?>> set, ServletContext servletContext){
          
          
        // 1.创建ServletDemo类的对象
        ServletDemo servletDemo = new ServletDemo();
        // 2. 在ServletContext 对象中添加Servlet,并得到Servlet的动态配置对象
        ServletRegistration.Dynamic registration = servletContext.addServlet("ServletDemo", servletDemo);
        // 3. 配置Servlet
        registration.addMapping("/servletDemo");	// 映射访问资源的路径
        registration.setLoadOnStartup(1);	// 设置Servlet加载时机
    }
    
  9. 部署并启动项目

  10. 通过浏览器测试


学生管理系统1

  • 需求:添加学生信息到Java服务器中的本地文件

步骤

  1. 创建一个 web 项目
  2. 创建一个用于保存学生信息的html文件
  3. 创建一个类,继承HttpServlet
  4. 重写doGet 和 doPost 方法
  5. 在web.xml 文件中修改默认主页和配置 Servlet
  6. 在doGet 方法中接收表单数据保存到文件中,并响应给浏览器结果
  7. 部署并启动项目
  8. 通过浏览器测试

获取表单数据
req.getParameter(name值):就可以通过HttpServletRequest 对象中的方法 通过表单的name属性获取到对应的表单数据

响应数据
PrintWriter pw = resp.getWriter():通过 HttpServletResponse 对象中的方法获取输出流对象
pw.println("Save Success"):将指定内容响应到浏览器中

<!--studentAdd.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加学生</title>
</head>
<body>
    <form action="/add" method="get">
        姓名:<input type="text" name="username"> <br>
        年龄:<input type="number" name="age"> <br>
        成绩:<input type="number" name="score">   <br>
        <button type="submit">添加</button>
    </form>
</body>
</html>
<!--web.xml-->
<?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">
    <!--配置首页-->
    <welcome-file-list>
        <welcome-file>/studentAdd.html</welcome-file>
    </welcome-file-list>
    <!--Servlet声明-->
    <servlet>
        <servlet-name>StudentServlet</servlet-name>
        <servlet-class>studentServlet.add</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>StudentServlet</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>
</web-app>
// add.java
package studentServlet;

import studentServlet.bean.Student;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class add extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取对应表单内容
        String username = req.getParameter("username");
        String age = req.getParameter("age");
        String score = req.getParameter("score");
        // 封装学生对象
        Student stu = new Student(username, Integer.parseInt(age), Integer.parseInt(score));
        // 保存到本地文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\Java\\code\\StudentServlet\\stu.txt",true));
        bw.write(stu.getUsername() + "," + stu.getAge() + "," + stu.getScore());
        bw.newLine();
        bw.close();

        // 响应给浏览器
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter pw = resp.getWriter();
        pw.println("添加成功,将在3秒后跳转到首页!!!");
        resp.setHeader("Refresh","3;url=/index.html");
        pw.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        doGet(req, resp);
    }
}

推荐阅读:【Java WEB】请求对象&响应对象

猜你喜欢

转载自blog.csdn.net/qq_24980365/article/details/123405211