【JavaWeb】Servlet

发布时间:2024年01月22日


动态资源与静态资源:

静态资源 :

  • 无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源.
  • 例如:html css js img ,音频文件和视频文件

动态资源

  • 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,
  • 运行时动态生成,例如Servlet,Thymeleaf … …
  • 动态资源指的不是视图上的动画效果或者是简单的人机交互效果

举例

  • 前端页面 直接验证 用户名密码格式是否准确 : 静态资源
  • 需要验证用户名是否已经存在 : 动态资源

Servlet是什么?

Servlet (server applet) 是运行在服务端(tomcat)的Java小程序
是sun公司提供一套定义动态资源规范;
从代码层面上来讲Servlet就是一个接口

  • 用来接收、处理客户端请求、响应给浏览器的动态资源。

    • 在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。
    • 我们可以把Servlet称为Web应用中的控制器
  • Servlet 是能处理客户端请求并做出响应的一套技术标准。

    • 不是所有的JAVA类都能用于处理客户端请求
  • Servlet是运行在服务端的,所以 Servlet 必须在WEB项目中开发且在Tomcat这样的服务容器中运行

Servlet 流程:
servlet


一、Servlet开发流程

  1. web项目,配置tomcat,添加为项目依赖 (tomcat依赖包括servlet的jar包)
  2. 前端 创建 HTML 要提交的表单 信息 method/action
  3. 后端 创建类 继承 HttpServlet
  4. 类重写servers方法 ,实现业务处理代码
  5. 路径匹配 :在web.xml中,配置servlet对应的请求映射路径

简单需求与实现

校验注册时,用户名是否被占用:
通过客户端向一个Servlet发送请求,携带username,如果用户名是’dougwake’,则向客户端响应 NO,如果是其他,响应YES

  1. HTML 表单
<body>
<h1>hello </h1>
<!-- get or post -->
<form method="get" action="userServlet">
    用户名: <input type="text" name="username"/>
    <input type="submit" value="校验"/>
</form>
</body>
  1. servlet类 继承 HttpServlet 重写方法service 处理业务逻辑
public class UserServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.从request对象中获取请求中的任何信息(username参数)
        String username = req.getParameter("username");

        //2.处理业务逻辑
        String info = "Yes";
        if ("dougwake".equals(username)) {
            info = "NO";
        }

        //3.将要响应的数据放入response
           // 手动设置响应头
        //resp.setHeader("Content-Type","text/css");
        resp.setContentType("text/html");
        
        PrintWriter writer = resp.getWriter();
        writer.write(info);
    }
}

Content-Type 告诉客户端响应的数据类型是什么,就用什么类型解析。
1

  1. web.xml配置servlet关联路径
<!--
  1. 配置servlet类,并且起一个别名
  2. servlet-class  对应要实例化的servlet类  (反射方式)
  3. servlet-name  用于关联请求的映射路径

  url-pattern 与前端关联的action (举例: 就是/aaa )
  -->

    <servlet>
        <servlet-name>userServlet</servlet-name>
        <servlet-class>com.wake.servlet.UserServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>userServlet</servlet-name>
        <url-pattern>/userServlet</url-pattern>
    </servlet-mapping>

url-pattern匹配的特殊写法(基本是使用精确匹配) :

    <servlet>
        <servlet-name>servletTest1</servlet-name>
        <servlet-class>com.wake.servlet.ServletTest1</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>servletTest1</servlet-name>
<!-- 可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern-->
        <url-pattern>/s1</url-pattern>
        <!-- <url-pattern>/userServlet2</url-pattern>-->
        <!--
            /        表示通配所有资源,不包括jsp文件
            /*       表示通配所有资源,包括jsp文件
            /a/*     匹配所有以a前缀的映射路径
            *.action 匹配所有以action为后缀的映射路径
        -->
        <!-- <url-pattern>/*</url-pattern>-->
    </servlet-mapping>

二、Servlet注解方式配置

用了注解 就不用xml配置

@WebServlet("/s6")
public class ServletTest1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servletTest执行!");
    }
}

@WebServlet(“/s6”) 不写/ 会报错

详细写法:

@WebServlet(
        name = "userServlet",
        //value = "/user",
        urlPatterns = {"/userServlet1","/userServlet2","/userServlet"},
        initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},
        loadOnStartup = 6
)
public class UserServlet  extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String encoding = getServletConfig().getInitParameter("encoding");
        System.out.println(encoding);
        // 获取请求中的参数
        String username = req.getParameter("username");
        if("atguigu".equals(username)){
            //通过响应对象响应信息
            resp.getWriter().write("NO");
        }else{
            resp.getWriter().write("YES");
        }
    }
}

三、Servlet生命周期

Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。

越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。

Servlet主要的生命周期执行特点:

生命周期对应方法执行时机执行次数
构造对象构造器第一次请求或者容器启动1
初始化init()构造完毕后1
处理服务service(HttpServletRequest req,HttpServletResponse resp)每次请求多次
销毁destory()容器关闭1
public class ServletLifeCycle  extends HttpServlet {
    public ServletLifeCycle(){
        System.out.println("构造器");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("初始化方法");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("service方法");
    }

    @Override
    public void destroy() {
        System.out.println("销毁方法");
    }
}
    <servlet>
        <servlet-name>servletLifeCycle</servlet-name>
        <servlet-class>com.atguigu.servlet.ServletLifeCycle</servlet-class>
        <!--load-on-startup
            如果配置的是正整数则表示容器在启动时就要实例化Servlet,
            数字表示的是实例化的顺序
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletLifeCycle</servlet-name>
        <url-pattern>/servletLiftCycle</url-pattern>
    </servlet-mapping>

生命周期总结:

1.Servlet对象在容器Tomcat中是单例的
2. Servlet的成员变量在多个线程之中的共享的
3. 所以不建议在service中修改成员变量 , 在并发请求时,会引发线程安全问题!
4. default-servlet 用于加载静态资源的servlet , 默认随服务启动,默认启动序号为1
5. Tomcat容器中,已经定义了一些随系统启动实例化的servlet,自定义的servlet的load-on-startup尽量不要占用数字1-5
6. load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复
7. 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程

四、Servlet继承结构

图解:
1

自定义Servlet中,必须要对处理请求的方法进行重写

  • 要么重写service方法
  • 要么重写doGet/doPost方法

五、ServletConfig和ServletContext

获取 xml 相应的配置文件信息

5.1 ServletConfig

  • Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象
  • 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性
    1
    ServletConfig是一个接口
方法名作用
getServletName()获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称
getServletContext()获取ServletContext对象
getInitParameter()获取配置Servlet时设置的『初始化参数』,根据名字获取值
getInitParameterNames()获取所有初始化参数名组成的Enumeration对象

5.2 ServletContext

  • ServletContext对象有称呼为上下文对象,或者叫应用域对象(后面统一讲解域对象)
  • 容器会为每个app创建一个独立的唯一的ServletContext对象(单例)
  • ServletContext对象为所有的Servlet所共享
  • ServletContext可以为所有的Servlet提供初始配置参数
    2
    ServletContext的使用:
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       
        // 从ServletContext中获取为所有的Servlet准备的参数
        ServletContext servletContext = this.getServletContext();
        String valueA = servletContext.getInitParameter("paramA");
        System.out.println("paramA:"+valueA);
        // 获取所有参数名
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        // 迭代并获取参数名
        while (initParameterNames.hasMoreElements()) {
            String paramaterName = initParameterNames.nextElement();
            System.out.println(paramaterName+":"+servletContext.getInitParameter(paramaterName));
        }
    }
 }

配置文件:

    <context-param>
        <param-name>paramA</param-name>
        <param-value>valueA</param-value>
    </context-param>
    <context-param>
        <param-name>paramB</param-name>
        <param-value>valueB</param-value>
    </context-param>

1

5.2.1 servletContext 获取文件路径和上下文

获取资源的真实路径 :

String realPath = servletContext.getRealPath("资源在web目录中的路径");

1

        ServletContext application = this.getServletContext();
        String path = application.getRealPath("upload");
        System.out.println(path);

结果
获取项目的上下文路径 :

String contextPath = servletContext.getContextPath();
System.out.println(contextPath);

1
1

5.2.2 servletContext 域对象相关API

  • 一些用于存储数据传递数据的对象,传递数据不同的范围,我们称之为不同的域

    • 不同的域对象代表不同的域,共享数据的范围也不同
  • ServletContext代表应用,所以ServletContext域也叫作应用域,

    • 是webapp中最大的域,可以在本应用内实现数据的共享和传递
  • webapp中的三大域对象,分别是应用域,会话域,请求域

三大域对象都具有的API如下:

API功能解释
void setAttribute(String key,Object value);向域中存储/修改数据
Object getAttribute(String key);获得域中的数据
void removeAttribute(String key);移除域中的数据

举例:
可以将servlet1 的数据 存入 域中
使用servlet2 or servlet3 … 在域中拿数据

// servlet1 
        ServletContext application = this.getServletContext();
        application.setAttribute("ak","value");
// servlet2
        ServletContext application = this.getServletContext();
        String ak = (String) application.getAttribute("ak");
        System.out.println(ak);

result

六、HttpServletRequest

  • HttpServletRequest是一个接口,其父接口是ServletRequest
  • HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入
  • HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得

常见API :

  • 获取请求行信息相关(方式,请求的url,协议及版本)
API功能解释
StringBuffer getRequestURL();获取客户端请求的url
String getRequestURI();获取客户端请求项目中的具体资源
int getServerPort();获取客户端发送请求时的端口
int getLocalPort();获取本应用在所在容器的端口
int getRemotePort();获取客户端程序的端口
String getScheme();获取请求协议
String getProtocol();获取请求协议及版本号
String getMethod();获取请求方式
  • 获得请求头信息相关
API功能解释
String getHeader(String headerName);根据头名称获取请求头
Enumeration getHeaderNames();获取所有的请求头名字
String getContentType();获取content-type请求头
  • 获得请求参数相关
API功能解释
String getParameter(String parameterName);根据请求参数名获取请求单个参数值
String[] getParameterValues(String parameterName);根据请求参数名获取请求多个参数值数组
Enumeration getParameterNames();获取所有请求参数名
Map<String, String[]> getParameterMap();获取所有请求参数的键值对集合
BufferedReader getReader() throws IOException;获取读取请求体的字符输入流
ServletInputStream getInputStream() throws IOException;获取读取请求体的字节输入流
int getContentLength();获得请求体长度的字节数

juli
2
1

  • 其他API
API功能解释
String getServletPath();获取请求的Servlet的映射路径
ServletContext getServletContext();获取ServletContext对象
Cookie[] getCookies();获取请求中的所有cookie
HttpSession getSession();获取Session对象
void setCharacterEncoding(String encoding) ;设置请求体字符集

七、HttpServletResponse

  • HttpServletResponse是一个接口,其父接口是ServletResponse
  • HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入
  • HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息

常见API:

  • 设置响应行相关
API功能解释
void setStatus(int code);设置响应状态码
  • 设置响应头相关
API功能解释
void setHeader(String headerName, String headerValue);设置/修改响应头键值对
void setContentType(String contentType);设置content-type响应头及响应字符集(设置MIME类型)
  • 设置响应体相关
API功能解释
PrintWriter getWriter() throws IOException;获得向响应体放入信息的字符输出流
ServletOutputStream getOutputStream() throws IOException;获得向响应体放入信息的字节输出流
void setContentLength(int length);设置响应体的字节长度,其实就是在设置content-length响应头
  • 其他API
API功能解释
void sendError(int code, String message) throws IOException;向客户端响应错误信息的方法,需要指定响应码和响应信息
void addCookie(Cookie cookie);向响应体中增加cookie
void setCharacterEncoding(String encoding);设置响应体字符集

MIME类型:

  • MIME类型,可以理解为文档类型,用户表示传递的数据是属于什么类型的文档
  • 浏览器可以根据MIME类型决定该用什么样的方式解析接收到的响应体数据
  • 可以这样理解: 前后端交互数据时,告诉对方发给对方的是 html/css/js/图片/声音/视频/… …
  • tomcat/conf/web.xml中配置了常见文件的拓展名和MIMIE类型的对应关系
  • 常见的MIME类型举例如下:
文件拓展名MIME类型
.htmltext/html
.csstext/css
.jsapplication/javascript
.png /.jpeg/.jpg/… …image/jpeg
.mp3/.mpe/.mpeg/ … …audio/mpeg
.mp4video/mp4
.m1v/.m1v/.m2v/.mpe/… …video/mpeg

八、请求转发和响应重定向

  • 请求转发和响应重定向是web应用中间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段
  • 请求转发通过HttpServletRequest实现
  • 响应重定向通过HttpServletResponse实现

总结:

实现页面跳转的情况,优先使用响应重定向

8.1 请求转发

举例来说:
servletA 接收 到从客户端发送来的请求 后将生成的请求、响应对象转发给servletB,由B来执行。
并且这个过程 客户端是不知情的,全由服务端处理,
也就是客户端只发送了一次请求,服务端也只产生一对请求响应对象。
客户端的地址栏也不会发生变化。

请求转发运行逻辑图 :
1
特点:

  1. 请求转发通过HttpServletRequest对象实现(getRequestDispatcher方法)
  2. 请求转发为服务器内部行为,客户端是不知道的。
  3. 客户端只发送一次请求,服务端也只产生一对reqresp对象
  4. 客户端地址栏不会变化
  5. 请求转发时,请求、响应对象传递给下一个servlet,请求中的参数也继续向下传递
  6. 目标资源可以是servlet动态资源,也可以是HTML静态资源
  7. 还可以说WEB-INF下受保护的资源,但不可以是外部资源

代码举例:
3
ServletA 转发 给 ServletB :
以及 转发 给 HTML

@WebServlet("/sa")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是A");
//  获取请求参数
        String num = req.getParameter("num");
        System.out.println("num:" + num);

// 域中值:
		req.setAttribute("keyA","A requestMsg");
		
//获取请求转发器
// 转发给servlet
        //RequestDispatcher dispatcher = req.getRequestDispatcher("sb");

//转发给静态视图资源HTML        
        //RequestDispatcher dispatcher = req.getRequestDispatcher("bbb.html");

// 转发给WEB-INF下的资源 可以通过服务端访问 成功
        RequestDispatcher dispatcher = req.getRequestDispatcher("WEB-INF/aaa.html");

//转发给外部资源 失败 不可以哦
        //RequestDispatcher dispatcher = req.getRequestDispatcher("https://blog.csdn.net/GavinGroves");

//  做出转发动作
        dispatcher.forward(req,resp);
    }
}

sb接收转发 来的报文对象:

@WebServlet("/sb")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("我是B");
        //域值
        String keyA = (String)req.getAttribute("keyA");
        System.out.println("域中:"+keyA);
        //参数
        String num = req.getParameter("num");
        System.out.println(num);
    }
}

地址栏不会变
1
参数也传递了
1

8.2 响应重定向

客户端发送请求 给 servletA ,A接收后发出响应 302 以及目标资源地址,
客户端接收响应,再次发送请求给 servletB, B 接收响应
过程中 地址栏发生变化,即参数和域中值是无法传递的
并且因为是客户端行为,无法重定向WEB-INF下的资源,但可以重定向外部链接。
1

特点:

  1. 重定向是由HttpServletResponse对象实现(sendRedirect方法)
  2. 响应重定向是在服务端的指示下 的客户端行为
  3. 客户端的地址栏发生变化,客户端至少发送两次请求,即产生多次请求
  4. 服务端因为客户端的多次请求,就会有多个request对象,此时的请求参数就不能自动传递
  5. 目标资源可以是视图静态资源
  6. 但不能是WEB-INF下的资源
  7. 可以转发到外部资源

代码实现:

@WebServlet("/sc1")
public class ServletC extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("sc1");
        //参数获取
        String count = req.getParameter("count");
        System.out.println("count:"+count);
        //域中存入数据
        req.setAttribute("keyC","requestMsg");
        //响应重定向
        resp.sendRedirect("sd2");
        //resp.sendRedirect("bbb.html");
        
//WEB-INF的资源是不能通过浏览器直接访问的 失败
        //resp.sendRedirect("WEB-INF/aaa.html");
        
 // 重定向到外部资源 是可以的 	
		//resp.sendRedirect("https://blog.csdn.net/GavinGroves");
    }
}
@WebServlet("/sd2")
public class ServletD extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("sd2");
		//获取请求参数
		String count = req.getParameter("count");
        System.out.println("sd2count : " +count);
        //获取请求域 中 数据 为 null
        String keyC =(String) req.getAttribute("keyC");
        System.out.println(keyC);
        //做出响应
        resp.getWriter().write("sd2 response");
    }
}

结果

sc1 响应码302 重定向 给了sd2
jieguo
地址变化
1
1


总结

客户端 向 服务端 -> 请求 响应 静态数据和动态数据 区别:
1
地址 对应 关系 图:
1
映射关系图:
1

乱码设置:

-Dfile.encoding=UTF-8

文章来源:https://blog.csdn.net/GavinGroves/article/details/135724059
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。