JavaWeb(Tomcat配置、JSP、requset、response、session、cookie、EL表达式、Servlet、监听器、过滤器、Ajax异步请求、文件上传)

1. Tomcat介绍及配置
1.1 Tomcat介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ixE2B8zs-1611284533119)(./asset/tomcat介绍.png)]

Tomcat是Apache下的一个开源的免费的轻量级的应用服务器,常用于中小型企业用户并发量不是很高的应用中

单体的Tomcat一般支持200个并发量(常规的硬件),最高可支持500个并发(硬件要求比较高)

1.2 Tomcat安装与配置

直接到Tomcat官网目下进行下载【下载完成后直接解压即可】

在安装目录中打开bin目录找到startup.bat,双击启动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hg7gbiDO-1611284533123)(./asset/tomcat启动.png)]

在浏览器的地址栏中输入:localhost:8080/127.0.0.1:8080 访问即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Np8gqCi1-1611284533126)(./asset/tomcat启动页面.png)]

tomcat的配置方式与Java的JDK配置方式是一样的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D0NEJqy3-1611284533127)(./asset/tomcat环境配置.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nw7t3OMF-1611284533131)(./asset/cmd-tomcat.png)]

1.3 Tomcat目录介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cvYMjVNh-1611284533133)(./asset/tomcat文件目录.png)]

1.4 Tomcat启动常见问题

闪退现象:双击startup.bat文件时,黑窗口一闪而过。

通常是因为JDK环境配置不符合规范,tomcat在运行时会自动去系统环境中去查找一个叫JAVA_HOME的变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GiKXkbGh-1611284533133)(./asset/jdk变量.png)]

启动报错:通常是因为指定的端口服务已经启动了,再次启动时就会出现该问题

特殊情况:电脑刚重启,就打开了一个tomcat的启动选项,仍然提示端口被占用,可能是因为系统中有其他的应用占用了默认的8080端口

1.查找所有端口号对应的服务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARNilCHK-1611284533135)(./asset/查看所有的端口号.png)]

2.关闭该端口号对应的应用服务id(PID)

命令:taskkill /f /t /im "PID的值"
taskkill /f /t /im "1648"
2. JSP核心内容
2.1 软件系统架构

软件系统架构指的是要开发的软件对应的方向(web端、移动端),主要有两种架构,B/S及C/S架构

B/S架构:browser and Server 浏览器与服务器架构模式,例如:天猫、京东、后台管理类,简单点说:在网页端运行的都是B/S架构

优缺点:携带不方便、不需要下载、不需要频繁更新、数据都在服务器上(安全)

C/S架构:Client and Server 客户端与服务器架构模式,例如:微信、钉钉、qq、王者等,简单点说:手机上运行的APP都是C/S架构

优缺点:携带方便、需要下载、频繁更新、数据再客户端上存储(不安全)、效率高、速度快

2.2 JSP介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OykCjSTC-1611284533136)(./asset/jsp介绍.png)]

jsp是一个动态网页标准技术,允许在前端的页面中嵌入Java代码,jsp的执行原理:先进行翻译,在进行编译,最后执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SIMDlwAX-1611284533137)(./asset/jsp执行原理.png)]

2.3 JSP的基础语法
语法描述
<%%>小脚本(嵌入java代码)
<%=%>在网页中输出java变量
<%!%>在网页中定义方法
<%@ %>指令
2.4 JSP的内置对象

jsp中默认支持的对象,可以直接拿来使用的

对象名描述
out网页输出对象
page当前页对象
request请求对象
response响应对象
session会话
application全局对象(上下文对象)
exception异常处理对象
pageContext页面上下文对象
config配置对象
2.4.1 jsp页面创建
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>jsp</title>
  </head>
  <body>
   hello jsp!!
   <hr>
    jsp从入门到入土
  </body>
</html>
2.4.2 web项目启动

idea中配置Tomcat

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KzHSZGxe-1611284533138)(./asset/idea-tomcat配置.png)]

给Tomcat服务配置需要运行的项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zbg9iCey-1611284533140)(./asset/idea-tomcat-配置项目.png)]

2.4.3 out对象
<%
String str = "haha";
//使用out对象输出,只能在脚本中使用
out.print(str);
%>
<%--还可以结合前端使用--%>
str:<%=str %>

求最大值

<%!
  public int max(int x,int y){
  return x>y?x:y;
}
%>
最大值:<%=max(5,6) %>

jsp显示当前日期

<%@ page import="java.util.Date,java.text.SimpleDateFormat" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        //在网页中显示当前的日期
        Date date = new Date();
        //对日期进行格式化操作
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String showTime = sdf.format(date);
    %>
    当前时间为:<%=showTime%>
</body>
</html>
2.4.4 request对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nbPFhKLu-1611284533142)(./asset/用户交互过程.png)]

用户发送的所有的请求都是通过request对象实现,request中携带了请求的地址、参数等等

方法名描述
getParameter(String key)获取参数
setCharacterEncoding(String encoding)设置请求编码
getRequestDispatch(String path).forward(req,resp)请求转发

登录案例(用户发送登录请求,后台获取用户发送的请求参数)

//login.jsp
<%--
  action指定表单信息发送的服务器地址
  method指定发送请求的方式
  --%>
<form action="control.jsp">
  <input type="text" name="uname" placeholder="请输入用户名">
  <input type="password" name="pwd" placeholder="请输入密码">
  <input type="submit" value="登录">
</form>
//control.jsp
//获取请求中携带的信息
String uname = request.getParameter("uname");
String pwd = request.getParameter("pwd");

中文乱码处理方案

//中文乱码处理方案1(get请求):new String(uname.getBytes("ISO-8859-1"),"utf-8")
示例:uname = new String(uname.getBytes("ISO-8859-1"),"utf-8");
//中文乱码处理方案2(post请求)
示例:request.setCharacterEncoding("utf-8")
//中文乱码处理方案2(修改tomcat配置)
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URLEncoding="utf-8"/>

请求转发:将该请求转发给其他控制层处理,用户发送的一次请求,进行了多次转发

例如:小张需要一万块,自己没钱,他向支付宝借了一万块,但支付宝本身是没钱的,但是支付宝向银行借了一万块给了小张

//判断用户信息是否正确,转发到对应的页面
if("admin".equals(uname)){
//请求转发到success.jsp
request.getRequestDispatcher("success.jsp").forward(request,response);
}else{
request.getRequestDispatcher("error.jsp").forward(request,response);
}

request特点

1.一次请求,多次转发

2.请求转发后,地址栏中的地址不发生改变

3.request请求转发可以携带参数

通过request对象设置参数

//设置参数
request.setAttribute("msg","登录成功");
//获取参数
Object msg = request.getAttribute("msg");

request中getParameter()和getAttribute()区别

<%
//getParameter()用于获取表单或直接在地址栏中拼接的参数
String uname = request.getParameter("uname");
//getAttribute()只能获取通过setAttribute()设置的参数
Object msg = request.getAttribute("msg");
%>
2.4.5 response对象

response作为服务端响应给客户端的过程,仅仅只是将服务端的请求重定向给了另一个地址

例如:小张向小王借一万,小王说我没有,你可以向支付宝借,小张就向支付宝借了一万

方法名描述
sendRedirect(url)重定向

登录案例(需要注意重定向时地址栏中的变化及参数携带问题)

<%
	String uname = request.getParameter("uname");
	String pwd = request.getParameter("pwd");
	if("admin".equals(uname)){
    //重定向
    response.sendRedirect("success.jsp");
  }else{
    response.sendRedirect("error.jsp");
  }
%>

response的特点

1.至少两次请求

2.地址栏中的地址会发生改变

3.不能携带参数

面试题:请求转发和请求重定向的区别

2.4.6 session对象

Session称为一次会话,浏览器的打开及关闭。例如:登录淘宝之后,在该浏览器上无论打开的哪个页面都属于一次会话(不需要登录了)

session对象

方法名描述
setAttribute(key,val)设置键值对
getAttribute(key)通过键获取值
getId()获取session的id
setMaxInactiveInterval(time)设置session的过期时间[单位:秒](tomcat中默认配置的过期时间是30分钟)
removeAttribute(key)从session中移除指定的key

案例:使用session实现页面访问权限控制

<!--login.jsp-->
<%
String errMsg = "";
//登录页面可能会被打开多次(用户想要切换账户)
Object object = session.getAttribute("errMsg");
if(object != null){//从登录失败后跳转过来的
  errMsg = (String)object;
  //移除session中的key
  session.removeAttribute("errMsg");
}
%>
<p style="color:red;"><%=errMsg%></p>
<form action="control.jsp" method="post">
  <input type="text" name="uname" placeholder="请输入用户名">
  <input type="password" name="pwd" placeholder="请输入密码">
  <input type="submit" value="登录">
</form>
<!--control.jsp-->
<%
//设置请求编码格式
request.setCharacterEncoding("utf-8");
//获取表单中的参数
String uname = request.getParameter("uname");
String pwd = request.getParameter("pwd");
//对用户名及密码进行验证(操作数据库)
if("admin".equals(uname) && "admin".equals(pwd)){
  //将用户的用户名存储起来作为登录成功的标识
  session.setAttribute("uname",uname);
  //设置session的失效事件(单位:秒)
  session.setMaxInactiveInterval(5);
  //登录成功直接跳转到个人中心页面
  response.sendRedirect("person.jsp");
}else{
  //通过session存储一个错误信息
  session.setAttribute("errMsg","用户名或密码错误");
  //回到登录页面,并提示错误信息
  response.sendRedirect("login.jsp");
}
%>
<!--个人中心-->
<%
//首先需要先验证用户是否已经登录
Object object = session.getAttribute("uname");
if(object == null){//代表用户未登录,跳转到登录页面
  response.sendRedirect("login.jsp");
  return;
}
%>
<h4>个人中心</h4>
<p>欢迎,<%=object%></p>

Cookie对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KMwFmBKZ-1611284533142)(./asset/cookie.png)]

cookie是在本地进行数据存储的对象,访问速度快、效率高但是不安全

<%
	 //将name存储到Cookie中
  Cookie nameCookie = new Cookie("name",name);
	//将创建的cookie存储到响应头中
  response.addCookie(nameCookie);
%>
<%
  //获取cookie
  Cookie[] cookies = request.getCookies();
  for(Cookie cook : cookies){
    String val = cook.getValue();
    out.println(cook.getName()+":"+val+"<br/>");
  }
%>

Cookie生效时间

1.值为正数时,代表了cookie的存活时长(秒为单位)

2.值为0时,代表立即清除cookie

3.值为负数时,代表cookie的生命周期与当前会话一致

<%
	Cookie nameCookie = new Cookie("name",name);
  //设置cookie的生效时间setMaxAge() 秒
  nameCookie.setMaxAge(10);
  response.addCookie(nameCookie);
%>
<%
//获取cookie
Cookie[] cookies = request.getCookies();
if(cookies != null){
  for(Cookie cook : cookies){
    String val = cook.getValue();
    //设置cookie立马失效
    cook.setMaxAge(0);
    //更新response中的cookie对象
    response.addCookie(cook);
    //对获取到的数据进行解码
    //val = URLDecoder.decode(val,"utf-8");
    out.println(cook.getName()+":"+val+"<br/>");
  }
}
%>

Cookie针对不同的浏览器可以存储的数量是有限的

一、浏览器允许每个域名所包含的cookie数:

Microsoft指出InternetExplorer8增加cookie限制为每个域名50个,但IE7似乎也允许每个域名50个cookie。

Firefox每个域名cookie限制为50个。

Opera每个域名cookie限制为30个。

Safari/WebKit貌似没有cookie限制。但是如果cookie很多,则会使header大小超过服务器的处理的限制,会导致错误发生。

注:“每个域名cookie限制为20个”将不再正确!

二、当很多的cookie被设置,浏览器如何去响应。

除Safari(可以设置全部cookie,不管数量多少),有两个方法:

最少最近使用(leastrecentlyused(LRU))的方法:当Cookie已达到限额,自动踢除最老的Cookie,以使给最新的Cookie一些空间。Internet Explorer和Opera使用此方法。

Firefox很独特:虽然最后的设置的Cookie始终保留,但似乎随机决定哪些cookie被保留。似乎没有任何计划(建议:在Firefox中不要超过Cookie限制)。

三、不同浏览器间cookie总大小也不同:

Firefox和Safari允许cookie多达4097个字节,包括名(name)、值(value)和等号。

Opera允许cookie多达4096个字节,包括:名(name)、值(value)和等号。

Internet Explorer允许cookie多达4095个字节,包括:名(name)、值(value)和等号。

注:多字节字符计算为两个字节。在所有浏览器中,任何cookie大小超过限制都被忽略,且永远不会被设置。

Session与Cookie的区别

1.session是存储在服务端的,Cookie是存储在客户端

2.session数据安全,Cookie数据不安全

3.session效率较低,Cookie效率相对较高

2.4.7 application对象

application是全局作用域对象,也称为web的上下文对象,通常用于存储一些全局参数

方法名描述
setAttribute(key,val)设置全局参数
getAttribute(key)获取全局参数

案例:统计文章的浏览次数

<%
	//设置默认的阅读量
    int count = 0;
    //1.先从全局作用域中获取存储的已访问的数量
    Object object = application.getAttribute("count");
    if(object != null){
        count = (Integer)object;
    }
    //2.在已有的阅读量的基础上+1
    count++;
    //3.将最新的阅读量存储到全局作用域中(更新)
    application.setAttribute("count",count);
%>
阅读量:<%=count%>
2.5 JSP的四大作用域

为了根据不同的情况去进行数据的存储、传递等等,jsp中给出了四大作用域以满足不同的使用情况

作用域描述
pageContext当前页生效
request一次请求中生效
session一次会话中生效
application整个web中生效
<%
//当前页
pageContext.setAttribute("pageContext","当前页生效");
//请求中
request.setAttribute("request","一次请求中生效");
//一次会话
session.setAttribute("session","一次会话中生效");
//全局作用域
application.setAttribute("application","整个web项目中");
//请求转发
//request.getRequestDispatcher("show.jsp").forward(request,response);
%>
<hr>
pageContext:<%=pageContext.getAttribute("pageContext")%>
<hr>
request:<%=request.getAttribute("request")%>
<hr>
session:<%=session.getAttribute("session")%>
<hr>
application:<%=application.getAttribute("application")%>
2.6 include指令

include指令用于引入其他的页面

<%@ include file="permission.jsp"%>

动作标签(将jsp中的常见的一些功能以标签的形式进行使用)

<jsp:include /> 将另一个页面中的内容引入进来,include的指令会将整个jsp页面进行编译及翻译的过程,可以使用变量

(动作标签只是引入了页面的结果,指令是将整个jsp的逻辑等都加载进来)

<jsp:forward /> 以标签的形式替换了request在小脚本中的跳转形式

2.7 三层结构的JDBC

常见的系统架构模式,分为三种:MVC、MVP、MVVM,模式的存在是为了将代码进行解耦,方便代码管理维护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x3HmBjrM-1611284533143)(./asset/三层架构.png)]

2.8 EL表达式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hKeBf92G-1611284533144)(./asset/el表达式.png)]

EL表达式核心点是将对象存储到某个域中,在需要的页面中直接使用表达式来使用,从来减少获取及强制类型转换的操作

<%
//存储
session.setAttribute("name","小黑");
//存储对象
Emp emp = new Emp();
emp.setEname("阿狸");
emp.setSex("女");
session.setAttribute("emp",emp);
%>
name:${name}
<hr>
ename:${emp.ename}
sex:${emp.sex}

注意:el表达式存储对象后,可以直接通过对象.属性获取值(实际是通过类中的setter和getter方法获取的)

运算符关键词描述
==eq判断是否相等
>gt判断是否大于
<lt判断是否小于
empty判断是否为空
!not
${empty emp}
${not empty emp}
${emp == null}

多个作用域有相同的key

<%
request.setAttribute("name","小黑");
application.setAttribute("name","小花");
session.setAttribute("name","小红");
pageContext.setAttribute("name","小绿");
%>
<%--当多个域中有相同的key时,按照作用域的大小,从小到大查找--%>
${name}
2.9 JSTL标签库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bBTN35ca-1611284533145)(./asset/jstl.png)]

JSTL是一个标签库(其中是以标签的形式来进行使用的),可以简化JSP页面中的小脚本的使用频率,让JSP页面看着更干净整洁

JSTL中也提供了用于做逻辑判断的功能,可以结合EL表达式使用

步骤:

1.关联jar包(JSTL的标签库、JSTL的规范)

2.引入标准库路径

<!%--prefix:别名 uri:引入的标准库路径--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--1~5循环--%>
<c:forEach begin="1" end="5" var="index">
  ${index}
</c:forEach>
<%--遍历数组
  items:指定要遍历的对象
    var:每次遍历对应的对象
    begin:开始值
    end:结束值
    varStatus:下标对象
    --%>
<c:forEach items="${strArr}" var="str" varStatus="indexObj">
  ${str},${indexObj.index}
</c:forEach>
<hr>
<%--遍历list集合--%>
<c:forEach items="${list}" var="str" varStatus="indexObj">
  ${str},${indexObj.index}
</c:forEach>
<hr>
<%--
  for(int i=0;i<empList.size();i++){empList.get(i);}
for(Emp emp : empList){}
--%>
<c:forEach items="${empList}" var="emp">
  姓名:${emp.ename},序号:${emp.eno}<br>
</c:forEach>
<hr>
<%--遍历Map集合--%>
<c:forEach items="${map}" var="map">
  ${map.key},${map.value}
</c:forEach>
3. Servlet核心内容
3.1 Servlet介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ML4OzSmQ-1611284533146)(./asset/servlet.png)]

Servlet用于接收及响应前端发送的请求,可以在同一个Servlet中处理多个请求,也可以定义返回的数据类型。

Servlet和JSP之间的关系

先有的Servlet再有JSP,JSP文件执行需要先翻译成java文件(servlet),然后再进行编译成class文件,最后执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNbtRVLo-1611284533147)(./asset/jsp编译.png)]

查找路径:eclipse中配置本地的Tomcat,项目启动运行后,在Tomcat的work中查找

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fS5ZlUYs-1611284533148)(./asset/servlet关系图.png)]

3.2 Servlet入门案例

需求:定义Servlet接收前端发送的登录请求,对请求进行处理

<form action="loginServlet" method="post">
  <input type="text" name="uname">
  <input type="submit" value="提交">
</form>
//自定义的Servlet要继承HttpServlet
public class LoginServlet extends HttpServlet {

    /**
     * Servlet中提供了service方法用于接收及响应web客户端的请求
     * service可以执行多次(每次请求都会执行该方法)
     * super.service(req, resp);用于请求分发
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("------service------");
        //设置编码
        request.setCharacterEncoding("utf-8");
        //获取请求中的参数
        String uname = request.getParameter("uname");
        if(uname.equals("admin")){
            //重定向
            response.sendRedirect("success.jsp");
        } else{
            response.sendRedirect("fail.jsp");
        }
    }
}

servlet配置

<!--
        配置Servlet,指定请求地址与Servlet之间的对应关系
            servlet-name:给Servlet指定一个唯一标识
            servlet-class:指定servlet所在的位置
    -->
<servlet>
  <servlet-name>LoginServlet</servlet-name>
  <servlet-class>cn.yunhe.servlet.LoginServlet</servlet-class>
</servlet>
<!--
        给指定的Servlet配置请求路径
            servlet-name:指定该路径要关联的servlet
            url-pattern:指定请求地址(特别注意必须给/[web项目的根目录])
    -->
<servlet-mapping>
  <servlet-name>LoginServlet</servlet-name>
  <url-pattern>/loginServlet</url-pattern>
</servlet-mapping>
3.3 Servlet生命周期

Servlet的生命周期依赖于web容器的生命周期,生命周期划分为:初始化(一次)、执行(多次)、销毁(一次)。

public LifeCycleServlet(){
  System.out.println("-----LifeCycleServlet构造器执行-------");
}
@Override
public void init() throws ServletException {
  System.out.println("------LifeCycleServlet 初始化执行--------");
  super.init();
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  System.out.println("------LifeCycleServlet service执行--------");
}
@Override
public void destroy() {
  System.out.println("------LifeCycleServlet destroy执行--------");
  super.destroy();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWplMYm1-1611284533148)(./asset/servlet生命周期.png)]

默认Servlet的初始化是在web客户端发送对应的请求到Servlet的时候,才会被初始化(不用就不初始化)

3.4 Servlet配置
3.4.1 加载顺序

配置Servlet加载顺序(默认是使用的时候才初始化),需要在配置Servlet的时候去指定load-on-startup标签

<servlet>
  <servlet-name>LifeCycleServlet</servlet-name>
  <servlet-class>cn.yunhe.servlet.LifeCycleServlet</servlet-class>
  <!--设置Servlet在web容器初始化的时候也进行初始化-->
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>LifeCycleServlet</servlet-name>
  <url-pattern>/lifeCycleServlet</url-pattern>
</servlet-mapping>

初始化顺序:值越大越靠后加载,值一样按照在xml文件中的配置顺序,靠后的先加载,如果不写该标签,只有在第一次请求的时候才会初始化

3.4.2 全局参数与局部参数

局部参数只在对应的Servlet中使用,全局参数是在所有的Servlet中都可以使用

<!--配置Servlet-->
<servlet>
  <servlet-name>paramServlet</servlet-name>
  <servlet-class>cn.yunhe.servlet.ParamServlet</servlet-class>
  <!--设置局部参数-->
  <init-param>
    <param-name>count</param-name>
    <param-value>1</param-value>
  </init-param>
</servlet>

获取结果

//获取局部变量参数
String count = getInitParameter("count");
System.out.println("ParamServlet count="+count);

全局变量配置

<!--Servlet的全局变量-->
<context-param>
  <param-name>name</param-name>
  <param-value>application</param-value>
</context-param>
 //获取全局变量(需要获取到上下文对象)
ServletContext servletContext = getServletContext();
String value = servletContext.getInitParameter("name");
System.out.println("ApplicationParamServlet name="+value);
3.5 Servlet-过滤器

可以通过配置,对需要进行特殊处理的请求进行过滤操作,类似于水龙头上的滤网,开发中可以用于编码过滤、请求头配置等等

过滤器的过滤规则

/*            		  过滤所有的请求
/permission/*       过滤指定目录下的所以请求
*.action            过滤以.action结尾的请求
/loginServlet       过滤指定的请求

编码过滤器

public class EncodingFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("----EncodingFilter初始化-----------");
  }

  /**
     * 设置过滤规则
     * 设置请求的下一步操作
     * @param servletRequest
     *  servletRequest是HttpServletRequest的父类,HttpServletRequest相当于jsp中的request对象
     * @param servletResponse
     *  servletResponse是HttpServletResponse的父类,HttpServletResponse相当于jsp中的response对象
     * @param filterChain
     *  filterChain用于控制请求的下一步操作,同时将最新的的request及response对象携带过去
     * @throws IOException
     * @throws ServletException
     */
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("-----EncodingFilter---doFilter---------");
    //设置过滤规则为编码处理
    servletRequest.setCharacterEncoding("utf-8");
    servletResponse.setCharacterEncoding("utf-8");
    //指定请求的下一步操作(放过)
    filterChain.doFilter(servletRequest,servletResponse);
  }

  @Override
  public void destroy() {
    System.out.println("----EncodingFilter销毁-----------");
  }
}

页面访问权限控制过滤器

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  //获取用户的登录标识
  HttpServletRequest request = (HttpServletRequest)servletRequest;
  HttpServletResponse response = (HttpServletResponse)servletResponse;
  HttpSession session = request.getSession();
  Object object = session.getAttribute("user");
  //判断用户是否已登录
  if(object == null){//未登录
    response.sendRedirect("/login.jsp");
  } else{
    //放过
    filterChain.doFilter(servletRequest,servletResponse);
  }
}

当有存在多个过滤器(有相同的过滤规则)时,就会形成过滤器链,过滤器的执行顺序与在web.xml文件中的配置顺序有关(靠前的先执行)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IvAZ60Gh-1611284533150)(./asset/过滤器链.png)]

3.6 Servlet-监听器

Servlet中的监听器指的是对Servlet中的四大作用域的监听,主要是监听域对象的创建与销毁以及域对象的属性变化(添加属性、移除属性、覆盖属性)等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ST2wlHH-1611284533151)(./asset/监听器对象.png)]

统计在线机器信息

public class OnlineListen implements ServletRequestListener, HttpSessionListener, ServletContextListener {

  ServletContext servletContext = null;
  ServletRequest request = null;
  //创建集合,用于存储在线信息对象
  List<PCInfo> pcInfoList = null;
  @Override
  public void contextInitialized(ServletContextEvent servletContextEvent) {
    servletContext = servletContextEvent.getServletContext();
  }
  @Override
  public void contextDestroyed(ServletContextEvent servletContextEvent) {}
  @Override
  public void requestDestroyed(ServletRequestEvent servletRequestEvent) {}
  @Override
  public void requestInitialized(ServletRequestEvent servletRequestEvent) {
    request = servletRequestEvent.getServletRequest();
  }
  @Override
  public void sessionCreated(HttpSessionEvent httpSessionEvent) {
    System.out.println("-------sessionCreated-------");
    HttpSession session = httpSessionEvent.getSession();
    //获取sessionId
    String sessionId = session.getId();
    //获取主机信息(需要从请求中获取)
    String address = request.getLocalAddr();
    String name = request.getLocalName();
    //把信息封装成对象
    PCInfo pcInfo = new PCInfo();
    pcInfo.setSessionId(sessionId);
    pcInfo.setPcAddress(address);
    pcInfo.setPcName(name);
    //从全局作用域中获取在线信息集合,判断是否是第一次打开
    Object object = servletContext.getAttribute("pcInfoList");
    boolean flag = false;
    if(object == null){//第一次打开
      pcInfoList = new ArrayList<>();
      //存储到集合中
      pcInfoList.add(pcInfo);
      //将最新的在线信息存储到全局作用域中
      servletContext.setAttribute("pcInfoList",pcInfoList);
    } else {
      pcInfoList = (List<PCInfo>)object;
      for(PCInfo info : pcInfoList){
        if(info.getSessionId().equals(sessionId)){
          flag = true;
          break;
        }
        flag = false;
      }
      if(!flag){
        //存储到集合中
        pcInfoList.add(pcInfo);
        //将最新的在线信息存储到全局作用域中
        servletContext.setAttribute("pcInfoList",pcInfoList);
      }
    }
  }
  /**
     * session的销毁方法,在浏览器关闭后不会立马执行
     * @param httpSessionEvent
     */
  @Override
  public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
    System.out.println("--------sessionDestroyed---------");
    //获取sessionId
    String sessionId = httpSessionEvent.getSession().getId();
    //从集合中找到对应的sessionID,然后从集合中移除,再将全局作用域中的对象更新下
    for(PCInfo info : pcInfoList){
      if(info.getSessionId().equals(sessionId)){
        pcInfoList.remove(info);
        servletContext.setAttribute("pcInfoList",pcInfoList);
        break;
      }
    }
  }
}
<!--监听配置-->
<listener>
  <listener-class>cn.yunhe.listen.OnlineListen</listener-class>
</listener>
3.7 Servlet2.x与3.x的区别
属性描述
String[] values/String[] params定义servlet的请求地址,可以定义多个(servlet-class)
String name定义Servlet的名字(servlet-name)
int loadonStartup定义初始化顺序(load-on-startup),默认是-1
boolean asyncSupported是否支持异步操作(默认false,true支持)
@WebServlet(value = {"/loginServlet","/loginServlet.do"},name = "LoginServlet",loadOnStartup = 1,asyncSupported = true)

Servlet2.x规范

1.项目目录结构必须要有WEB-INF,web.xml等文件夹和文件

2.在web.xml中配置servlet,filter,listener,以web.xml为java web项目的统一入口

Servlet3.x规范

1.项目中可以不需要WEB-INF,web.xml等文件夹和文件
2.在没有web.xml文件的情况下,通过注解实现servlet,filter, listener的声明,例如
@WebServlet, @WebFilter,@WebListener,当使用注解时,容器自动进行扫描。
3.关于一些异步请求的改动
4.关于ServletContainerInitializer的使用

3.8 Ajax异步请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZeEoDANx-1611284533152)(./asset/ajax.png)]

Ajax是一种实现局部刷新的动态网页开发技术,重点在于可以快速高效的实现局部数据的动态改变。

同步:所有的操作都需要按照顺序来执行,前面的未执行完,后面的就需要一直等待(安全性高,效率很低)

异步:前一个的操作结果不影响后一个任务的执行,且可以同时进行(效率高)

3.8.1 原生Ajaxshixan

需求:点击按钮,向后台发送请求,将后台返回的结果在前端页面中显示出来

//获取元素
let btn = document.getElementsByTagName("button")[0];
//绑定事件
btn.onclick = function(){
  //使用Ajax发送请求到后台
  var xmlhttp;
  if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
    xmlhttp=new XMLHttpRequest();
  }
  else
  {// code for IE6, IE5
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
  //指定请求地址及请求类型
  xmlhttp.open("GET","ajaxServlet");
  //发送请求
  xmlhttp.send();

  //指定回调函数,对响应的结果进行处理
  xmlhttp.onreadystatechange = function(){
    //代表请求已完成
    if(xmlhttp.readyState == 4){
      //响应正常
      if(xmlhttp.status == 200){
        //获取影响结果
        alert(xmlhttp.responseText);
      }
    }
  }
}
//设置编码
req.setCharacterEncoding("utf-8");
//返回文本数据(使用数据打印流返回结果)
PrintWriter out = resp.getWriter();
out.write("hello Ajax");
out.close();

常见的响应状态码

状态码描述
200响应成功
404服务器找不到请求资源(通常是请求路径错误)
500服务器内部错误(通常是Java代码出现了异常)
403服务器拒绝请求(请求参数不正确)
414请求地址过长(需要改为post请求)
3.8.2 jQuery中的Ajax

jQuery中的Ajax简化了原生Ajax的使用方式,同时提供了多个参数的配置,更加灵活、方便

需求:请求后台数据,在前端页面中渲染

$.ajax({
  /*指定请求地址*/
  url: "ajaxServlet",
  /*指定请求类型,jQuery1.9之前使用type属性*/
  method: "post",
  /*指定要提交的参数,若没有参数可以不写*/
  data:{
    "key":"ajax",
    "val":"haha"
  },
  /*指定期望后台返回的数据类型,默认是string*/
  dataType: "json",
  /*指定请求成功后的回调函数,参数为返回的数据对象*/
  success:function(resultData){
    $.each(resultData,function(index,json){
      $('table').append("<tr><td>"+json.eno+"</td><td>"+json.ename+"</td><td>"+
                        json.sex+"</td><td>"+json.phone+"</td><td>"+json.dno+"</td></tr>");
    })
  }
});
//设置编码
req.setCharacterEncoding("utf-8");
//返回的数据要以HTML的格式存在
resp.setContentType("text/html;charset=utf8");
//返回文本数据(使用数据打印流返回结果)
PrintWriter out = resp.getWriter();
//模拟数据列表
List<Emp> empList = new ArrayList<>();
empList.add(new Emp(1,"张飞","男","123456789",10));
empList.add(new Emp(2,"刘备","男","123456789",10));
empList.add(new Emp(3,"小乔","女","123456789",20));
empList.add(new Emp(4,"大乔","女","123456789",20));
//将java中的集合转换为json类型的字符串,返回给前端
String jsonStr = JSON.toJSONString(empList);
out.write(jsonStr);
out.close();

需求:Ajax请求添加数据,传递json字符串

$.ajax({
  url: 'addEmpServlet',
  method:"post",
  /*设置请求参数类型为json*/
  contentType:"application/json;charset=utf-8",
  /*将json对象转换为字符串进行传递*/
  data:JSON.stringify({
    "ename":$('input:eq(0)').val(),
    "phone":$('input:eq(1)').val(),
    "dno":$('input:eq(2)').val()
  }),
  success:function(data){
    console.log(data);
  }
})
try{
  BufferedReader br = new BufferedReader(new InputStreamReader(
    (ServletInputStream)req.getInputStream(), "utf-8"));
  StringBuffer sb = new StringBuffer("");
  String temp;
  while ((temp = br.readLine()) != null) {
    sb.append(temp);
  }
  br.close();
  //获取到的json字符串
  String acceptjson = sb.toString();
  //将json字符串转为jsonobject对象
  Emp emp = JSONObject.parseObject(acceptjson, Emp.class);
  //将jsonobject对象转为java对象
  System.out.println(emp);

} catch (Exception e){
  e.printStackTrace();
}

注意

Java中无法直接读取json对象,需要通过第三方工具将对应的json字符串转换为对应的JavaBean,本课程中使用的是阿里提供的fastJson

request.getParameter()只能获取请求头中的信息,JSON.stringify()将数据设置在了请求体中,需要使用流进行读取,再转换为bean对象
根据Servlet规范,如果同时满足下列条件,则请求体(Entity)中的表单数据,将被填充到request的parameter集合中(request.getParameter系列方法可以读取相关数据):
1 . 这是一个HTTP/HTTPS请求
2. 请求方法是POST(querystring无论是否POST都将被设置到parameter中)
3 . 请求的类型(Content-Type头)是application/x-www-form-urlencoded
4. Servlet调用了getParameter系列方法

如果上述条件没有同时满足,则相关的表单数据不会被设置进request的parameter集合中,相关的数据可以通过request.getInputStream()来访问。反之,如果上述条件均满足,相关的表单数据将不能再通过request.getInputStream()来读取

3.1.1 When Parameters Are Available The following are the conditions that mustbe met before post form data will be populated to the parameter set:

  1. The request is an HTTP or HTTPS request.

  2. The HTTP method is POST.

  3. The content type is application/x-www-form-urlencoded.

  4. The servlet has made an initial call of any of the getParameterfamily of methods on the request object.

If the conditions are not met and the post form data is not included in the parameter set, the post data must still be available to the servlet via the request object’s input stream. If the conditions are met, post form data will no longer be available for reading directly from the request object’s input stream.

3.9 文件上传

实现图片上传(要求限制后缀以及图片的大小,将图片存储到指定的服务器中,地址存储到数据库中,最后在页面中显示出来)

文件上传需要满足以下条件

1.表单的提交属性中要有enctype=“multipart/form-data”(用于标注这是一个文件上传的表单)

2.表单的提交方式必须为POST

实现步骤:

1.关联图片上传的jar包(commons-io.jar、commons-fileupload.jar),数据库驱动包以及jstl相关的jar包(是否需要使用jstl)

2.定义表单,指定enctype属性以及提交方式为post,同时给定input类型为file的表单

3.解析提交的表单数据(request)

​ a.创建一个DiskFileItemFactory对象,作为ServletFileupload对象的工厂

​ b.创建ServletFileupload对象,通过该对象去解析request对象,返回一个FileItem集合对象

​ c.每个FileItem中包含的就是表单中对应的键值对

​ d.判断是否是文件对象

4.截取上传的文件的后缀与指定的后缀名进行匹配

5.给上传的文件起个新名字以保证名字不冲突(UUID、时间戳等等)

6.将该过名字的文件写入到指定的服务器的地址中

7.将文件的地址写入到数据库中进行存储

8.在前端的页面中显示上传的图片

前端添加页面

<c:if test="${not empty picSuffix}">
  <p>只能上传:${picSuffix}</p>
</c:if>
<c:if test="${not empty sizeMsg}">
  <p>${sizeMsg}</p>
</c:if>
<c:remove var="sizeMsg"/>
<c:remove var="picSuffix"/>
<form action="fileUploadServlet" enctype="multipart/form-data" method="post">
  <input type="text" name="uname">
  <input type="file" name="file">
  <input type="submit" value="提交">
</form>

后台上传代码

request.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
//需要一个读取磁盘的工厂对象
FileItemFactory factory = new DiskFileItemFactory();
//通过工厂生产ServletFileUpload
ServletFileUpload fileUpload = new ServletFileUpload(factory);
try {
  //设置可上传的文件大小
  fileUpload.setSizeMax(1024*10);
  //定义可以上传的文件后缀格式
  String[] suffixArr = {".png",".jpg",".gif",".jpeg"};
  //解析request对象
  List<FileItem> fileItemList = fileUpload.parseRequest(request);
  //遍历表单中的key val集合,拿到每一个具体的fileItem对象(包含了key和val值)
  for(FileItem fileItem : fileItemList){
    //判断当前的fileItem是普通的表单数据还是一个文件上传对象
    if(fileItem.isFormField()){//true代表是普通的表单字段
      String fieldName = fileItem.getFieldName();
      String fieldVal = fileItem.getString();
    } else{//false代表是文件上传字段
      String fieldName = fileItem.getFieldName();
      //getName()获取上传的文件的名字
      String fieldVal = fileItem.getName();
      //获取上传的文件的后缀
      int lastIndex = fieldVal.lastIndexOf(".");
      String suffix = fieldVal.substring(lastIndex);
      //判断上传的文件后缀是否存在于指定的格式中
      List<String> suffixList = Arrays.asList(suffixArr);
      if(!suffixList.contains(suffix)){
        session.setAttribute("picSuffix",suffixList);
        resp.sendRedirect("index.jsp");
        return;
      } else{
        /*
        * 开发中存储图片的方式
        * 1.转为base64存储到数据库(不推荐)
        * 2.图片存储到服务器上,图片所在的地址存储到数据库中
        * */
        //获取服务器中存储图片的文件夹地址
        String realPath = request.getRealPath("/upload");
        //将图片写入到指定的服务器地址中
        File file = new File(realPath);
        if(!file.exists()){
          //如果文件不存在就创建下
          file.mkdir();
        }
        //为文件生成一个随机的名字,以保证名字不冲突(此处使用UUID生成)
        //59685860-8d89-4b73-ba56-3fe9a27f11fd
        String uuid = UUID.randomUUID().toString();
        String fileName = uuid.replace("-","")+suffix;
        File writeFile = new File(realPath,fileName);
        fileItem.write(writeFile);
        //将图片的路径存储到数据库中
        String sql = "insert into t_item(pic) values(?)";
        DBUtil.curd(sql,fileName);
        //跳转到显示页面
        resp.sendRedirect("show_pic.jsp");
        return;
      }
    }
  }
} catch (SizeLimitExceededException e){
  session.setAttribute("sizeMsg","可上传的文件大小为:"+fileUpload.getSizeMax()/1024+"KB");
  resp.sendRedirect("index.jsp");
} catch (FileUploadException e) {
  e.printStackTrace();
} catch (Exception e) {
  e.printStackTrace();
}

前端展示代码

<%--此处应该通过三层架构获取数据库中的数据进行使用--%>
<%
//getContextPath()获取服务器的根目录地址
//request.getServletPath() 获取servlet地址
//request.getServletContext().getRealPath()获取上下文对象的绝对地址(对应磁盘地址)
String serverPath = request.getContextPath();
//图片存储路径
String picPath = serverPath + +"/upload/";
//获取到数据库中的图片路径集合
List<Picture> pictureList = new ArrayList<>();
Connection connection = DBUtil.newInstance();
String sql = "select * from t_item";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
  Picture picture = new Picture();
  picture.setId(resultSet.getInt(1));
  picture.setPic(resultSet.getString(2));
  pictureList.add(picture);
}
//存到域中
session.setAttribute("pictureList",pictureList);
%>
<c:forEach items="${pictureList}" var="picture">
  <img src="<%=picPath%>${picture.pic}" alt="" width="300px" height="300px" border="1px">
</c:forEach>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值