JavaWeb详解

JavaWeb动态Web资源开发

  • 静态Web: 用户看到的数据始终不变;动态Web:各人看到的信息不同
  • 动态Web:1.页面动态展示,淘宝"千人千面";2.和数据库交互

Web应用程序:给浏览器访问的程序 = 静态Web+动态Web

浏览器访问网络资源流程图
JavaWeb详解_第1张图片

  1. 客户端通过网络协议(如Http),请求进入服务器,

  2. WebSeervice收到请求,其中的**WebServerPlugin(服务插件)**判断要访问的文件是静态还是动态的.


  3. 静态直接找到资源文件(FIle System),动态的经过jsp,servlet(web技术)增强后(比如用jdbc连接数据库),

  4. 再通过WebServer(Web服务)找到对应的资源文件,然后以一种编码格式,响应回给客户端.


Http:基于Tcp的超文本传输协议

  • http使用80端口;https使用443端口,s指的是SSL/TLS协议,在HTTP和TCP/IP协议中间

  • http2.0=http/1.1版本,允许客户端与服务器连接后,获得多个资源.1.0是只能获取一个资源

请求行

  • post:参数,大小没有限制,浏览器url栏不显示数据,安全,但get比它更高效

消息头(请求头):告诉服务器怎样响应你这个请求

Accept: */* 你这个请求的数据类型
Accept-Encoding: gzip, deflate, br 编码格式
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 语言环境
cache-control: max-age=0 缓存控制,最大缓存存活时间
Connection: keep-alive 连接成功后的持久策略,keep-alive始终保持连接
Host: www.dealctr.com 主机信息

响应行(响应头):告诉客户端(浏览器)应该怎样接收数据

reFresh :告诉客户端,多久刷新一次
location :网页是否重新定位

响应状态码

200:请求响应成功

300:请求重定向

404:找不到资源

500:服务器代码错误.502:网关错误

思考:浏览器从输入网址回车,到页面展示回来,经历了什么


Web服务器

服务器是一种被动的操作,处理请求与给用户回复响应

  1. 微软windows自带IIS服务器.

  2. Tomcat是一款先进,稳定,免费的轻量级应用服务器


Tomcat

重新部署项目是重新加载当前一个项目.

  1. java的根加载器在r(runtiem)t.jar包里.

  2. 启动startup.bat后,Tomcat的默认网址是http://localhost:8000/

  3. conf下的server.xml文件是tomcat的配置文件,可以在里面修改初始参数,如端口号

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

一个webapps文件夹,就是一个Web应用

  1. webapps(Web项目)\ROOT下的index.jsp是默认访问的主页面:localhost:8080/index.jsp

浏览器访问网站流程

  1. 输入网址后,先去C:\Windows\System32\drivers\etc\hosts文件中找,有没有网址对应的域名映射(如下面的activate.navicat.com).
  2. 找到后返回对应的ip地址(activate.navicat.com对应的是127.0.0.1)
  3. 这个地址中有我们需要的web程序,找到后直接访问.
  4. 如果没有找到,就去**DNS(运营商根服务器)**上找,找的到就返回,找不到就提示"404(not found)找不到资源"

改变本机ip地址访问前缀:修改C:\Windows\System32\drivers\etc\hosts中的127.0.0.1(本地ip地址)的映射网址.

# 设置本机的ip地址中的,127.0.0.1和LiChangGe127.0.0.1的,映射域名为activate.navicat.com
127.0.0.1       LiChangGe127.0.0.1       activate.navicat.com
127.0.0.1       LiChangGe
# 都可以用
http://activate.navicat.com:8080/
http://lichangge:8080/

如果不可以,就再修改server.xml的host属性

<Host name="LiChangGe"  appBase="webapps" unpackWARs="true" autoDeploy="true">Host>

Web项目结构

  1. 一个常见的TomcatWeb项目基本结构如下:webapps(项目顶级目录)\ROOT(自己的web项目)+index.jsp\WEB-INFO(web程序配置信息目录)\web.xml(web程序配置文件)

    JavaWeb详解_第2张图片

    JavaWeb详解_第3张图片

  2. Tomcat的examples官方API

JavaWeb详解_第4张图片


Maven项目架构管理工具

  • 核心思想:约定大于配置

idea可以不配置环境变量,自带maven

# 查看maven版本
C:\Users\林木>mvn -version
Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739)

# 环境变量配置,你自己的maven安装目录
MAVEN_HOME
D:\Maven\apache-maven-3.8.3

# maven的运行程序bin目录,用idea可以不配置
M2_HOME
D:\Maven\apache-maven-3.8.3\bin

# Path引用maven,必须有
Path
%MAVEN_HOME%\bin
  1. conf\settings.xml配置maven本地仓库目录和镜像

    # 本地仓库,配置后去看看C:\用户\李长歌(这里是你自己的电脑账户)\.m2(默认隐藏的),这是maven默认的本地仓库,可以删掉.
    <localRepository>D:\Maven\apache-maven-3.8.3\mvn_propertieslocalRepository>
    # 镜像
    <mirrors>
      
        <mirror>
            <id>aliyunmavenid>
            <mirrorOf>*mirrorOf>
            <name>Nexus aliyunname>
            <url>https://maven.aliyun.com/repository/publicurl>
        mirror>
    mirrors>
    

idea创建MavenWeb项目

一键直达

JavaWeb详解_第5张图片

用模板创建

JavaWeb详解_第6张图片

JavaWeb详解_第7张图片

maven常用的全局设置

JavaWeb详解_第8张图片

创建新模块时选择Maven构建,实现联动父项目

JavaWeb详解_第9张图片

添加webapp目录

JavaWeb详解_第10张图片

添加框架支持时选择web服务,来添加web目录
JavaWeb详解_第11张图片

一个标准的web子模块

JavaWeb详解_第12张图片

  • 必须配置工件:因为访问一个网站,必须指定一个文件夹

JavaWeb详解_第13张图片

http://localhost:8080/xiaomi

pom.xml(Maven核心配置文件)代码解读

父类和子类模块的maven代码会联动

<groupId>com.changGe.ligroupId>
<artifactId>JavaWebartifactId>
<version>1.0-SNAPSHOTversion>

<modules>
  <module>XiaoMimodule>
modules>

<name>JavaWebname>
<packaging>pompackaging>

# 子类
<groupId>com.changGe.ligroupId>
<artifactId>XiaoMiartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>

<name>XiaoMi Maven Webappname>

配置运行时的java版本信息

<properties>
  <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
  <maven.compiler.source>1.8maven.compiler.source>
  <maven.compiler.target>1.8maven.compiler.target>
properties>

配置依赖


<dependencies>

dependencies> 
  1. maven遇到配置文件,无法导出或使用时 (1条消息) Maven解决配置文件无法导出或者无法生效_ReolPurion的博客-CSDN博客
  2. idea的日志文件,记载了我们的错误
  3. bat是批处理文件,可以在里面写cmd代码

HttpServlet

需要导包

<dependency>
  <groupId>javax.servletgroupId>
  <artifactId>javax.servlet-apiartifactId>
  <version>4.0.1version>
  
  <scope>providedscope>
dependency>

当存储数据时,其下所有类本质是一个Map集合

继承HttpServlet(javax.servlet.http包下),需要Maven导入依赖

只要是实现了Servlet接口,就叫做Servlet小程序

<dependency>
  <groupId>javax.servletgroupId>
  <artifactId>servlet-apiartifactId>
  <version>2.5version>
dependency>

重写方法

package com.changGe.li.servlet;

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

public class TestServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setCharacterEncoding("utf-8");
    resp.getWriter().print("Hello World");
  }

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

}

配置域名映射

<servlet>
  
  <servlet-name>heservlet-name>
  <servlet-class>com.changGe.li.servlet.TestServletservlet-class>
servlet>

<servlet-mapping>
  
  <servlet-name>heservlet-name>
  <url-pattern>/heurl-pattern>
servlet-mapping>

tomcat日志文件配置

JavaWeb详解_第14张图片


Servlet原理

  • 如果需要携带数据去到新页面,用request转发.
  • 不携带数据,只是单纯的页面跳转或刷新,就用respons重定向(respons也可以携带参数,后面再说).

浏览器向服务器发送请求,访问的是web容器(可以理解为服务器)

  1. 浏览器访问的是映射,然后返回IP地址


  2. 客户端发送请求,到达web容器.

  3. 如果是第一次的话,web容器直接访问servlet,第二次和以后,就是带着请求,找servlet的对应的service方法(如doGet()和doPost()等).

  4. 同时web容器的respons对象,也会监听接收,来自serivice的响应.监听到数据,web容器响应给客户端

JavaWeb详解_第15张图片

作用到代码上的执行流程

表单提交时携带请求和请求参数

  1. 按下submit提交表单,浏览器向action的值的地方发送请求
  2. 提取表单中所有有name属性的元素,把他们的值作为参数传递:如localhost:8080/Super/jsp/user.do?method=modify&uid=26
  1. 服务器找到web.xml,看有没有对应的映射
  2. 找到这个映射对应的servlet
<servlet>
  <servlet-name>modifyPasswordservlet-name>
  <servlet-class>com.changGe.li.servlets.ModifyPasswordServletservlet-class>
servlet>
<servlet-mapping>
  <servlet-name>modifyPasswordservlet-name>
  
  <url-pattern>/jsp/user.dourl-pattern>
servlet-mapping>


<url-pattern>/*.dourl-pattern>

  1. 优先访问最接近的映射
<a href="${pageContext.request.contextPath}/hello">

<url-pattern>/hellourl-pattern>
  
<url-pattern>/hello.dourl-pattern>
  1. 调用对应的service(),如上面的表单提交方式是get,就找到doGet();

  2. 执行其中的实现代码

public class ModifyPasswordServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   	//重定向发送一个请求且携带参数
    resp.sendRedirect(req.getContextPath()+"/jsp/user.do?method=query");
    
  }

ServletContxet:管理所有Servlet,实现Servlet间共享数据

每个程序都有一个对应的上下文对象(ServletContext)
JavaWeb详解_第16张图片

实现Servlet间共享数据

<servlet>
  <servlet-name>heservlet-name>
  <servlet-class>com.changGe.li.servlet.TestServletservlet-class>
servlet>

<servlet-mapping>
  <servlet-name>heservlet-name>
  <url-pattern>/hellourl-pattern>
servlet-mapping>



<servlet>
  <servlet-name>helloservlet-name>
  <servlet-class>com.changGe.li.servlet.TestServletOneservlet-class>
servlet>

<servlet-mapping>
  <servlet-name>helloservlet-name>
  <url-pattern>/hello.dourl-pattern>
servlet-mapping>

public class TestServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setCharacterEncoding("utf-8");

    ServletContext servletContext = this.getServletContext();

    servletContext.setAttribute("name","武则天");

  }
}  


public class TestServletOne extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setCharacterEncoding("utf-8");

    ServletContext servletContext = this.getServletContext();

    Object name = servletContext.getAttribute("name");

    System.out.println(name);

  }
  
}

servletContext转发

// 一定要写上/代表当前web项目目录下
servletContext.getRequestDispatcher("/info.jsp").forward(req,resp);

配置初始化参数后,其下所有servlet都可以获取

<context-param>
  <param-name>nameparam-name>
  <param-value>李长歌param-value>
context-param>
ServletContext servletContext = this.getServletContext();

String name = servletContext.getInitParameter("name");
System.out.println(name);//李长歌

项目构建后会被打包,在target/项目/WEB-INF/classes下

classes俗称类路径

JavaWeb详解_第17张图片

如果java目录下的资源没有导入成功,在pom.xml中添加下列代码

然后删除target目录,重新部署或重启tomcat服务器

filtering的作用 (4条消息) maven Filtering true 作用_滕青山YYDS的博客-CSDN博客


<build>
  <resources>
    <resource>
      
      <directory>src/main/javadirectory>
      <includes>
        <include>**/*.propertiesinclude>
        <include>**/*.xmlinclude>
      includes>
      
      <filtering>truefiltering>
    resource>
  resources>
build>

读取资源文件

public class TestServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setCharacterEncoding("utf-8");

    ServletContext servletContext = this.getServletContext();

    InputStream resourceAsStream = servletContext.getResourceAsStream("/WEB-INF/classes/com/changGe/li/lib/test1.properties");

    Properties properties = new Properties();
    properties.load(resourceAsStream);

    String name = properties.getProperty("name");

    System.out.println(name);

  }
  
}  

Request请求转发:直接请求另一个资源的路径,直接写相对路径就可以

服务器接收到浏览器的请求后,针对这个请求自动创建Request(请求)和Respons(响应)对象

HttpServlet源码中有初始的状态码

public interface HttpServletRequest extends ServletRequest {

    /**
     * String identifier for Basic authentication. Value "BASIC"
     */
    public static final String BASIC_AUTH = "BASIC";

    /**
     * String identifier for Form authentication. Value "FORM"
     */
    public static final String FORM_AUTH = "FORM";

    /**
     * String identifier for Client Certificate authentication. Value "CLIENT_CERT"
     */
    public static final String CLIENT_CERT_AUTH = "CLIENT_CERT";

请求转发时,可以携带参数

req.setAttribute("name","李长歌");
req.getRequestDispatcher("info.jsp").forward(req,resp);

获取复选框中数据

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

  //获取所有的值,返回数组
  String[] names = req.getParameterValues("check");
  for (String name : names) {
    System.out.println(name);
  }

}

Respons响应重定向:从你现在的路径去到一个新的路径,url栏会发生变化,要写绝对路径

//req.getContextPath() = 当前项目目录路径:localhost:8080
resp.sendRedirect(req.getContextPath()+"/info.jsp");

重写向时不会传递参数

响应数据

//把集合响应回去,ajax用做函数的data
resp.setContentType("application/json");
resp.setCharacterEncoding("utf-8");
try {
  PrintWriter writer = resp.getWriter();

  //将集合变成json对象输出
  writer.write(JSON.toJSONString(result));

  writer.flush();
  writer.close();

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

下载文件:respons负责把读取到的数据,响应回给浏览器

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

  //以utf-8解析请求
  resp.setCharacterEncoding("utf-8");
  //让浏览器以utf-8模式解析响应
  resp.setHeader("Content-type", "text/html;charset=UTF-8");
  
  String imgUrl = "E:\\JAVA\\JavaWeb\\XiaoMi\\src\\main\\resources\\test.properties";

  //从最后一个/开始截取,获得文件名
  String fileName = imgUrl.substring(imgUrl.lastIndexOf("\\") + 1);

  //输入流
  FileInputStream fileInputStream = new FileInputStream(imgUrl);  //设置响应头:内容类型,附件;文件名字=
  resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));

  //获取响应的文件输出流
  ServletOutputStream outputStream = resp.getOutputStream();

  byte[] bytes = new byte[10];

  int length = 0;

  while ((length = fileInputStream.read(bytes)) != -1){
    outputStream.write(bytes,0,length);
  }

}

    resp.setCharacterEncoding("utf-8");
    resp.setHeader("Content-type", "text/html;charset=UTF-8");

    String imgUrl = "E:\\JAVA\\JavaWeb\\XiaoMi\\src\\main\\resources\\test.properties";

    //从最后一个/开始截取,获得文件名
    String fileName = imgUrl.substring(imgUrl.lastIndexOf("\\") + 1);

    //输入流
    FileInputStream fileInputStream = new FileInputStream(imgUrl);
    BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
    FileReader fileReader = new FileReader(imgUrl);


    //设置响应头:内容类型,附件;文件名字=
    resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));

    //获取响应的文件输出流
    ServletOutputStream outputStream = resp.getOutputStream();
    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
   // PrintWriter writer = resp.getWriter();

    byte[] bytes = new byte[10];
    char[] chars = new char[1024];

    int length = 0;

/*
    while ((length = fileInputStream.read(bytes)) != -1){
      outputStream.write(bytes,0,length);
    }*/

    while ((length = bufferedInputStream.read(bytes)) != -1){
      bufferedOutputStream.write(bytes,0,length);
      bufferedOutputStream.flush();
    }

    bufferedInputStream.close();
    bufferedOutputStream.close();
    outputStream.close();
    fileInputStream.close();

验证码

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

  //宽高,以8位RGB色彩模式打包出去
  BufferedImage bufferedImage = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);

  //得到支笔
  Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();

  graphics.setBackground(new Color(0xEBA9E7));
  graphics.setColor(new Color(0x00F4E0));
  //实心矩形,x,y,width,height
  graphics.fillRect(400,400,600,600);

  //字体,罗马基线,大小
  graphics.setFont(new Font("方正舒体",Font.ROMAN_BASELINE,50));
  //要写的字符串,x,y
  graphics.drawString(verifyCode(),50,200);


  //让浏览器以图片格式解析
  resp.setContentType("image/png");

  //设置浏览器的3秒刷新一次
  resp.setHeader("refresh","3");
  //多少秒后过期
  resp.setDateHeader("expires",-1);
  //缓存控制不缓存
  resp.setHeader("Cache-Control","no-cache");
  //程序不缓存,作用和上面一样,一般两者一起使用
  resp.setHeader("Program","no-cache");

  //把图片,按照格式,写到一个地方去
  ImageIO.write(bufferedImage,"png",resp.getOutputStream());

}

public String verifyCode(){
  Random random = new Random();

  /**
     * valueOf()的底层:return (obj == null) ? "null" : obj.toString();
     * 它不会报空指针异常,但是如果是null,也会直接变成"null"
     */
  String number = String.valueOf(random.nextInt(10000000));

  StringBuilder stringBuilder = new StringBuilder(number);

  //保证验证码是7个字符
  for (int i = 0; i < 7 - number.length(); i++) {
    stringBuilder.append("x");
  }
  System.out.println(stringBuilder);

  return stringBuilder.toString();

}

请求重定向与请求转发的区别

  1. 请求重定向
    使用responce.sendRedirect(“xx.jsp”)来进行重定向。是客户端的行为:即客户端会访问两次,第一次访问后会立即跳转到第二个重定向页面上,从本质上讲等于两次请求,而前一次的请求封装的request对象不会保存,地址栏的URL地址会改变。

JavaWeb详解_第18张图片

2、请求转发
使用request.getRequestDispatcher(“xx.jsp”).forward(request,response)请求转发。forward(request,response)用于保存内置对象request和response。是服务器的行为服务器会代替客户端去访问转发页面,从本质是一次请求转发后请求对象会保存,地址栏的URL地址不会改变。

JavaWeb详解_第19张图片


Cookie(客户端技术)

有状态会话:会话(客户端和服务器间的一次交流seesion) + 缓存(cookie)

本质是把用户的数据写给浏览器,由浏览器保存

用cookie读取上一次访问时间:刷新一次数据也刷新

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  String format = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒").format(new Date());
    Cookie lastTime = new Cookie("lastTime", format);

    boolean flag = true;

    Cookie[] cookies = req.getCookies();

    if(cookies != null && cookies.length > 0){
      for (Cookie cookie : cookies) {

        String value = cookie.getName();

        if(value != null && value.equals("lastTime")){
          flag = false;

          //特定格式解析字符串,常用于中文解码
          req.setAttribute("lastTime", URLDecoder.decode(cookie.getValue(),"utf-8"));

          //更新登录时间
          cookie.setValue(format);
          /**
           * 缓存时间0秒,就是删除cookie,
           * 只有用户关闭浏览器才会删除cookie
           */
          cookie.setMaxAge(60 * 60);//单位是秒
          resp.addCookie(lastTime);

          req.getRequestDispatcher("index.jsp").forward(req,resp);
          return;
        }

      }//for

    }

    if(flag){
      resp.addCookie(lastTime);

      req.setAttribute("msg", "这是您第一次登录");
      req.getRequestDispatcher("info.jsp").forward(req,resp);
    }

}

精简版

String format = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒").format(new Date());

Cookie[] cookies = req.getCookies();

boolean flag = true;

if(cookies != null && cookies.length > 0){
  for (Cookie cookie : cookies) {

    String value = cookie.getName();

    if(value != null && value.equals("lastTime")){
      flag = false;
      req.setAttribute("msg", "您上一次登录时间:"+URLDecoder.decode(cookie.getValue(),"utf-8"));
    }

  }//for

}

if(flag){
  req.setAttribute("msg", "这是您第一次登录");
}

Cookie lastTime = new Cookie("lastTime", format);
resp.addCookie(lastTime);

req.getRequestDispatcher("info.jsp").forward(req,resp);

浏览器的applition里可以看到cookie缓存.,可以删除掉特定的cookie

JavaWeb详解_第20张图片

只有清除了localhost:8080的缓存后,cookie才真正消失,每次浏览器都默认存储了一些cookie

JavaWeb详解_第21张图片

用everything我们可以找到**cookiie的存放位置,**只是我们不能用(没有权限,到AC就看不到了)

JavaWeb详解_第22张图片

浏览器最多发送300个coolie,约是15个网站的,最大共1200kb数据.


Seesion(服务器技术)

服务器会为每个浏览器都创建一个seesion,在服务器上保存用户的数据,全局作用域,直到浏览器关闭.

seesion在浏览器默认存储30分钟

<session-config>
  
  <session-timeout>15session-timeout>
session-config>
  1. seesion存储在cookie中,去浏览器的applition中找到cookie看,或者请求头的cookie里保存的有

  2. 底层代码相当于response.addcookie(new Cookie(“JSSSIONID”,value));

    //约等于此,不过是服务器内部实现的,我们这样的不行
    resp.addCookie(new Cookie("JSSSIONID", "123"));
    
  3. seesion可以存储对象,能用seesion就不要用context

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

  HttpSession session = req.getSession();
  session.setAttribute("name","李长歌");
  session.setAttribute("age","18");
  session.setAttribute("sex","女");

  //如果是新创建的
  if(session.isNew()){
    Enumeration<String> attributeNames = session.getAttributeNames();

    //sessionId是D6E8AD19A02583A4CB3DA5F36F86EB28
    System.out.println("sessionId是"+session.getId());

    /**
       * 结果:
       * 女
       * 李长歌
       * 18
       * 
       * 页面自动回到浏览器主页了,因为session注销了
       */
    while (attributeNames.hasMoreElements()){
      String name = attributeNames.nextElement();
      System.out.println(session.getAttribute(name));

      //可以用于用户退出时删除用户信息
      session.removeAttribute(name);
    }

    //注销
    session.invalidate();

  }

}

和全文索引的应用场景一样

JavaWeb详解_第23张图片


JSP(Java服务页面JavaServerPages)

本质就是Servlet(小应用服务程序)

  1. PHP语言不能承载大访问量,微软ASP是HTML+VB脚本,用C#语言开发
  2. jsp基于Java的B(browser)S架构,sun公司开发,可以承载三高:高并发,高性能和高可用(数据要高可靠,服务要高可用)

index.jsp源码分析

服务器访问任何资源,本质是访问Servlet. 因为idea的tomcat>work目录下的jsp页面最后变成了.java文件.

JSP源码分析__板蓝根_的博客-CSDN博客_jsp源码

这个博客没有找到,就在这里面找

JavaWeb详解_第24张图片

这是我最终找到的路径:C:\Users\林木\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\61c407dd-c490-4564-89ac-71da59dbac6b\work\Catalina\localhost\XiaoMi\org\apache\jsp

  1. 继承自HttpJspBase类,HttpJspBase继承自HttpServlet类

    public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    
  2. 初始化,销毁和服务三大方法

    public void _jspInit() {
    }
    
    public void _jspDestroy() {
    }
    
    public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
    
  3. 判断请求类型.method()

    if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
            response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
            return;
          }
    
  4. 初始化对象有context(改名application),page = this…

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;
    
  5. 有个try里面,在输出页面前为一些默认的对象,如pageContext等赋值,我们可以在jsp页面中直接使用

    try {
      response.setContentType("text/html;charset=utf-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                                                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;
    
      out.write("\n");
      out.write("\n");
      out.write("\n");
      out.write("\n");
    

JavaWeb详解_第25张图片

<%--这是jsp表达式,加"="号专门用来输出--%>
<input type="text" value="名字是:<% String name="李长歌"; %>
                                                  <%= name%>"/>

<!--对应到jsp.java底层->
out.write("        

jsp语法

javax-servlet-jsp-api依赖

<dependency>
  <groupId>javax.servlet.jspgroupId>
  <artifactId>jsp-apiartifactId>
  <version>2.0version>
dependency>

html的注释可以在控制台看到,jsp不显示

<%--jsp脚本在_jspService()中--%>

<% for(int i = 0 ; i < 5; i++){%>
  

hello world<%= i %>

<% } %> <%--jsp声明在jsp类中--%> <%! public void test(){} %>

自定义错误页面

<%@ page pageEncoding="utf-8" %>

<%--jsp标签:写在页面最上面,作为配置.显式声明这是一个错误页面--%>
<%@ page  isErrorPage="true" %>
<%@page errorPage="info.jsp" %>
<html>

<body>
    <% int i = 1 / 0;%>
body>
html>

<error-page>
  
  <error-code>500error-code>
  <location>/info.jsplocation>
error-page>

添加公共页面

<%--是将页面中的代码提出来,然后和现有页面代码拼接在一起--%>
<%@ include file="info.jsp" %>
out.write("\r\n");
out.write("    Info\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("

info下的h3

\r\n"); out.write(" \r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\n"); out.write("\n"); out.write("\n"); out.write("\n"); <%--第二种方法--%> <%--这是调用其它页面,不会出现代码冲突的问题--%> out.write("

22222

\n"); org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "info.jsp", out, false); out.write("\n");

jsp标签

<%--作用域是request--%>

  

name is:${pageContext.request.getParameter("name")}

static包下一般放前端的资源,如css,img,js和plugins等.

WEB-INF目录对用户不可见


EL表达式

作用域优先级:pageScope > requestScope > sessionScope > pageContext(页面运行时)>applicationScope(服务器)

如果没有给定取值作用域,则默认从pageScope开始查找,找到则返回,没找到则按照上述顺序继续查找,以此类推,知道找到为止。

如果最终没有找到,则返回null


九大对象及作用域

JavaWeb详解_第26张图片

  1. page对象的数据出了页面就消失了;
  2. seesion间可以互相访问,但是当要统计服务器中所有数据时,就要去到更高级的application对象了:
  3. page -> request -> seesion -> pageContext>application
  4. 类似于双亲委派机制,先从应用类加载器找,找不到用扩展类加载器(ext中的jar包),还找不到就最后去根加载器(rt目录下的jar包).

  <%
  pageContext.setAttribute("name","李长歌");
  session.setAttribute("name1","session");
  request.setAttribute("name2","request");
  application.setAttribute("name3","application");

  //获取值
  String name5 = (String) pageContext.findAttribute("name5");
  %>
  <%--找不到也不报错,el表达式自动过滤null值,不显示在页面中--%>
  
  name:${name}
  name1:${name1}
  name2:${name2}
  name3:${name3}
  name5:${name5}

设置作用域

<%
	//等同于session.setAttribute("key","value");
	pageContext.setAttribute("key","value",pageContext.SESSION_SCOPE);
%>

作用域保存数据的重要性从低到高:request(客户端看完即删,新闻图片等.) < seestion(需要二次使用,但不事关关键业务,如购物车) < application(需要全局作用,事关关键业务,如聊天数据)

<%
	pagecontext.setAttribute("name","李长歌");

	request = pagecontex.getRequest();
	requext.forward("forward.jsp");
%>

<%--页面转发时,会自动将数据也给转发走--%>
<%--info页面下--%>
<%
	String name = (String)pagecontext.getAttribute("name");
	out.print(name);
%>

jstl标签

jstl-api依赖和standrard标签库依赖

如果报错说找不到这个包,可能是因为tomcat中没有这个jar包,直接粘到tomcat的work目录中去

<dependency>
  <groupId>jstlgroupId>
  <artifactId>jstlartifactId>
  <version>1.2version>
dependency>
<dependency>
  <groupId>taglibsgroupId>
  <artifactId>standardartifactId>
  <version>1.1.2version>
dependency>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--写在最顶上,导入core标签库,字首(也就是前缀)为c,字首可以随便写--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<%--如果报错:根据标记文件中的TLD或attribute指令,attribute[items]不接受任何表达式
  是因为和tomcat中的jstl-api.jar包不兼容
  就换成下面这个--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>

switch循环和if判断


  <%--定义变量sorce,赋值为100--%>
    

    <%-- 把test判断的值,赋值给number --%>
    
        
        <%--循环判断--%>
        
            <%--会自动break--%>
            

                

            
        
    

增加for

<%
    ArrayList arrayList = new ArrayList<>();
    arrayList.add(0,0);
    arrayList.add(1,1);
    arrayList.add(2,2);
    arrayList.add(3,3);
    request.setAttribute("list",arrayList);
%>

<%--从1遍历到3,一次i+2,最后只输出1,3--%>

    <%--从list集合中取值,值为item,打印item到页面上--%>
    

 
  

JavaBean(咖啡豆)

jsp:useBean的用法_远方©的博客-CSDN博客_jsp:usebean

  • 有无参构造+属性私有化,有对应的set,get方法
<%--实例化对象User--%> <%--获取表单中的所有值,并一一赋值给User类,对应的set方法--%> <%--从user对象中取值--%> <%--李长歌--%> <%--18--%> <%=user.getAge()%><%--18--%>
<form action="index.jsp" method="get">

    <input type="text" name="name" value="李长歌">
    <input type="text" name="age" value="18">
    <input type="submit" value="请提交">
form>

<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>

<%--只给setName()赋值--%>
<jsp:setProperty name="user" property="name"/>

<jsp:getProperty name="user" property="name"/><%--李长歌--%>

<%--尽管age的input输入了值,还是为0--%>  
<jsp:getProperty name="user" property="age"/><%--0--%>
<%=user.getAge()%><%--0--%>
<form action="index.jsp" method="get">

    <input type="text" name="name" value="李长歌">
    <input type="text" name="age" value="18">
    <input type="submit" value="请提交">
form>

<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>

<%--给setName()赋值"武则天"--%>
<jsp:setProperty name="user" property="name" value="武则天"/>

<%--最后得到的还是"武则天"--%>
<jsp:getProperty name="user" property="name"/>
<form action="index.jsp?name=李世民" method="get">

    <input type="text" name="name" value="李长歌">
    <input type="text" name="age" value="18">
    <input type="submit" value="请提交">

form>

<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>

<%--从请求的参数取值name,赋值给user对象的setName--%>
<jsp:setProperty name="user" property="name" param="name"/>

<%--最后得到的还是"李长歌"--%>
<jsp:getProperty name="user" property="name"/>
<body>

<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>

<%--赋值给user对象的setName--%>
<jsp:setProperty name="user" property="name" value="武则天"/>

<%--"武则天"--%>
<jsp:getProperty name="user" property="name"/>

body>

MVC(Model view controller模型视图控制器)三层架构

三层架构就是在Model层进行dao(数据持久),service(业务处理)的中间过渡

JavaWeb详解_第27张图片


Filter过滤器:在服务器与资源文件之间过滤数据

mysql-connector-java依赖,一定要用和自己数据库版本一样的jar包(依赖),不同版本的结构不同

<dependency>
  <groupId>mysqlgroupId>
  <artifactId>mysql-connector-javaartifactId>
  <version>8.0.27version>
dependency>

实现Filter下的方法init初始化,doFIlter业务,destroy销毁,可以只实现doFilter()

@Override
public void init(FilterConfig filterConfig) throws ServletException {
  Filter.super.init(filterConfig);
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

}
@Override
public void destroy() {
  Filter.super.destroy();
}

用filter实现登录验证

  1. 用户提交表单后,找到对应的servlet

欢迎登录

${error}${param.get("error")}
<servlet>
  <servlet-name>loginservlet-name>
  <servlet-class>com.changGe.li.servlets.LoginServletservlet-class>
servlet>
<servlet-mapping>
  <servlet-name>loginservlet-name>
  <url-pattern>/loginurl-pattern>
servlet-mapping>
  1. 这里面取值用户的信息,判断是否成功.

成功了就跳转主页,失败是失败页面.

public class LoginServlet extends HttpServlet {

  //数据库工具类
  private UserService userService = new UserServiceImpl();

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

    String userCode = req.getParameter("userCode");
    String password = req.getParameter("password");

		//根据用户名和密码找到User对象
    User user = userService.getUser(userCode,password);

    //不为空就保存到session中
    if(user != null){
      req.getSession().setAttribute("user",user);

      resp.sendRedirect(req.getContextPath()+"/jsp/frame.jsp");
    }else {
      //失败就重定向
      req.setAttribute("error","用户名或密码错误");

      req.getRequestDispatcher("login.jsp").forward(req,resp);
    }


  }
}
  1. 注销就是让用户对应的seesion删除掉:req.getSession().removeAttribute(“user”);

注销后回到登录页面.

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  User user = (User)req.getSession().getAttribute("user");

  if(user != null){
    req.getSession().removeAttribute("user");
    resp.sendRedirect(req.getContextPath()+"/login.jsp");
  }else {
    //为空,代表已经被删除了
    req.getRequestDispatcher("error.jsp").forward(req,resp);
  }

}
  1. 有个过滤器每次有新请求时,都判断用户的seesion是否为空

<filter>
  <filter-name>forcoLoginfilter-name>
  <filter-class>com.changGe.li.filters.ForcoLoginFilterfilter-class>
filter>
<filter-mapping>
  <filter-name>forcoLoginfilter-name>
  <url-pattern>/jsp/*url-pattern>
filter-mapping>
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

  HttpServletRequest req = (HttpServletRequest) request;
  HttpServletResponse resq = (HttpServletResponse) response;

  //从session中获取user对象
  User user = (User)req.getSession().getAttribute("user");

  if(user == null){
    request.setAttribute("error","请先登录,谢谢");

    resq.sendRedirect(req.getContextPath()+"/login.jsp?error="+ URLEncoder.encode("请先登录,谢谢","utf-8"));

  }else{
    //找到之后直接进入主页.不用登录
    //必须让请求和响应 链接 起来后,程序才会继续进行,不然就卡住
    chain.doFilter(req, resq);
  }

}
  1. user的session经常用到,可以提取成静态常量
public static final String USER_SEESION = "user";

用这个思想,实现一个判断用户vip等级的demo.

主要是过滤器根据username获取到的seesion,进而获取对象,然后判断等级

等级:

info.jsp


    

您的等级是:${grade}

web.xml

<filter>
  <filter-name>verifyfilter-name>
  <filter-class>com.changGe.li.FilterTestfilter-class>
filter>
<filter-mapping>
  <filter-name>verifyfilter-name>

  
  <servlet-name>heservlet-name>
  
  <url-pattern>/*url-pattern>
filter-mapping>

<servlet>
  <servlet-name>heservlet-name>
  <servlet-class>com.changGe.li.servlet.TestServletservlet-class>
servlet>
<servlet-mapping>
  <servlet-name>heservlet-name>
  <url-pattern>/hellourl-pattern>
servlet-mapping>

TestServlet

public class TestServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String grade = req.getParameter("grade");
    req.getSession().setAttribute("grade",grade);

    req.getRequestDispatcher("info.jsp").forward(req,resp);
  }

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

}

FilterTest

public class FilterTest implements Filter {

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resq = (HttpServletResponse) response;

    String grade = req.getParameter("grade");
    if(grade == null || grade.equals("")){

      //从session中获取user对象
      grade = String.valueOf(req.getSession().getAttribute("grade"));

    }

    if(grade != null && !grade.equals("")){
      System.out.println("if中的"+grade);

      switch (grade){
        case "1":
          req.setAttribute("grade","1");
          break;
        case "2":
          req.setAttribute("grade","2");
          break;
        default:
          req.setAttribute("grade","3");
      }
      chain.doFilter(req, resq);
    }else{
      resq.getWriter().write("等级为空");
    }

  }

}

Listener监听器

各种类型的监听器都可以实现,如HttpSestionListener,始终监听session域

index.jsp

<form action="hello" method="get">

  <input type="submit" value="提交">

form>

<listener>
    <listener-class>com.changGe.li.ListenerTestlistener-class>
listener>

监听到有session被创建时,就创建一个sess的session,赋值

有session被销毁时,得到资源

public class ListenerTest implements HttpSessionListener {
  //有session被创建时
  @Override
  public void sessionCreated(HttpSessionEvent se) {
    //创建session
    se.getSession().setAttribute("sess", URLEncoder.encode("监听到session被创建了"));
  }

  //有session被销毁时
  @Override
  public void sessionDestroyed(HttpSessionEvent se) {
    Object source = se.getSource();
		//输出结果org.apache.catalina.session.StandardSessionFacade@8539a6f
    System.out.println("监听到的资源是:"+source.toString());
  }

}

servlet中先创建一个session,触发sessionCreated()

然后得到sessionCreated()创建的session打印出去

最后删除自己创建的session

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

  //创建session
  req.getSession().setAttribute("name","李长歌");

  //写去session
  resp.getWriter().write(String.valueOf(req.getSession().getAttribute("sess")));

  //删除session
  req.getSession().invalidate();

}

实现统计在线人数

只要有个session被创建后,在线人数就+1;销毁时在线人数-1

servlet

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

  //一秒刷新一次
  resp.setHeader("refresh","1");

  //一定要从ServletContext中取值,ServletContext监控所有session
  String online = String.valueOf(req.getServletContext().getAttribute("online"));

  req.setAttribute("online",online);

  req.getRequestDispatcher("info.jsp").forward(req,resp);

}
public class ListenerTest implements HttpSessionListener {

  //有session被创建时
  @Override
  public void sessionCreated(HttpSessionEvent se) {

    //在最高的作用域servletContext操作session
    ServletContext servletContext = se.getSession().getServletContext();

    Integer online = (Integer) servletContext.getAttribute("online");

    if(online == null || online <= 0){
      online = 1;
    }else {
      online ++;
    }

    servletContext.setAttribute("online",online);

  }

  //有session被销毁时
  @Override
  public void sessionDestroyed(HttpSessionEvent se) {
    ServletContext servletContext = se.getSession().getServletContext();

    Integer online = (Integer)servletContext.getAttribute("online");

    if(online == null || online < 0){
      online = 0;
    }else {
      online --;
    }

    servletContext.setAttribute("online",online);

  }

}

info.jsp


  

在线人数是:${online}

web.xml

<session-config>
  
  <session-timeout>1session-timeout>
session-config>

实现GUI窗口关闭,实现它的子类,用的是适配器模式

public static void main(String[] args) {
  JFrame jFrame = new JFrame();

  //适配器模式,windowAdapter实现了WindowListener
  jFrame.addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosed(WindowEvent e) {
      super.windowClosed(e);
    }
  });

}

WindowAdapter实现了WindowListener

public abstract class WindowAdapter
    implements WindowListener, WindowStateListener, WindowFocusListener
{

juit单元测试依赖


<dependency>
  <groupId>junitgroupId>
  <artifactId>junitartifactId>
  <version>4.13.2version>
  <scope>testscope>
dependency>

q.setAttribute(“online”,online);

req.getRequestDispatcher(“info.jsp”).forward(req,resp);

}


```java
public class ListenerTest implements HttpSessionListener {

  //有session被创建时
  @Override
  public void sessionCreated(HttpSessionEvent se) {

    //在最高的作用域servletContext操作session
    ServletContext servletContext = se.getSession().getServletContext();

    Integer online = (Integer) servletContext.getAttribute("online");

    if(online == null || online <= 0){
      online = 1;
    }else {
      online ++;
    }

    servletContext.setAttribute("online",online);

  }

  //有session被销毁时
  @Override
  public void sessionDestroyed(HttpSessionEvent se) {
    ServletContext servletContext = se.getSession().getServletContext();

    Integer online = (Integer)servletContext.getAttribute("online");

    if(online == null || online < 0){
      online = 0;
    }else {
      online --;
    }

    servletContext.setAttribute("online",online);

  }

}

info.jsp


  

在线人数是:${online}

web.xml

<session-config>
  
  <session-timeout>1session-timeout>
session-config>

实现GUI窗口关闭,实现它的子类,用的是适配器模式

public static void main(String[] args) {
  JFrame jFrame = new JFrame();

  //适配器模式,windowAdapter实现了WindowListener
  jFrame.addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosed(WindowEvent e) {
      super.windowClosed(e);
    }
  });

}

WindowAdapter实现了WindowListener

public abstract class WindowAdapter
    implements WindowListener, WindowStateListener, WindowFocusListener
{

juit单元测试依赖


<dependency>
  <groupId>junitgroupId>
  <artifactId>junitartifactId>
  <version>4.13.2version>
  <scope>testscope>
dependency>

ORM对象关系映射 :就是实体类和数据库字段互相对应

你可能感兴趣的:(JavaWeb,java,tomcat,java-ee,maven)