Servlet学习笔记

Servlet学习笔记

Servlet生命周期

Servlet 接口,java中的一个包,Server Applet 服务端小程序,javax.servlet

功能 根据客户端提交的请求,调用服务器端Java代码,完成对请求的处理和运算

生命周期 从创建到被销毁的过程

实例化 --> 初始化 --> 服务 --> 销毁

请求 --> 服务器 --> URI --> 映射为Servlet --> 开启生命周期

1.被创建:执行init方法,只执行一次
  Servlet什么时候被创建?
  --默认情况下,第一次被访问时,Servlet被创建,然后执行init方法;
  --可以配置执行Servlet的创建时机;
2.提供服务:执行service方法,执行多次
3.被销毁:当Servlet服务器正常关闭时,执行destroy方法,只执行一次

注册Servlet类和映射关系

<servlet>
	<servlet-name>some-servlet</servlet-name>
	<servlet-class>Someservlet</servlet-class>
</servlet>
--映射,url
<servlet-mapping>
	<servlet-name>some-servlet</servlet-name>
    <url-pattern>/some</url-pattern>
</servlet-mapping>

Servlet特征

1.Serlvet是单例多线程的

  1. 一个Servlet实例只会执行一次无参构造器与init()方法,并在第一次访问时执行
  2. 用户没提交一次对当前Servlet请求,就会执行一次service()方法
  3. 一个Servlet实例只会去执行一次destory()方法,在应用停止时执行
  4. 忧郁Servlet是单例多线程的,所以为了保证其安全性,一般情况下不为Servlet类定义可修改的成员变量。因为每个线程均可修改这个成员变量,会出现线程安全问题
  5. 默认情况下,Servlet在Web容器启动时不会被实例化。

Web容器启动时创建Servlet实例

<!--在servlet下面添加,n代表优先级,越小越高-->
<load-on-stattup>n</load-on-stattup>

Servlet map 两个map

第一个map key--uri value--Servlet引用 第二个map key--uri value--Servlet class

请求解析出的uri访问第一个map,根据key找到value,找不到就访问第二个map,找到Servlet class,创建Servlet实例,并添加到第一个map中//所以Servlet是单例。

ServletInfo() 对当前Servlet进行的说明,版本/作者/信息等

ServletContext

ServletConfig() Servlet配置对象,Servlet容器传递信息到Servlet初始化,就是获取Servlet注册信息

ServletContext 通过ServletConfig.getServletContext()取得。getInitParameterNames()获取init param枚举对象

ServletContext 可以代表整个应用

<!--放在servlet里面-->
<init-param>
	<param-name>name</param-name>
    <param-value>value</param-value>
</init-param>
...
ServletContext sc = config.getServletContext();

getInitParameterNames() ServletContext中获取初始化参数名

Enumeration<String> names = sc.getInitParameterNames();
while(names.hasMoreElements()){
	String name = names.nextElement();
	String value = sc.getInitParameter(name);
}

setAttribute(name,value) ServletContext中设置域属性

作用域

sc.setAttribute("email","qianzi@163.com");
sc.setAttribute("address","wuhan");

getAttribute(name) ServletContext中获取域属性,全局获取

String str = (String)sc.getAttribute("email");//返回的是一个Object,要转型

getContextPath() ServletContext中获取应用名称

String path = sc.getContextPath();

getRealPath(value) ServletContext中获取本地文件路径

String realPath = sc.getRealPath("/images");//获取图片images的本地绝对路径

欢迎界面和URL-PATTERN

welcome-file-list 欢迎页面--可以有多个,从上往下查找,没有设置在tomcat中有默认的欢迎页面index

<welcome-file-list>
  <welcome-file>index.jsp</welcome-file>
  <welcome-file>index.html</welcome-file>
</welcome-file-list>

url-pattern 在servlet-mapping配置的定位url

精确路径模式 访问主页+/xxx时响应Servlet。可以配置多个

<servlet-mapping>
	<servlet-name>servletname</servlet-name>
    <url-pattern>/x/xxx/xxx/xxx/x</url-pattern>
    <url-pattern>/x/xxx/xxx</url-pattern>
    <url-pattern>/x/xxx</url-pattern>
    <url-pattern>/xxx</url-pattern>
</servlet-mapping>

通配符路径模式 *表示通配符,在/*后面无论是什么目录都拦截

<servlet-mapping>
	<servlet-name>servletname</servlet-name>
    <url-pattern>/x/xxx/xxx/xxx/*</url-pattern>
    <url-pattern>/x/xxx/*</url-pattern>
    <url-pattern>/x/*</url-pattern>
    <url-pattern>/xxx</url-pattern>
</servlet-mapping>

全路径匹配 /*表示所有路径都拦截 /也表示所有路径都拦截,但是不拦截动态资源请求

<servlet-mapping>
	<servlet-name>servletname</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
	<servlet-name>servletname</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

后缀模式 *.xxx 表示拦截.xxx结尾的路径,后缀模式不能和其他的混用

<servlet-mapping>
	<servlet-name>servletname</servlet-name>
    <url-pattern>*.xxx</url-pattern>
</servlet-mapping>

匹配原则

越精确就越先匹配谁,而不匹配其他的路径

精确路径优先匹配原则

最长路径优先匹配原则

精确路径 > 长路径带通配符 > 短路径带通配符 > 后缀匹配

Servlet核心

自定义GenericServlet类 在开发中不需要去实现Servlet接口,因此定义一个GenericServlet类实现Servlet接口,然后再定义Servlet类的时候继承GenericServlet类,重写service等函数即可,其他方法空实现。这是缺省适配器设计模式

缺省适配器设计模式 让一个类去空实现一个接口,然后让子类去实现接口的部分方法

模板方法设计模式 父类的方法让子类去实现,子类只用实现父类留给子类的方法就可以实现父类中复杂的方法

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
//缺省适配器设置模式,实现Servlet和ServletConfig接口,创建Servlet类时更方便使用。
public class GenericServlet implements Servlet,ServletConfig {
    private ServletConfig config;
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.config = config;
        this.init();
    }
//模板方法设计模式
    private void init() {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }

    @Override
    public String getServletName() {
        return config.getServletName();
    }

    @Override
    public ServletContext getServletContext() {
        return config.getServletContext();
    }

    @Override
    public String getInitParameter(String name){
        return config.getInitParameter(name);
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }

}

系统自带GenericServlet类 只需要继承系统自带的类就OK了,不需要自己写

自定义HttpServlet类 区分Get和Post提交,写出doPost和doGet函数让子类去对POST和GET去处理

public class HttpServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //获取请求提交方式
        String method = request.getMethod();
        if ("POST".equals(method)) {
            doPost(request, response);
        } else if ("GET".equals(method)) {
            doGet(request, response);
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) {
    }
}

系统也自带HttpServlet 功能更加强大,而且识别了Http协议和版本。

请求的生命周期 HttpServletRequest实例对象什么时候创建和销毁

服务器接收Htpp请求协议的格式对象,进行解析,同时服务器会创建HttpServletRequest的实现类和RequestFacade的对象,即请求对象,调用set方法,将数据封装到对象中,此时HttpServletRequest实例就创建完成了。通过HttpServletRequest对象去执行相应的操作,执行完后向客户端发送响应结束后,HttpServletRequest实例被销毁。一次请求对应一个请求对象,不同的请求之间没有关系,HttpServletRequest生命周期很短暂。

请求参数 request获取请求参数,参数名称,参数值可以为多个

getParameter(name) HttpServletPequest中获取指定名称的参数值,一个参数若是有多个值,获取其第一个值

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

getParameterNames() 获取全部参数名称的枚举集合

Enumeration<String> names = request.getParameterNames();
while(names.haMoreElements()){
	String eleName = names.nextElement();
	String eleValue = request.getParameter(eleName);
}

getParameterValues(name) 获取指定参数的所有值

String[] values = request.getParameterValues("name");

getParameterMap() 获取全部参数,返回map,key表示参数名称,value表示参数所有值

Map<String,String[]> map = request.getParameterMap();
for(String key : map.keySet()){
	String[] values = map.getValue(key);
}

请求的域属性 Attribute,请求域,只在一次请求中有效

setAttribute(name,value) HttpServletRequest中设置域属性

request.setArrtribut("name","value");

转发请求 getRequestDispatcher().forward

request.getRequestDispatcher("\OtherServlet").forward(request,response);

getAttribute(name) 获取指定名称的域属性

String value = request.getAttribute("name");

getAttributeNames() 获取所有域属性的名称

Enumeration<String> names = request.getAttributeNames();
while(names.hasMoreElements()){
	String name = names.nextElement();
	String value = request.getAttribute(name);
}

removeAttribute(name) 从请求中删除指定的域属性

request.removeAttribute("name");

HttpServletRequest常用方法 request

getRequestURL() return StringBuffer 获取request的url url:http://xxx

StringBuffer requestURL = request.getRequestURL();

getRequestURI() return String 获取request的uri,本地相对路径

String requestURI = request.getRequestURI();

getContextPath() return String 获取当前Servlet的根路径

String requestContextPath = request.getContextPath();

getMethod() return String 获取请求方式,是GET还是POST

String requestMethod = request.getMethod();

getRemoteAddr() return String 获取客户端IP

String clientIP = request.getRemoteAddr();

getServletPath() return String 获取Servlet匹配的精确部分路径

//Servlet url-pattern   /xxx/xx/x/*
//访问 /xxx/xx/x/111/2
String servletPath = request.getServletPath();
// 输出 /xxx/xx/x

getPathInfo() return String 获取Serlvet匹配的通配符部分路径

 String pathInfo = request.getPathInfo();
 //输出 /111/2
 // 不存在通配符部分。pathInfo为null

中文乱码问题 http协议基于TCP协议,传输的形式基于字节流

字节流 以%开头以16进制出现

产生原因 程序界面和服务器的编码方式不同

GET与POST GET在Tomcat9中自动解决了乱码问题

setCharacterEncoding(“编码方式”) 设置了请求正文中的字符编码,解决了post提交的中文乱码,解决不了get方案

request.setCharacterEncoding("UTF-8");

完美解决方案 对需要解决乱码的参数设置编码方式,服务器接收到的字符编码为ISO8859-1

String name = request.getParameter("name");
name = new String(name.getBytes("ISO8859-1"),"UTF-8");

Response 响应对象

PrintWriter response.getWriter()

响应对象会把要响应的数据放入getWriter中,这是一个标准输出流,服务器会直接发送该对象到客户端

PrintWriter out = response.getWriter();

写入流的方式 append print write

out.append("text");
out.print("text");
out.write("text");

关闭流 不要直接关闭PrintWriter流,当响应结束了,流会由服务器自动关闭

解决乱码问题 setCharacterEncoding() setContentType

setCharacterEncoding(“charset”) 该方法要写在response.getWriter()之前

response.setCharacterEncoding("UTF-8");
//并不能解决乱码问题,该方法只是设置MIME字符编码,要先设置ContentType才能解决乱码

setContentType(“type”) 设置响应的格式

response.setContentType("text/html;charset=UTF-8");
//可以指定MIME的字符编码即响应体的字符编码

请求转发与重定向 通过HttpServletRequest获取RequestDispatcher对象的forward方法,完成请求转发,通过HttpServletResponse的sendRedirect方法,完成重定向功能。

请求转发 请求发送到服务器访问资源1,但是又用到资源2,所以资源1将请求转发到资源2,资源2可以是WEB-INF中的资源,请求转发成为服务器内跳转

重定向 请求发送的服务器访问资源1,但是又用到了资源2,资源1响应请求后,接受响应后再次请求资源2,这就是重定向,资源2不可以是WEB-INF中的资源,重定向成为服务器外跳转

response.sendRedirect("other");
//将请求重定向到other

重定向无法传递数据 解决方案,加?和参数

response.sendRedirect("other?name=qianzi&address=wuhan");

重定向传输数据乱码 发送请求的编码根据TCP IP协议,是字节流数据

//URLEncoder URLDecoder编码解码库
name = URLEncoder.encode(name,"UTF-8");//编码
pname =  URLDecoder.decode(pname,"UTF-8");//解码

重定向到其他应用

response.sendRedirect("/otherWebapp");

重定向作用 重定向到其他应用,请求转发不行,防止表单重复提交

请求转发和重定向 跳转到其他应用,对于表单数据的处理,或者请求会消耗大量服务器资源,使用重定向,如果只是数据传递,使用请求转发,两者都能使用的时候,建议使用重定向

请求转发的forword()和include()的区别 资源1请求转发到资源2,其实是内部的资源请求,而两次请求会被合并成一个请求。转发的资源2的请求会被增强。

forword和include的区别在于response forword是在资源2得到请求后才开启响应输出流,资源1的响应输出流不会合并到资源2,include是在资源1得到请求后转发时开启响应输出流,资源1的响应将会被合并到资源2的响应输出流中

访问路径问题

URL 统一资源定位符

格式 协议,主机,端口,路径

绝对路径 = 参照路径 + 相对路径 服务器或浏览器会自动生成参照路径,所以不用自己写参照路径

以斜杠开头的相对路径 前台路径和后台路径

前台路径 出现在jsp中,加/表示以服务器路径为根路径 无/表示以当前路径为根路径,查找资源

后台路径 出现在xml,java类中,以项目路径为根路径,标识出资源

以路径名开头的相对路径 当前访问路径的资源路径为根路径

Servlet线程安全

线程安全 多线程并发访问,存在可修改的共享数据

JVM可能存在的线程安全问题的数据分析

栈内存数据分析 栈内是多例的,jvm会为每个线程创建一个栈,不存在数据共享,方法中的局部变量存放在Stack的栈针中,执行完毕,出栈,所以不会导致数据共享。所以栈内存中不存在线程安全问题。

堆内存数据分析 一个jvm只存在一个堆,堆内存是共享的。被创建的对象是存放在堆内存中,成员变量是存在堆内存中,堆内存的数据是多线程共享的,堆内存存在线程安全问题

方法数据分析 一个jvm只有一个方法区,静态变量与常量存放在方法区,方法区是多线程共享的。常量是不能被修改的,不存在线程安全问题。静态变量是多线程共享的,所以静态变量存在线程安全问题。

线程安全问题解决方案

对于一般性的类,不要定义为单例,除非特殊需要或者类创建时消耗大量系统资源

尽量少使用静态变量

单例类尽量不使用成员变量

使用synchronized实现线程同步。

Servlet线程安全解决问题

servlet类变量最好放在方法中。

synchronized 同步,这样不好,一次只能有一个访问

synckronized (this){
	username = request.getParameter("name");
	....
}

参考资料

Servlet 经典实战视频教程_动力节点

https://www.bilibili.com/video/BV1yx411s7zC

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值