- servlet线程安全
- web.xml:
- WEB-INF目录:
- tomcat启动过程:
- servlet
- ServletContext(servlet上下文对象)
- ServletConfig(servlet的配置对象)
- http协议(HyperText Transfer Protocal超文本传输协议)
- 转发和重定向
- web开发中的乱码处理
- jstl(java/jsp的标准标签库)
- 过滤器Filter
- 监听器
- EL表达式
servlet线程安全
容器收到请求后,会启动一个线程来进行相应的处理
默认情况下,容器只会为每一个servlet创建一个实例对象,如果同时有多个请求访问同一个servlet,则肯定有多个线程访问这个servlet,如果这些线程要修改servlet实例对象的某个属性,就可能发生线程安全问题
web.xml:
web.xml文件存储在/webContent/WEB-INF/web.xml
web.xml当前web项目的入口,即每一个web项目都要有一个web.xml文件
web.xml在tomcat服务器启动的时候解析和加载,启动完毕后,改动了web.xml不会生效
解析:用dom4j能读取web.xml的内容
加载:解析完毕后把读取到的内容以字符串的方式存储到内存中
WEB-INF目录:
在webapp/WEB-INF目录 此目录中的所有资源,都不能通过浏览器来访问,但是程序可以访问,一些重要的页面可以放在里面
tomcat启动过程:
- Initialzation processed in xx ms–>启动时首先解析和加载tomcat目录中的web.xml–>tomcat自检
- starting service Catalina–>正在开启服务
- starting servlet engine–>开启服务中的servlet引擎,引擎开启后,自动读取/加载/解析部署后的web项目的web.xml–>项目的web.xml检查
- Server startup in xxx ms–>启动完毕
servlet
sun公司做出一套servlet规范(接口),此套规范无法独立运行,必须放在web服务器中(tomcat)中运行,web服务器(tomcat),也可以称为web容器(放置多个web项目),有了这个前提可以做到通过浏览器请求一个url地址,最终执行了某个servlet中的doXXX方法,即通过浏览器就可以执行一段java代码,这就是设计servlet接口的目的
多个平台之间调用的时候,可以模拟servlet接口
提交方式:
- post(表单): 以数据块的方式发送数据给服务端,可以发送大量数据,在地址栏不会显示内容,安全.
- get(表单): 以数据字符串的方式发送给服务端,只能发送两百多字符,在地址栏上会显示数据内容,没有明确指定post提交,默认一律get提交
servlet运行的步骤:
- 浏览器依据url建立与web服务器(tomcat)的连接
- 浏览器对请求的数据打包并发送给服务器
- 服务器解析请求的数据,并分别把数据封装到request对象和response对象
- web应用服务器根据url的路径,寻找servlet并反射实例化servlet对象
- web应用服务器调用init方法,做初始化
- web应用服务器自动调用service方法,service方法根据method指定的值调用对应的doXXX方法
- 执行doXXX方法,方法中通过response对象,把数据响应给浏览器
- 浏览器获取到响应回来的数据(响应的实体内容),浏览器格式化显示数据到网页上
servlet生命周期
web.xml部分代码:
<servlet>
<servlet-name>UserLoginServletName</servlet-name>
<servlet-class>cn.tedu.servlet.UserLoginServlet</servlet-class>
<!-- Object obj=Class.forName("cn.tedu.servlet.UserLoginServlet").newInstance(); -->
</servlet>
<servlet-mapping>
<servlet-name>UserLoginServletName</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
- 浏览器请求某个url的时候,也就是在请求服务器中的servlet,如果web.xml中有此url,就寻找这个url的兄弟节点
,如果有,就取出名字,拿这个名字去整个web.xml文件中查找是否有 层级关系的servlet的名字,如果找到了就找 的兄弟节点 ,取出servlet-class的值,并反射实例化对象,此servlet生命周期开始,用此对象自动调用init方法(如果有),init方法只执行一次,并做相关的初始化工作; - 然后自动调用service方法,service方法会根据method指定的值去调用对应的doXXX方法,只要服务器不停止,每次请求都会执行service方法,直到正常停止web应用服务器,会自动调用destroy方法,destroy方法用于销毁对象并释放内存空间,生命周期结束.
- 第一次请求servlet的时候生命周期开始
- 在服务器启动的时候生命周期开始
<servlet>
<load-on-startup>数字<load-on-startup/>
</servlet>
load-on-startup:
- 此元素标记容器在启动的时候加载这个servlet,生命周期开始
- 它的值必须是一个整数,表示servlet加载的顺序,值大于等于0的时候表示容器在启动的时候加载,值小于0或没有配置此项表示容器在该servlet被第一次请求的时候加载,整数值越小,优先级越高,servlet就越是优先加载,当值相同的时候,容器就按照书写的顺序加载
init()
每一次生命周期开始都调用此方法,一般做初始化工作
有两种初始化方式:
- 局部初始化
<servlet>
<servlet-name>SysInitServletName</servlet-name>
<servlet-class>cn.tedu.servlet.SysInitServlet</servlet-class>
<init-param>
<param-name>ipRange</param-name>
<param-value>10.8.38.1-10.8.38.100</param-value>
</init-param>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
初始化的数据仅在指定的servlet中使用
- 全局初始化
<web-app>
<context-param>
<param-name>globalIpRange</param-name>
<param-value>10.8.38.1-10.8.38.100</param-value>
</context-param>
<context-param>
<param-name>globalEncoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<web-app/>
所有的servlet,filter,listener都可以读取初始化数据
初始化方法有两个
- init(); 无参数
- init(ServeletConfig);有参数
如果这两个方法同时存在,执行的是带参数的方法
service()
每次请求调用此方法,由此方法决定调用某个doXXX方法
doXXX()
只做三件事
- 获取前端提交的数据
- 基于前端提交的数据调用具体业务
- 根据业务的返回结果做响应给客户端
destroy()
方法中放置释放资源代码,对象=null;
在web容器正常停止时调用
ServletContext(servlet上下文对象)
当tomcat启动的时候,先自检自己 自检完毕后starting service开启服务 staring servlet engine 开启servlet引擎 开始读取web容器中的web项目,逐一解析加载每一个web项目的web.xml web容器(tomcat容器/web应用服务器)会为每一个web项目分配一个ServletContext,每一个web项目的ServletContext只有一个 tomcat停止服务,web项目的ServletContext就销毁了 ServletContext是一个对象,存储在tomcat容器中,是服务端对象,ServletContext对象中维护了多个map集合,可以把数据存储给ServletContext对象中,供所有的Servlet,filter,listener访问
获取ServletContext对象的方式
- 在servlet类中的任意方法中写this.getServletContext();
- 用ServletConfig对象.getServletContext();
- 用request.getSession().getServletContext();
- 用过滤器中的带参数的init方法,FilterConfig对象.getServletContext();
ServletConfig(servlet的配置对象)
在Servlet中的有参数的init()方法,参数就是ServletConfig
ServletConfig对象.getInitParameter(“key”);//获取局部参数
ServletConfig对象.getServletContext().getInitParameter();获取全局初始化数据
ServletConfig对象中存储的是当前的servlet的信息,servletConfig对象只对应一个servlet
ServletConfig对象中能存储什么数据,ServletConfig.getXXX()
http协议(HyperText Transfer Protocal超文本传输协议)
有w3c指定的一种应用层面的协议,用来定义浏览器与web服务器之间如何通信以及通信的数据格式
当请求服务器的资源时,会根据http协议发送一些数据(消息头,请求头,发送的数据)
服务器接收到后,会拆开数据,服务器根据拆开的数据做服务器的处理,处理完后给响应给浏览器(响应头和响应体内容)
消息头,请求头,发送的数据,响应头,响应的内容都要遵守http协议,否则http协议无法识别这些信息
每一种浏览器都可以查看http协议的信息,也可以借助第三方工具postman等
请求的数据包组成:
- 请求行:请求的方式+请求的资源路径+协议版本
- 消息头:消息头中包含键值对,一般由w3c定义,通信双方通过消息头内容传递信息
- 实体内容:只有请求的方式是post,实体内容才会有数据 响应的数据包组成:
- 状态行:协议的类型+版本+状态码+状态码描述
- 消息头:web服务器返回的一些消息头给浏览器
- 实体内容:服务器返回的结果
servlet如何处理http协议
当web容易受到一个http请求时,通信数据由web容器负责封装和提供这些信息,这些信息分为两个对象
与请求数据对应的是HTTPServletRequest接口类型的对象
与响应数据对应的是HttpServletResponse接口类型的对象
HTTPServletRequest对象
当客户端通过http协议访问服务器的时候,请求中所有的消息信息都封装到这个对象中,通过此对象,getXXX方法获取协议中的消息信息
比如:
getParameter(“key”);
getRemoteAddr();
…
HTTPServletResponse对象
提供给客户端响应数据,封装了http响应的数据,通过这个对象可以设置状态行消息头和实体内容
比如:
响应的内容为png图像
response.setContentType(“image/png”);
设置响应的内容是utf-8格式:
response.setCharacterEncoding(“UTF-8”);
设置响应到浏览器的页面:
response.sendRedirect(“login.jsp”);
…
转发和重定向
重定向
response.sendRedirect(“可以是jsp,也可以是servlet的url”);
相当于重新请求服务器资源(jsp,servlet)
request对象和response对象都是servlet容器新创建的对象
地址栏会显示重定向后的连接(url)
服务器象浏览器发送一个302状态码以及一个location的消息头
该消息头的值是一个地址,称之为重定向地址,浏览器收到后会立即向location中标记的地址发出请求
转发
request.getRequestDispatcher(可以是jsp,也可以是servlet).forward(request,response);
转发实际上就是请求指定jsp或servlet,也会跳转到指定目的地,相当于请求服务器资源,就是在服务器中请求服务器中的另一份资源,就是在服务器内部跳转,request对象和response对象都是原来的对象,不会新创建,如果request对象绑定了数据,转发就是把数据转发的下一个目标中,地址栏的内容不是目的地址,原来地址栏是什么,就是什么,即地址的地址不变,不带数据的转发,没有任何意义,用重定向即可
转发的步骤:
- 给request绑定数据 request.setAttribute(“key”,value);
- 把绑定了数据的request对象转到以一个目标 request.getRequestDispatcher(目标url).forword(request,response);
web开发中的乱码处理
- 所有的乱码都可以用下面的方式处理
汉字=new String(乱码的字节,”UTF-8”);
比如:
String 乱码=request.getParameter(“userName”);
汉字=new String(乱码.getBytes(“ISO8859-1”),”UTF-8”);
ISO8859-1也可以写成ISO-8859-1 - 下面的方法虽然代码简洁.但只有post提交的时候才生效
如果是get方式提交,下面的的语句不好使,此时只能方式一
request.setCharacterEncoding(“UTF-8”);
注意:第一种和第二种不要同时使用
tomcat版本问题
tomcat8版本及以后版本只需要使用方案二即可 方案一废弃不用,因为tomcat已经给实现处理了
- tomcat7版本及以前版本无论get提交还是post提交,request中都是ISO8859-1
- tomcat8版本及以后版本
get提交request中还是ISO8859-1,但getParameter取出数据就变成utf-8;
post提交request中还是ISO8859-1,但取出的还是ISO8859-1
jstl(java/jsp的标准标签库)
是sun公司定义的一套标准,成为javaee5.0的核心
如何使用jstl
在官网下载jstl开发包,把jstl.jar拷贝到项目中,然后构建路径 在需要的jsp页面上写taglib指令 <%@ taglib prefix=”c” url=”jstl的命名空间”%>
jstl标签分类:
核心标签库 简称 c 常用标签
- <%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core” %>
- 格式化标签库 简称 fmt
- <%@ taglib prefix=”fmt” uri=”http://java.sun.com/jsp/jstl/fmt” %>
- sql标签库 简称sql
- <%@ taglib prefix=”sql” uri=”http://java.sun.com/jsp/jstl/sql” %>
大部分情况下使用核心标签
分支标签:
<c:if test="" var="" scope="">
</c:if>
当test条件为true时,执行标签体的内容
var:指定一个对象名称,此对象名称由用户定义
scope:指定绑定数据的范围
page,request,session,application
<c:choose>
<c:when test=""> </c:when>
...
<c:when test=""> </c:when>
<c:otherwise ></c:otherwise >
</c:choose>
<c:forEach var="" items="" varStatus="">
</c:forEache>
| 属性 | 描述 | 是否必要 | 默认值 | | :—— |:— | :— | :— | | items | 要被循环的信息 | Four | 无 | | begin | 开始的元素(0=第一个元素,1=第二个元素) | 否 | 0 | | end | 最后一个元素(0=第一个元素,1=第二个元素) | 否 | Last element | | step | 每一次迭代的步长 | 否 | 1 | | var | 代表当前条目的变量名称 | 否 | 无 | | varStatus | 代表循环状态的变量名称 | 否 | 无 |
开发servlet常见的错误
- 404是客户端请求服务端的资源不存在
- 405错误web服务器无法定位doXXX方法
- 500错误:全都是服务端的java代码错误(间接或直接)
- 200是请求和响应成功
- 302是重定向的时候响应的状态码
base64
- java端: java.util.Base64
//加密:
Base64.getEncoder().encodeToString(upwd.getBytes()));
//解密
Base64.getDecoder().decode(加密的数据);
- js客户端:
加密:
window.btoa(“test”);//把test加密
解密:
window.atob(“加密的数据”);
过滤器Filter
过滤器就是一个servlet的特例
是servlet2.3规范中定义的中小型,可插入的web组件,用来拦截servlet
容器的请求和响应过程以便查看,提取或以某种方式操作正在客户端和服务器之间交换的数据
实现过滤器的步骤:
- 创建一个java类实现javax.servlet.Filter接口
- 在实现类中的doFilter方法添加过滤器的过滤算法
- 在web.xml中添加
<filter></filter><filter-mapping></filter-mapping>
在filter和servlet中url-pattern可以写
-
/* 匹配所有请求
-
*.jsp 匹配所有的jsp
-
/user/* 匹配url中带有/user/为开头的所有请求
-
*.do 匹配所有以.do为结尾的请求
-
/login 匹配只有login的请求
生命周期
- 当服务启动时,会加载web.xml中的内容,如果文件中有
项,那么就读取 节点中的内容,然后反射实例化对象,生命周期开始,然后通过过滤对象自动调用init方法,做当前过滤的初始化,init方法只执行一次,如果 中是/*,代表所有的请求都要被此过滤器过滤/拦截,只要请求任何资源都要经过过滤器的doFilter方法,在此方法中写过滤条件,可以根据过滤条件来决定是否filterChain.doFilter(),如果调用此语句,会寻找下一个过滤器,若果进入下一个过滤器的doFilter方法,如果没有就直接访问要请求的资源,请求的资源响应完毕后,会按照过滤器的调用顺序逐层返回,最后所有过滤器的doFilter方法执行完毕,如果正常停止服务器,会执行destroy方法,然后生命周期结束
局部变量
FilterConfig:是过滤器的配置对象,存储的是局部的数据,FilterConfig对象
只在过滤器的init(FilterConfig filterConfig)方法中出现
<filter>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
特点
过滤器不能太多,过滤原则也不能太复杂
降低系统性能,如果是/*,多有请求都要经过过滤器
实现系统功能的热插拔,去掉
监听器
servlet规范中定义一种特殊的组件,用来监听servlet容器产生的事件,并作出相应处理
容器产生的事件有两大类:
生命周期相关的事件:
监听器监听,如果生命周期开始就触发做相应的处理
如果生命周期结束就触发做相应的处理
- ServletRequestListerner request对象的监听器接口
- requestCreated() 当请求request对象创建了就触发调用此方法
- requestDestroyed() 当请求request对象销毁了就触发调用此方法
- HttpSessionListener session对象的监听器接口
- sessionCreated() 当服务器第一次创建session对象就触发调用此方法
- sessionDestroyed() 当服务器销毁session对象就触发调用此方法
- ServletContextListener servletContext对象的监听器接口
- contextInitialized() 当服务器给某一个应用创建ServletContext就触发调用此方法
- contextDestroyed() 当服务器中某一个应用销毁ServletContext就触发调用此方法
绑定数据相关的事件
监听器监听:
如果某个对象中添加数据就触发做相应的处理
如果某个对象中数据被移除就触发做相应的处理
如果某个对象中数据被替换了就触发做相应的处理
当调用 setAttribute()方法时触发
当调用removeAttriute()方法时触发
当调用replaceAttribute()方法时触发 - ServletRequestAttributeListener 请求对象中的数据变化监听器接口
- attributeAdded() request对象中数据被添加则触发此方法
- attributeRemoved() request对象中数据被移除则触发此方法
- attributeReplace() request对象中数据被替换则触发此方法
- HttpSessionAttributeListener session对象中的数据变化监听器接口
- attributeAdded() session对象中数据被添加则触发此方法
- attributeRemoved() session对象中数据被移除则触发此方法
- attributeReplace() session对象中数据被替换则触发此方法
- ServletContextAttributeListener servlet上下文数据变化监听器接口
- attributeAdded() servletContext对象中数据被添加则触发此方法
- attributeRemoved() servletContext对象中数据被移除则触发此方法
- attributeReplace() servletContext对象中数据被替换则触发此方法
实现监听器的步骤:
- 1.新建一个java类实现上面的六个中某一个接口
- 2.编写对应方法的监听逻辑
- 3.在web.xml中,注册监听器
监听器的生命周期:
在tomcat启动的时候就生命周期开始
如果有对应的条件触发了,就执行对应监听处理方法
在tomcat正常停止的时候,监听器对象消失
在tomcat中启动,监听器,过滤器,servlet的生命周期开始的顺序
最先是监听器生命周期开始
其次是过滤器生命周期开始
最后servlet生命周期开始
带有load-on-startup,启动时开始
不带load-on-startup,但需要带servlet-mapping则启动后,第一次请求时开始
EL表达式
使用EL表达式访问bean对象的属性数据,可以使用如下两种方式
- ${对象名.方法名去掉get,然后把剩下的字符串的第一个字母小写}
- ${对象名[“方法名去掉get,然后把剩下的字符串的第一个字母小写”]}
比如:
${user.name}
${user[“name”]}
就是替换了<%=user.getName()%>
通过el表达式可以从指定的作用域中查找数据
查找数据的原则:
- 先从当前页的对象(pageContext)中寻找是否有此数据,如果有就取出
- 如果没有就从请求对象(request)中寻找是否有此数据,如果有就取出
- 若果没有就从会话对象(session)中寻找是否有此数据,如果有就取出
- 如果没有就从servlet上下文对象(ServletContext/application)中寻找是否有此数据,如果有就取出
- 如果没有就真的没有了,不会在页面上显示异常,也不显示null,就什么都不显示
取出数据的方式:
- 先从当前页的对象(pageContext)中寻找是否有此数据,如果有就取出${pageScope.名称}
- 如果没有就从请求对象(request)中寻找是否有此数据,如果有就取出${requestScope.名称}
- 若果没有就从会话对象(session)中寻找是否有此数据,如果有就取出${sessionScope.名称}
- 如果没有就从servlet上下文对象(ServletContext/application)中寻找是否有此数据,如果有就取出${applicationScope.名称} 如果没有就真的没有了,不会在页面上显示异常,也不显示null,就什么都不显示
注意,xxxScope可以不写,那么就按照上面的原则,逐层查找,找到就取出
没有找到就什么都不显示,建议写xxxScope,精确查找
用EL表达式输出简单的运算结果
- 算数运算: + - * / % 注意:+符号,只能做求和,不能做字符串连接
比如:
${1+1} //2 - 逻辑运算: && || ! and or not
比如:
${true and false} //false - 关系运算:
-
>= < <= == !=
- gt ge lt le eq ne
- great than great equal less than less equal equals not equals 比如:
- ${1 gt 2} //false
- ${1 > 2} //false
empty:用户判断一个字符串是否为空,或者一个集合是否为空
以下情况结果都为true: - 空字符串
- 空的集合
- 值为null
- 找不对应的值
上面的四种情况都可以用empty来判断
比如: - ${empty null} //true
- ${empty “”} //true
- ${empty page.data}
用EL表达式获取请求的参数
上文是用表单或问好传值的方式传送数据
下文假设取userName的值${param.userName} ,底层就是request.getParameter(“userName”);
下文假设取多个数据
${paramValues.hobby},底层就是request.getParameterValues(“hobby”);
注意:
上文用request.setAttribute(“key”,value)
下文用Object obj=request.getAttribute(key);
下文用${requestScope.key}
注意:
上文中用表单提交或问号传值,request对象不没有丢失,是用转发到
下一个页面
在下一个页面(下文)中 ${param.userName}取单值
- ${paramValues.hobby}取多值
- ${paramValues.hobby[0]}
- ${paramValues.hobby[1]}