0


青梅煮酒Java Web 笔记

Java Web 笔记

一些基本概念:

image-20220810175650208

访问一个服务器要打冒号加端口,一般服务器默认是80端口,这时不用打端口名。Ip与端口难记,就有了域名,会先去访问DNS,可以被解析成IP。

Http协议是搭建在TCP协议之上的

Tomcat就是HTTPServer,浏览器与HTTPServer是浏览器request与服务器response的关系。HTTP建立一个TCP连接,将request或response以文件形式发送然后TCP向上提交给对方。浏览器收到response将头去除剩下的就是HTML网页交给内核渲染生成DOM。

image-20220810221057820

Tomcat

image-20220810223611594

原始方法启动

在Tomcat本地目录中bin文件夹startup.dat文件双击,就启动了一个黑框:

image-20220810224031761

image-20220810224755176

JAVA_HOME这个环境变量配置好了黑框才出来

localhost:8080浏览器输入这,就看到了熟悉的黄猫页面。黑框关闭就服务关闭了刷新浏览器无效了。

已经打开一个黑框,再双击startup.dat,此时去logs文件夹下看.log文件,里面有错误信息,端口被挤占。一个端口只能被绑定一次。

新开一个cmd命令行:

image-20220810225545654

输入这个可查看被谁绑定了:

image-20220810225657929

conf文件夹server.xml文件能将8080端口改成其它数字(端口)。浏览器输入修改的端口访问成功。

鼠标放在这个小框上右键属性:

image-20220810224156490

image-20220810224254234

属性显示如图GBK

conf文件夹下logging.properties这个文件

image-20220810224412044

如图修改成GBK就不会乱码。

项目部署

image-20220810230417234

三个方法都是Tomcat本地安装目录中的为文件/文件夹,一般推荐第三种方法

1.

已经双击startup.dat启动服务器的情况下:本地tocat安装目录->webapps文件夹:新建一个hello文件夹将001.html放到里面。

浏览器:localhost:8080/hello/001.html

1.01优雅点的方法:001.html右键变成压缩文件001.zip。文件001.zip改名为hello.war。

hello.war直接放到webapps文件加下等一会自动会出现hello文件夹,里面有html文件。同样浏览器:localhost:8080/hello/001.html

2.

image-20220811100039718

如图,在全文件最底下新增context标签,docbase是001.html所在的文件夹,path是设定一个tomcat虚拟路径。

双击启动Tomcat后浏览器访问:localhost:8080/hello2/001.html

server.xml是比较敏感的一个文件,一般不要修改。

3.

server.xml是比较敏感的一个文件,一般不要修改。

所以有了第三种方法,在localhost文件夹下添加一个hello3.xml内容如下:

image-20220811100824195

同样启动后浏览器访问:localhost:8080/hello3/001.html

IDEA中建立

云盘里有个五分钟视频讲破解。

刚开始学习,不用maven创建,newproject时选择 java Enterprise,然后继续就可以。

常见完成之后在总的项目名称上右键Add Framework:

image-20220811113341995

然后:

image-20220811113426285

这时候出现了Web文件夹,里面有web-INF文件夹,index.jsp等

然后配置tomcat选择local

image-20220811114101938

新建的项目配置tomcat时点击上图加号出现两个选项点击artifact,然后就出现了这个路径。它是指明web文件夹路径。

image-20220811114234726

浏览器访问的时候直接localhost:8080/myweb_war_exploded默认访问index.jsp新建了其他页面文件时,斜杠加文件名。

这时候已经将logging.properties中UTF-8全部改为GBK了。不出现乱码

已经启动tomcat时又新建了页面文件,要点击deploy或者重启。

Servlet

概念

Servlet是一种服务器端的Java应用程序,独立于平台和协议,可动态生成web页面。

Servlet是使用JavaServlet接口,运行在Web应用服务器上的Java应用程序,可以对web浏览器或其它Http客户端发送的请求进行处理。

处理客户传来的HTTP请求并返回响应。通常所说的servlet就是指HttpServlet,能处理的请求有doGet等等,开发时可直接继承javax.servlet.http.HttpServlet。

Servlet需要在web.xml中描述,映射Servlet的名字,配置Servlet类,初始化参数。安全配置,URL映射以及启动优先权等。

添加servlet

引入包,自己看图:

image-20220811132654592

Web.xml的配置

一个Servlet对象正常运行需要进行适当配置,告知哪个请求调用哪个Servlet对象处理。

<!--    <servlet>-->起到注册作用
<!--        <servlet-name>demo01</servlet-name>-->     //这个类自定义名字
<!--        <servlet-class>web.servlet.MyServletDemo01</servlet-class>--> //指定完整位置包名,类名
<!--        <load-on-startup>0</load-on-startup>-->
        <!-- 如果放的是一个0或者正整数,系统将在启动后加载
             如果放的是一个负数(缺省是-1),那么就是懒加载-->
<!--    </servlet>-->

<!--    <servlet-mapping>-->映射访问servlet的url
<!--        <servlet-name>demo01</servlet-name>-->     类名要与想调用的类的<servlet>中的<servlet-name>一样
<!--        <url-pattern>/demo01</url-pattern>-->    当这个路径来访时调用类的service
<!--    </servlet-mapping>-->

配置好需重启tomcat
 

此时运行tomcat时只显示连接成功,浏览器默认访问index当访问:localhost:8080/myweb_war_exploded/demo01时

控制台输出了MyServletDemo01类的service方法的输出内容。

生命周期

image-20220811182012450

package Web.servlet;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet(urlPatterns = {"/demo01"})

public class MyServletDemo01 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init()....................");
    }

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

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("service.......................");
    }

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

    @Override
    public void destroy() {
        System.out.println("destory.......................");
    }
}

启动Tomcat,除了提示Connected to server什么都没有,浏览器输入localhost:8080/myweb_war_exploded/demo01访问此类时:

image-20220811182739303

再去掉用别的类也不会destroy说明一直处于等待状态。

知道点击关闭服务器:

image-20220811183052435

三个阶段:初始化,响应请求阶段与终止。分别对应上述三个方法。

初始化

init(),Servlet生命周期起点,此时将servlet装入tomcat内存,所种方式:请求时或脑图中所说的自动加载。

处理请求

调用service()方法,用于传递"请求"与"响应"对象。从response对象获得请求信息然后用response对象的方法响应。

service()方法可以调用doGet,doPost等方法来处理请求。

该方法可对此执行

终止

服务器不再需要Servlet或重新装入新实例时,只执行一次。

本次实验时分别访问了三个类,可见只有在关闭时才调用终止方法

@WebServlet

image-20220811222540989

//@WebServlet({"/demo01", "/process/demo02", "/data/demo03"})
//@WebServlet("/data/data1/data/*")
@WebServlet("*.do") 

可以看到第三种方式竟然没有斜杠,网页点击提交时怎么访问的?

image-20220812143032360

可见斜杠是自动加的。

派生类

image-20220811185905784

第二个类浏览器url访问demo03时默认doget方法响应。

当自己写表单要求post方法访问时post方法才响应:

image-20220811190145679

image-20220811185010411

package Web.servlet;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

@WebServlet("/demo02")
public class MyServletDemo02 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("这个接口只需要实现一个方法~-~");
    }
}
package Web.servlet;

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

@WebServlet("/demo03")
public class MyServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet inHttpServlet!");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPost() of HttpServlet");
    }
}

image-20220811224000580

GenericServlet类为三个接口中方法提供了部分实现,除了service(),但没实现Http请求处理。

通常情况下,编写Servlet类都继承于HttpServlet,因为HttpServlet提供了http请求的处理方法。专门为HTTP设计,对javax.servlet

Servlet接口中所有方法提供了默认实现。只需重写两个方法就可以实现自己的Servlet。

关于HTTP的编程

image-20220811225127133

image-20220811225444431

两个接口,一个获得请求参数,一个响应。

典型应用
读取表单,查看请求信息

image-20220812143322757

image-20220812143352106

image-20220812143032360

写一个简单表单,点击了选项B,点击提交,访问链接如上图。

package web.servlet;         import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;       import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;         import java.io.IOException;
import java.util.Enumeration;          import java.util.Map;import java.util.Set;
@WebServlet("*.do")
//做这个案例时使用上面这个路径要与网页交互,在Demo02中再换成@WebServlet("/demo01")

public class MyServletDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet()..............");

​    // 1. 获得Method
​    String method = req.getMethod();
​    System.out.printf("Method: %s\n", method);//Method: GET

​    // 2. 获得路径相关
​    //  URI = <虚拟路径> + <servlet路径> 
​    //  URL  : http:// ...  
​    String contextPath = req.getContextPath(); //虚拟路径 
​    String servletPath = req.getServletPath();
​    String uri = req.getRequestURI(); //主机名端口号与请求参数之间的路径 servlet 
​    StringBuffer url = req.getRequestURL();

​    System.out.printf("Path: %s\n", contextPath);       //ContextPath:/myweb_war_exploded
​    System.out.printf("ServletPath: %s\n", servletPath);//ServletPath:/a.do
​    System.out.printf("uri: %s\n", uri);                //uri:/myweb_war_exploded/a.do
​    System.out.printf("url: %s\n", url.toString());  //rl:http://localhost:8080/myweb_war_exploded/a.do

​    // 3. 请求参数
​    String queryString = req.getQueryString();    
​    System.out.printf("QueryString: %s\n", queryString);//QueryString:username-li=aaaa&username-2=bbbb&options=optionB

​    // 4. HTTP协议版本
​    String protocol = req.getProtocol(); 
​    System.out.printf("Protocol: %s\n", protocol);//Protocal:HTTP/1.1

​    // 5. 客户端机器的IP地址
​    String remoteAddress = req.getRemoteAddr();  

​    System.out.printf("RemoteAddr: %s\n", remoteAddress);//remoteAddress:0:0:0:0:0:0:0:1

​    // 6. 根据信息名来获得值,以及所有的信息名

(请求头里的所有key)
        Enumeration<String> headerNames = req.getHeaderNames();  
​    while(headerNames.hasMoreElements()){//方法:    boolean hasMoreElements( ) 测试此枚举是否包含更多的元素。有元素时true
​        String name = headerNames.nextElement();//返回下一个元素
​        String value = req.getHeader(name);
​        System.out.printf("[Header] %s: %s\n", name, value);打印的东西太多见下面第一个图
​    }

​    String s1 = (String)req.getAttribute("myP1");/获得共享数据(下面请求转发例子),知识:请求转发是同一次请求,所以能获得数据,否则为null
​    System.out.println(s1);null

//Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用
方法:Object nextElement( )如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
      

  // 9. 参数值的获得
        // 获得唯一值的参数值
        String v1 = req.getParameter("username-li");//get函数
        System.out.printf("username-li=%s\n", v1);//username-li=aaaa

        // 获得多个值的参数(多选)
        String[] options = req.getParameterValues("options"); 
        for(String option: options){
            System.out.println(option);//optionB。因为之选了B选项
        }

//        // 获得所有的参数
//所有的参数名
        Enumeration<String> pNames = req.getParameterNames(); 

        while(pNames.hasMoreElements()){
            String name = pNames.nextElement();
            String value = req.getParameter(name);//通过参数名获得参数

            System.out.printf("%s=%s\n", name, value);
            打印结果:
            username-li = aaaa
            username-2 = bbbb
            options = optionB
                  //options无论几个只返回一个,要和前端商量好
        }

//        // 获得所有的参数与值的键值对,值为数组类型,即1参数名可对应多个值。 
        Map<String, String[]> pMaps = req.getParameterMap();
//Key是name,V是参数,数组类型是可能有多个参数
        Set<String> keyset = pMaps.keySet();//Map的遍历方法获得Key

        for(String name: keyset){
            String[] values = pMaps.get(name);

            System.out.printf("%s=", name);
            for(String value: values){
                System.out.printf("%s; ", value);//username-li=aaaa; username-2=bbbb; options=optionB; 
            }

            System.out.println("");
        }
    }

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

​    // 7. 获得请求体,请求体的定义就是用post传的数据或文件  

//        BufferedReader br = req.getReader();
//
//        String line = null;
//        while((line = br.readLine()) != null){
//            System.out.println(line);//username-li=aaaa&username-2=bbbb&options=optionA&options=optionB
//        }

​    // 8.练习题 建立一个servlet,可以通过referer判断是不是盗链。(下面图片里蓝色的就是referer)

​    req.setCharacterEncoding("utf-8");//方法为 post时,要设定。否则中文乱码  

​    // 获得唯一值的参数
​    String v1 = req.getParameter("username-wang");//与7,冲突,两个方法只能使用一个,7注释掉。
​    System.out.printf("username-wang=%s\n", v1);

​    // 获得多个值的参数
​    String[] options = req.getParameterValues("options"); 
​    for(String option: options){
​        System.out.println(option);
​    }

​    // 获得所有的参数
​    Enumeration<String> pNames = req.getParameterNames();

​    while(pNames.hasMoreElements()){
​        String name = pNames.nextElement();
​        String value = req.getParameter(name);

​        System.out.printf("%s=%s\n", name, value);
​    }

​    // 获得所有的参数,以及参数对应的多个值
​    Map<String, String[]> pMaps = req.getParameterMap();

​    Set<String> keyset = pMaps.keySet();

​    for(String name: keyset){
​        String[] values = pMaps.get(name);

​        System.out.printf("%s=", name);
​        for(String value: values){
​            System.out.printf("%s; ", value);
​        }

​        System.out.println("");
​    }
}

}

image-20220812153214042

image-20220812153741402

image-20220812153801361

这个图就是getHeaderNames()方法打印内容

image-20220812222739235

请求转发
package web.servlet;

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

@WebServlet("/demo02")
public class MyServletDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("in doGet() of /demo02");//访问demo02这一行打印出来了,紧接着下面才自动打印demo01的东西,说明这里的东西也是要执行的 

//        RequestDispatcher dispatcher = req.getRequestDispatcher("/demo01");
//        dispatcher.forward(req, resp);  
        // 10. 请求转发:请求转发到服务器上的另一个资源(servlet,jsp或html)
       
 // 11. 请求间共享数据 
        // void setAttribute(String name, Object obj)
        // Object getAttribute(String name)
        // void removeAttribute(String name)
          req.setAttribute("myP1", "王校长很帅");//先访问一次demo01,那边get打印null,再运行demo02,由于请求转发了,又访问了demo01,这时候那边的get方法就有东西  。注意请求转发是同一个请求所以才能共享数据,重定向不行。    
​      req.getRequestDispatcher("/demo01").forward(req, resp);//连写
}
}
响应

image-20220812230540942

package web.servlet;

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

@WebServlet("/demo03")
public class MyServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 响应行 状态码 setStatus(int sc)
        resp.setStatus(200);

​    resp.setCharacterEncoding("utf-8");//通知浏览器自己使用的编码。有下面的setContentType,这里可以不写

​    // 2. 设置响应头 setHeader(String name, String value)
​    resp.setHeader("abcd", "xiaomi");
​    // resp.setHeader("content-type", "text/html;charset=utf-8");
​    resp.setContentType("text/html;charset=utf-8");//告诉浏览器要使用***来解码。(确保写入汉字时不会乱码)

​    // 3. 写入响应体 字符流 PrintWriter resp.getWriter()
​    //              字节流 ServletOutputStream resp.getOutputStream()
​    PrintWriter writer = resp.getWriter();

​    writer.println("<HTML>" +
​            "   <HEAD>" +
​            "   </HEAD>" +
​            "   <BODY>" +
​            "       <p>Hello, World! 你好!</p>" +
​            "   </BODY>" +
​            "</HTML>");
​    writer.flush();
​    writer.close();
}

image-20220812224452012

可以看到,访问03时,直接弹出来一个文件,下载之后就是html中的内容。还有右边捕捉的headers内容

重定向
package web.servlet;
//重定向
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo04")
public class MyServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // resp.setStatus(HttpServletResponse.SC_FOUND);

​    // resp.setHeader("location", "/myWeb_war_exploded/demo03");

​    resp.sendRedirect("./demo03");//用一行,代替上面两行(相对路径同级目录)
}
}

访问demo04时与访问03效果相同,直接弹出了html文件。

image-20220812231118847

image-20220812231048015

image-20220812231107669

响应一张图片
package web.servlet;
​
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
​
@WebServlet("/demo05")
public class MyServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 初始化一张图片
        int width = 200;
        int height = 100;
​
•    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);//图片类型,RGB
​
•    // 2. 画东西 图片涂成粉色,画一个蓝色的边框
•    Graphics g = image.getGraphics();
​
•    g.setColor(Color.PINK);
•    g.fillRect(0, 0, width, height);//左上到右下的坐标,整个涂满
​
•    g.setColor(Color.BLUE);
•    g.drawRect(0, 0, width - 1, height - 1);//draw非实心
​
•    // 3. resp输出
•    ImageIO.write(image, "jpg", resp.getOutputStream());//将image以jpg形式,输出给resp.getOutputStream()
​
}
​
}

image-20220812225755022

这就是那张图片,黑色部分不是

ServletContext

image-20220812231239656

《ServletContext类定义了一组方法,用于Servlet容器之间的通信,以获得相关信息,如文件的mime类型,分发请求和日志等》

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

@WebServlet("/demo01")
public class MyServletDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        ServletContext context2 = req.getServletContext();//两种方式得到都是一样的

​    System.out.printf("p1: %s\n", (String)context.getAttribute("p1"));//第一次访问,null,第二次访问就有数据了。只要服务器不重启。

​    System.out.println(context == context2);//true
​    context.setAttribute("p1", "Napoleon wang");
​    String filename = "a.jpg";
​    String mimeType = context.getMimeType(filename);
​    System.out.println(mimeType); //image/jpeg
​    String realPath = context.getRealPath("/myWeb_war_exploded/index.jsp");//一个文件在操作系统上的真实路径
​    System.out.println(realPath);//E:\IdeaProject\0102\out\artifacts\myweb_war_exploded\myweb_war_exploded\index.jsp
}

}
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo02")
public class MyServletDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        System.out.printf("p1: %s\n", (String)context.getAttribute("p1"));//第一次null,运行一次demo01,第二次访问,就有数据了
    }
}
//范围
cookie

image-20220813144917710

package myWeb;

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

@WebServlet("/1/cookie")
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 获取Cookie
        Cookie[] cookies = req.getCookies();

​    if(cookies != null){
​        for(Cookie c: cookies){
​            String name = c.getName();
​            String value = c.getValue();

​            System.out.printf("%s: %s\n", name, value);
​        }
​    }

​    // 2. 写入Cookie

//        Cookie cookie = new Cookie("servletKey", "AValue");
//        resp.addCookie(cookie);

​    // 3. 是否可以一次发送多个Cookie?

//        Cookie cookie2 = new Cookie("servletKey2", "AnotherValue");
//        resp.addCookie(cookie2);

​    // 4. Cookie在浏览器中能保持多长时间 4.1Path知识,注意看上张导图。
​    Cookie cookie = new Cookie("servletKey", "AValue");
​    cookie.setPath("/loginDemo_war_exploded"); 
​    cookie.setMaxAge(300); 
​    resp.addCookie(cookie);

​    Cookie cookie2 = new Cookie("servletKey2", "AnotherValue");
​    cookie.setMaxAge(1);
​    resp.addCookie(cookie2);

​    // setMaxAge : 正数 存在的秒数    记住
​    //             负数 浏览器关闭,Cookie就消失,默认
​    //             零   删除这个Cookie
​    // 5. Cookie里面是否可以有中文
​    // Tomcat 8 以前:不可以 -- URLEncode
​    // Tomcat 8 以及以后:可以
}
}

访问这个Servlet可以在network里面抓到,两个cookie都存在(其他的忽略掉,无关)

image-20220813144314972

关闭浏览器,再访问默认路径可以看到只剩"servletKey了,servletKey2已经消失

image-20220813144306287

JavaScript操作cookie

image-20220813185821944

访问、下载文件

image-20220813154706910

image-20220813155025840

一个点表示同级目录,

所以是此html文件同级的:http://localhost:8080/loginDemo_war_exploded/2.html
则这个点代表的是http://localhost:8080/loginDemo_war_exploded

所以当点击上面三个链接的时候服务器直接显示响应文件

点击下面三个链接时,映射到servlet来处理:

package myWeb;

//让连接指向的文件,无论浏览器是否能搞定都直接下载(默认是能搞定的直接打开,否则下载)
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/downloadFile")
public class DownloadFile extends HttpServlet { 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 获得 fileName
        String fileName = req.getParameter("filename");//参数名

​    // 2. 获得 fileName的realpath,并且用File打开
​    ServletContext context = this.getServletContext();
​    String realPath = context.getRealPath("./" + fileName);//获得此路径的本地真实路径

​    FileInputStream fis = new FileInputStream(realPath);
​    // 3. 设定Response的header
​    String mimeType = context.getMimeType(fileName); 
​    resp.setHeader("content-type", mimeType);
​    resp.setHeader("content-disposition", "attachment;filename=" + fileName);//保存的时候按什么文件保存,浏览器就知道什么类型了,按附件形式 

​    // UrlEncode UrlDecode: Chrome
​    // Base64 Encode/Decode: Firefox

​    // 作业,根据不同的浏览器,对文件名进行不同的编码,以使得能够正确的显示中文文件名

​    // 4. 把数据写入Response
​    ServletOutputStream sos = resp.getOutputStream();

​    byte[] buff = new byte[1024];
​    int len = 0;
​    while((len = fis.read(buff)) != -1){
​        sos.write(buff, 0, len);
​    }

​    sos.flush();
​    sos.close();
​    fis.close();
}
},
登录密码加盐等有空要看
Session

保存页面状态。会话跟踪:从一个客户机请求传送的数据开始,维持状态到下一个请求,并且能够识别出是相同的客户机所发送的信息,连接状态信息会一直保存。10位客户访问同一个网站,该网站自动生成十 个不同的会话保存客户活动信息,直到客户离开网站。

识别用户身份:session为客户端用户分配一个编号。session ID:服务器端随机生成的session文件的文件名,因此能够保证唯一性,进而确保session的安全。

image-20220814093013387

session作用于同一浏览器中,在各个页面中共享数据,无论当前浏览器是否在各个页面之间执行了跳转操作,整个用户会话一直存在。直到关闭浏览器,生命周期结束,如果在一个会话中客户端长时间不向服务器发送请求,session对象就会消失,这取决于服务器,Tomcat,默认30min,可以修改。

JSP一个很重要的内置对象,是javax.servlet.httpServletSession类的一个对象。

用户第一次访问jsp页面时,jsp容器会自动创建一个session对象,直到关闭浏览器服务器上的该客户的session才被注销。用户重新打开并再次连接到服务器时,服务器将为该客户创建一个新的session对象以及session ID。

用户登录时可以setAttribute(),用户点击注销时可以remove。然后跳转到首页。

image-20220813213629365

image-20220813213844355

image-20220813213908514

image-20220813213750873

遇到了一个错误有空可以看看:

image-20220813215045405

package myWeb;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/session")
public class SessionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 读取session
        HttpSession session = req.getSession();
        
​    String value = (String)session.getAttribute("key");
​    System.out.println(value);

​    // Cookie设定JSESSIONID
​    Cookie c = new Cookie("JSESSIONID", session.getId());
​    c.setMaxAge(120);
​    resp.addCookie(c);

​    // 2. 写入session

​    session.setAttribute("key", "MyValue");

​    // 3. 删除 removeAttribute
}

}

JSP

介绍

jsp只在服务器端运行,浏览器端不会出现。

jsp文件会被Tomcat编译成.java文件,

位置:每次启动tomcat时打印在控制台上的:

Using CATALINA_BASE: "C:\Users\24563\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\cd77e4b9-10b5-4910-9bb9-60c6cac2fe69"

image-20220813234522259

image-20220813234604270

内容是这样的:

image-20220813235227504

image-20220814090637533

image-20220813235000497

image-20220813234743606

可以看到将html变成字符串形式输出了。

jsp先被生成为以上之中servlet然后再编译为.class文件。所以jsp本质是servlet

页面元素

image-20220814085803660

<%任意的java代码%>

<%= %> 里面只写一个表达式,不写分号,相当于out.print()这个方法的参数。

<%! %>声明一个java类或方法或变量,声明之后作用域范围为当前页面,这种方法慎用。一般用前两种方法

<>

内置对象

内置对象:在servlet类中需要get才能用,在jsp页面中直接使用,不加声明使用的成员变量,

image-20220814123103654

下面的图:了解

image-20220816224519199

image-20220816224534589

image-20220816224551344

指令

image-20220814150938855

image-20220814155251787

image-20220814154304036

<% @ 指令名 属性1 ="属性值" 属性2 = "属性值"...%>
<%@ page import="java.io.PrintWriter" %>
page

在一个jsp页面中page指令可以出现多次,但每种属性只能出现一次,重复设置会覆盖。

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" errorPage="500.jsp" language="java" %>

errorPage:页面发生异常时,容器将转向属性值指向的页面。

这时500.jsp页面:

image-20220814154754454

这里出现了另一个属性:isErrorPage,isErrorPage="true"属性要设置为true,才可使用内建对象exception

手册:

image-20220816212854185

image-20220816212953138

image-20220816213015030

include

将指定位置上的资源包含到当前页面

taglib

taglib:允许在jsp页面使用标签(用户自定义标签)

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 使用某标签,标签地址

image-20220814175816235

prefix:在jsp里引用这个标签的前缀。

uri:标签地址。

D:\后端零基础就业班\0103_JavaWeb\loginDemo\lib 里面有两个包jstl、standard 
要想taglib就需要把他们放到自己项目的lib文件中。
学taglib引入资源时需要用到,课程中放在lib文件夹,但是遇到了错误:

遇到错误解决半小时:
    无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]
解决方法:csdn
    检查是否正确jar包以及tld文件,jstl.jar和standard.jar放在在WEB-INF/lib/下 tld文件都放在WEB-INF/下,然后引入jstl1.0jar包中的c.tld、c-1_0.tld、c-1_0-rt.tld文件在WEB-INF文件夹中。(我试过当jstl1.0版本的也成功过)其实jstl.jar包中也有这三个文件,但当我打开一探究竟时,发现几乎完全一样,只有协议不一样,所以我觉得jar包缺陷和jar包冲突可能性更大。

image-20220814221523536

注释与MVC
隐藏注释:<%-- --%> 这是jsp注释 
还有以下几种<%//单行注释%>、<%/* 多行*/%>

image-20220814180332151

上一行:不会被发送到浏览器。

第二行:会被发送到浏览器,虽然被注释掉了。在浏览器里能看到这行注释。发到浏览器端,浏览器端的引擎决定将其注释。

mvc

model时读取数据库生成的data

view是页面

动作元素

就业班没介绍。

用于在jsp页面执行某一个操作,如动态包含一个文件,转向另一个文件。动作元素和指令元素不同,动作元素是在客户端请求时期动态执行的,每次请求可能都会被执行一次,而指令元素编译时期被执行。

<jsp:include> forward,param,useBean,setProperty,getProperty等用时自己查。
EL
EL是表达式直接使用,JSTL则是标签库

降低使用门槛,对不擅长使用Java的人更友好。

image-20220814211706273

 与jsp的区别:<%=null%> 会报错 因为这个方式先去调用.toString()
${ null } el语言不会报错

禁用前与禁用后:禁用前显示这个key的值,禁用后当字符串显示出来了

image-20220814224045392

image-20220815140359037

${'${'}  输出结果为${
由于EL表达式是${开头,如果在网页中要显示${字样字符串,必须以字符串或者前面加\禁用

补充--输出字符串:

${"字符串内容"}  单双引号都可以
${'字符串内容'} 
<%@ page import="myWeb.User" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashMap" %><%--
  Created by IntelliJ IDEA.
  User: admin
  Date: 2020/11/7
  Time: 19:59
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>

<head>
    <title>Title</title>
</head>

<body>
    <%-- 1. EL 如何禁用?
            1. 在$前放一个\
            2. 使用指令page的isELIgnored="true" 默认不禁用,如果设置了,整个页面都会被禁用
            3.web.xml中配置<el-ignored>袁术
                <jsp-config>
                        <jsp-property-group>
                        <url-pattern>*.jsp</url-pattern>
                               <el-ignored>true</el-ignored>
                      </jsp-property-group>
                </jsp-config> 以上适合禁用web应用中所有jsp页面.
       --%>
    <%-- 2. EL中可以支持什么样的表达式?(${}里面都能放什么)
       能放:
         2.1 四种域对象中的属性
         2.2 属性或者数字、字符串(单双引号都行)、布尔值、通过运算符连接得到的表达式
             1. 算术运算符: + - * /(div) %(mod)  意思是除号也可以写为div所以括号里的东西在el里算关键字,
             2. 关系运算符: >(gt) <(lt) >=(ge) <=(le) ==(eq) !=(ne)
             3. 逻辑运算符: &&(and) ||(or) !(not)
             4. ? :
             5.empty运算符 前缀运算符用来确定一个对象或变量是否为mull或为空。可以与not运算符结合使用
      --%>
      为变量命名时使用了关键字会抛出异常。
    <%-- 3. 如何获取一个属性
         3.1 ${域对象.属性}
         3.2 ${属性} -> 按照 pageScope,requestScope, sessionScop, applicationScope的顺序从小到大找
         3.3 ${属性} 属性是一个对象,可以访问属性的getter和setter的 getXxxXxxXxx -> xxxXxxXxx
         3.4 ${属性[索引]} 属性是一个List对象,可以使用方括号索引的方法来使用
         3.5 ${属性.key} 属性是一个Map对象,可以使用.key的方法来使用
             ${属性["key"]}
       --%>
        <%-- 4. 隐含对象 类似jsp中的内置对象直接通过对象名操作,有11个
        pageContext  javabean对象,对应于javax.servlet.jsp.PageContest类型。其它隐含对象均对应map类型
        页面上下文对象,用于访问jsp内置对象和servletContext。获取这些对象后即可获取其属性值
       --%>
    <%
        String ab = "";//aaa,EL没法用,要加到域对象里el才能使用
        String a2 = "bbb";
        String a3 = "ccc";
                        箭头后就是访问作用域范围的隐含对象,只能用来取得指定范围内的属性值。
//        pageContext -> pageScope : 单一jsp page的范围
//        request -> requestScope  :用户请求期间(或一个jsp到另一个jsp请求期间)
//        session -> sessionScop   :用户持续在服务器连接的期间
//        application -> applicationScope  :服务器执行服务到服务器关闭为止
        pageContext.setAttribute("a", ab);
        pageContext.setAttribute("a2", a2);
        session.setAttribute("a2", a3);

​    User user = new User("abcd");
​    pageContext.setAttribute("user", user);

​    ArrayList<String> list = new ArrayList<String>();
​    list.add("a1111");
​    list.add("a2222");

​    pageContext.setAttribute("list", list);

​    HashMap<String, String> map = new HashMap<>();
​    map.put("s1", "s1 String");
​    map.put("s2", "s2 String");
​    pageContext.setAttribute("map", map);
%>

<p> ${a}</p> 
<p>${pageScope.a == "" ? "<空字符串>" : pageScope.a}${pageScope.a2}${sessionScope.a2}</p>
<%--    可以写a亦可以写全pageScope.a,不写会按照注释3.2依次找,如果指定了范围,就只在此范围内找--%>
<p>${pageScope.user.username}</p> <%--    使用了src底下的user类--%>
<p>${list}=${list[0]}=${list[10]}</p> 
<p>${map}=${map.s1}=${map.s3}</p>
<p>${map}=${map["s1"]}=${map["s3"]}</p><%--    同上一行--%>  
  <p>${pageContext.request.contextPath}</p>
  <p> ${a}</p>
<p>\${a}</p>
</body>
</html>

运行:

image-20220815110048453

EL表达式存取范围

变量没有指定范围时,系统默认从page范围中查找,然后依次在request、session、application范围中查找。如在此过程中找到此变量直接返回,如果没有,返回null。

JSTL
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
有五个标签库,这只是核心标签库。

EL是表达式,jstl 是标签库,要引入jar包.

image-20220815110707697

image-20220815203614398

<%@ page import="java.util.ArrayList" %><%--
  Created by IntelliJ IDEA.
  User: admin
  Date: 2020/11/9
  Time: 14:08
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
<%
    System.out.println("In JSTL1.jsp...");
%>
<html>

<head>
    <title>Title</title>
</head>

<body>
    <%-- 1. c:if test="布尔表达式"
            没有else,true:输出到页面(浏览器网页显示),false:不输出
         2. c:choose, c:when test="布尔表达式", c:otherwise  想switch
         3. forEach var="i" begin="0" end="14" step="1"   varStatus (count) =》for(int i = 0; i < 15; i++)    step(每次增加) varStatus循环次数
            forEach items="${list}" var="str" varStatus="s"  (index, count) =》for(String s: list)
       --%>
    <% // 获得 Data =》 Model
       // 根据Data 输出到 HTML =》Control + View
        Boolean b1 = true;
        pageContext.setAttribute("b1", b1);

​    ArrayList<String> list = new ArrayList<>();
​    list.add("aaa");
​    list.add("bbb");
​    list.add("ccc");

​    pageContext.setAttribute("list", list);

​    int weekday = 15; // 0 1 ~ 6
​    pageContext.setAttribute("weekday", weekday);
%>
<c:if test="${b1}">
​    某些要输出的语句。。。<br/>
</c:if>

<c:if test="${empty list}">
    空的List<br/>
</c:if>

<c:if test="${not empty list}">
    不空的List,能看到 <br/>
</c:if>

<c:choose>
    <c:when test="${weekday == 0}">星期日<br/></c:when>
    <c:when test="${weekday == 1}">星期一<br/></c:when>
    <c:when test="${weekday == 2}">星期二<br/></c:when>
    <c:when test="${weekday == 3}">星期三<br/></c:when>
    <c:when test="${weekday == 4}">星期四<br/></c:when>
    <c:when test="${weekday == 5}">星期五<br/></c:when>
    <c:when test="${weekday == 6}">星期六<br/></c:when>
    <c:otherwise>数字错误超出范围<br/></c:otherwise>
</c:choose>

<c:forEach var="i" begin="0" end="14" step="2" varStatus="s">
    ${i}-${s.count}&nbsp;
</c:forEach>

<br/>

<c:forEach items="${list}" var="str" varStatus="s">
    ${str}-${s.index}-${s.count}&nbsp;  
</c:forEach>

<br/>

</body>
</html>

image-20220815110559311

三层

image-20220815120351158

前后端分离:每次刷新,向服务器发送一次http请求,更新dom

不分离:Ajax也会发送http请求但是dom不摧毁,通过js更新dom.

image-20220815121036052

前后端不分离图用户请求到达servlet,然后调用业务层,完成任务,数据传回servlet,然后根据数据组成jsp页面,传回浏览器。

image-20220815121856854

前后端分离就不需要jsp了,发送Ajax请求,传回json格式数据给浏览器。浏览器根据json数据更新dom

Servlet过滤器

image-20220815203846725

介绍

过滤器是一种程序,先于servlet或Jsp页面运行在服务器中。可以附加到一个或多个servlet或jsp页面上,并且可以检查进入这些资源的请求信息。过滤器是客户端与目标资源间的中间层组件,拦截客户端的请求与相应信息。

服务器判断请求与过滤器对象是否相关联,如果是,交给过滤器处理。

过滤器对象在javax.servlet包中

package myWeb;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(value = "/JSTL1.jsp", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
public class FilterDemo01 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter init...");
    }

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("Filter Demo01 is called .......");

​    // 直接放行
​    filterChain.doFilter(servletRequest, servletResponse);

​    System.out.println("Filter Demo01 is called 2 ......");
}

@Override
public void destroy() {
    System.out.println("Filter destroy...");
}

}

运行顺序:

image-20220815205253783

多过滤器运行顺序
package myWeb;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
//劫持哪个网页
public class FilterDemo02 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter Demo02 is called .......");

​    // 放行
​    filterChain.doFilter(servletRequest, servletResponse);

​    System.out.println("Filter Demo02 is called 2 ......");
}

}
//访问jstl1测试多个过滤器调用顺序     

image-20220815205424670

上图可看运行顺序,中间一句时是stl.jsp页面中的语句。

可以用多个过滤器如字符编码,认证等等。

image-20220815224610894

配置

方法一:注解

方法二:web,xml中。

image-20220815222920473

声明过滤器对象:

<filter>标签用于声明过滤器对象,除了上图中出现的子标签还有<init-param>用于设置过滤器的初始化参数。
过滤器请求方式

属于可选择配置,属于上面配置中的可选配置

image-20220815225351476

标签注解都可。注解方式第一段代码中有例子

API

FilterChain接口由容器实现,只包含一个方法:doFIlter(),用于将请求或响应传递给下一个过滤器对象

image-20220815225905793

Servlet监听器
例子
package myWeb;
//监听属性变动,结合listener1.jsp使用
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener 
public class ServletContextAttributeDemoListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("Servlet Context: An attribute is set.");

​    ServletContext context = scae.getServletContext();
​    String name = scae.getName();
​    System.out.println(name);
​    System.out.println(scae.getValue());

​    if(name.equals("aa")) {//如果添加进来的属性名是这个就删掉
​        context.removeAttribute(name);
​    }
}

//    @Override
//    public void attributeRemoved(ServletContextAttributeEvent scae) {
//
//    }
//
//    @Override
//    public void attributeReplaced(ServletContextAttributeEvent scae) {
//
//    }
    ServletContextAttributeListener 顾名思义,是专门用来监听ServletContext属性的各种类型监听器之一,下面图片会介绍其他类型。
package myWeb;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

//@WebListener
不需要配置参数
public class ServletContextDemoListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Servlet Context is created...");

​    ServletContext context = sce.getServletContext();
​    System.out.println(context);
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
    System.out.println("Servlet Context is destroyed...");
}

}

image-20220815230715821

本例内容为监听到ServletContext 属性设置,符合条件的属性被删掉。

浏览器访问:Listener1.jsp

运行结果如下:刚加上来没来得及打印,就被即时删掉了

image-20220815231117533

监听:上下文,会话,请求

image-20220815231301322

image-20220815232118143

配置很简单

image-20220815232319426

image-20220815232343859

image-20220815232409772

Ajax与json

image-20220817085226733

Ajax在前端课程0141,0142也就是倒数第二节与倒数第三节

JavaScript中的对象:

image-20220817083210240

大括号,逗号隔开

json格式:可以在网站中测试,有错误右边会提示。

image-20220817084010614

还可以是数组:

image-20220817084831358

json格式可以递归,只需要最外层大括号,里面对象名括号括起来。

image-20220817085244427

例子前端实现Ajax后端处理json

Ajax实现可以使用js原生或者使用jQuery或vue。本例中使用jQuery因为简单,学习成本低。

image-20220817101903333

下载jQuery包到项目文件夹下,然后用的时候引入。

首先网页中向后端发送这样的一行数据:

image-20220817103005626

然后后端给前端发送这样的数据,

image-20220817102617737

网页端拿到后端数据后组装成这样添加到标签中:

image-20220817103132324

然后注释掉开始发送其它样式的数据。

image-20220817214219824

image-20220817214650129

前端页面网页核心代码:

<body>
<!--AJAX 将按照以下格式插入DOM-->
<!--<table>
    <tr>
        <th>一行一列</th>
        <th>一行二列</th>
    </tr>
    <tr>
        <th>二行一列</th>
        <th>二行二列</th>
    </tr>
    <tr>
        <th>三行一列</th>
        <th>三行二列</th>
    </tr>
    <tr>
        <th>四行一列</th>
        <th>四行二列</th>
    </tr>-->
<!--</table>-->
<button id="btn">Get JSON</button>
<div id="theD"></div>
<script type="text/javascript">
    var data = {
        text: '第一行'
    };
//$是jQueryjQuery语法。相当于dom,ready的时候调用这个函数
    $(function(){
        $('#btn').click(function(){//当点击时回调这个函数
            $.ajax({                 //$是jQuery中对象ajax 是function,函数里面是个js对象  
                url: './data_json',
                type: 'post',
                data: JSON.stringify(data),//对象转换为字符串类型,才能发送出去
                dataType: 'json', //(回来的数据要什么格式) 如果Content-Type没有设定,我们也可以通过设定 dataType来实现。
                success: function(data){//请求成功时调用这个函数
                    $('#theD').append(jsonToHTML(data));//调用了这个方法,下面有介绍,方法返回的结果append到选择的标签中
                }
            });
        });
    });//整个块分三次异步调用(什么时候调用,如何调用,当...时调用)
// parse JSON
function jsonToHTML(json){//得到的数据一步步的组装,组装完后就是上面注释中的格式。
    console.log(typeof json);
    var $table = $('<table></table>');//这是

    $(json['LINE']).each(function(){
        var $tr = $('<tr></tr>').appendTo($table);
        $(this['COLUMN']).each(function(){
            var $th = $('<th></th>').html(this['text']).appendTo($tr);
        });
    });

    return $table;
}
---
    这段方法的作用是将一个JSON对象转换成一个HTML表格,并返回这个表格。它用到了以下的方法:

console.log(typeof json):这个方法是打印出json对象的类型,例如object或string。
$('<table></table>'):这个方法是使用jQuery库创建一个空的表格元素,并赋值给变量$table。
$(json['LINE']).each(function(){...}):这个方法是遍历json对象中的LINE属性,它是一个数组,每个元素代表一行数据。对于每个元素,执行一个回调函数,参数是当前元素的索引和值。
$('<tr></tr>').appendTo($table):这个方法是创建一个空的行元素,并将它添加到表格元素的末尾,然后返回这个行元素,并赋值给变量$tr。
$(this['COLUMN']).each(function(){...}):这个方法是遍历当前行元素中的COLUMN属性,它也是一个数组,每个元素代表一列数据。对于每个元素,执行一个回调函数,参数是当前元素的索引和值。
$('<th></th>').html(this['text']).appendTo($tr):这个方法是创建一个空的表头单元格元素,并将它的内容设置为当前列元素中的text属性,然后将它添加到行元素的末尾,并返回这个单元格元素,并赋值给变量$th。
//appendTo() 方法在被选元素的结尾插入 HTML 元素。
//$(content).appendTo(selector)
content    必需。规定要插入的内容(必须包含 HTML 标签)。
注意:如果 content 是已存在的元素,它将从当前位置被移除,并在被选元素的结尾被插入。
selector    必需。规定把内容追加到哪个元素上。
    -------!!!!!!!!当selector是创建的两个标签时,就是放到两个元素中间。!!!!!!!!!
</script>
</body>
---
    是的,jQuery选择语法不仅能选择已有的HTML元素,还能创建新的HTML元素。你可以使用$()函数来创建一个新的HTML元素,并将它作为一个jQuery对象返回。例如,你可以这样创建一个新的<p>元素:
var p=(“<p></p>”); // 创建一个空的<p>元素
你也可以在$()函数中指定元素的内容或属性,例如:
var p=(“<p>Hello, world!</p>”); // 创建一个包含文本的<p>元素
var img=(“<img src=‘logo.png’ alt=‘logo’>”); // 创建一个带有src和alt属性的<img>元素
你可以使用jQuery的方法来操作或插入这些新创建的元素,例如:
$p.css(“color”, “red”); // 设置<p>元素的颜色为红色
$img.appendTo(“body”); // 将<img>元素添加到<body>元素的末尾
----
jQuery 的选择语法是基于 CSS 选择器的,所以它可以根据不同的字符串格式来判断是在选择还是在创建标签。一般来说,有以下几种情况:

如果字符串以<开头,并以>结尾,那么 jQuery 会认为它是一个 HTML 标签,例如<p></p>或<div id="container"></div>,并且会创建一个新的元素。
如果字符串是一个有效的 CSS 选择器,那么 jQuery 会认为它是一个选择器,例如#container或.item,并且会查找匹配的元素。
如果字符串是一个无效的 CSS 选择器,那么 jQuery 会抛出一个语法错误,例如Hello, world!或[name=],并且不会执行任何操作。

后端Servlet程序代码:

package myWeb;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@WebServlet("/data_json")
public class JSON extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 把AJAX发送过来的请求体变换为字符串

​    BufferedReader br = req.getReader();

​    StringBuffer bfr = new StringBuffer();

​    String line = null;
​    while((line = br.readLine()) != null){

//            System.out.println(line);
            bfr.append(line);
        }

​    String inputJson = bfr.toString();

​    System.out.println(inputJson);

​    // 2. 返回JSON数据
​    resp.setContentType("application/json;charset=UTF-8");
​    resp.setCharacterEncoding("UTF-8");

​    PrintWriter writer = resp.getWriter();

//        writer.println("{");
//        writer.println("            \"LINE\": [");//斜杠是转义
//        writer.println("            { \"COLUMN\": [");
//        writer.println("                { \"text\": \"一行一列\" },");
//        writer.println("                { \"text\": \"一行二列\" }]},");
//        writer.println("            { \"COLUMN\": [");
//        writer.println("                { \"text\": \"二行一列\" },");
//        writer.println("                { \"text\": \"二行二列\" }]},");
//        writer.println("            { \"COLUMN\": [");
//        writer.println("                { \"text\": \"三行一列\" },");
//        writer.println("                { \"text\": \"三行二列\" }]},");
//        writer.println("            { \"COLUMN\": [");
//        writer.println("                { \"text\": \"四行一列\" },");
//        writer.println("                { \"text\": \"四行二列\" }]}");
//        writer.println("    ]");
//        writer.println("}");

​    // 3. json 字符串 =》 Java 对象(引入包中方法帮你做)
​    // 4. Java 对象 =》 json 字符串

​    //  3.1/4.1 引入相应的工具包: Jsonlib可以,但是我们用 jackson(spring默认包) 引入包,并且保证包一起被out

三个包版本号要一样,libraries中添加,artifacts中确保在outputlayout里
        //  3.2/4.2 建立和Java 对象对应的类

​    //  3.3/4.3 初始化一个ObjectMapper(引入包里的东西)
​    ObjectMapper mapper = new ObjectMapper();
​    //  3.4 json 字符串 =》 Java 对象

//        Cell c = mapper.readValue(inputJson, Cell.class);//参数一被转化的,参数二转化为那个类 ,此时数据 text: '第一行'
//        System.out.println(c.getText());//输出:第一行

​    //      json 字符串 =》 List<Java对象>

//        List<Cell> cs = mapper.readValue(inputJson, new TypeReference<List<Cell>>(){});
//        System.out.println(cs.get(0).getText());

  //list对应的Ajax发送的数据{   [  {text: '第一行'},{text: '第二行'}]
        

​    //      json 字符串 =》 Map<String, Java对象>

//        Map<String, Cell> cs = mapper.readValue(inputJson, new TypeReference<Map<String, Cell>>(){});
//        System.out.println(cs.get("first").getText());
//{first: {text: '第一行'},second:{text: '第二行'}}

​    //      json 字符串 =》 Java的复杂对象

//        Column cln = mapper.readValue(inputJson, Column.class);
//        System.out.println(cln.getColumn().get(0).getText());
//对应的数据  {column: [ {text: '第一行'},  {text: '第二行'}  ]}

​    // 4.4 Java对象 =》 json 字符串 

//写一个table类
        Cell c1 = new Cell();
        c1.setText("一行一列");
        Cell c2 = new Cell();
        c2.setText("一行二列");

​    Column cln = new Column();
​    cln.getColumn().add(c1);
​    cln.getColumn().add(c2);

​    Table tbl = new Table();
​    tbl.getLine().add(cln);

​    c1 = new Cell();
​    c1.setText("二行一列");
​    c2 = new Cell();
​    c2.setText("二行二列");
​    cln = new Column();
​    cln.getColumn().add(c1);
​    cln.getColumn().add(c2);
​    tbl.getLine().add(cln);

​    c1 = new Cell();
​    c1.setText("三行一列");
​    c2 = new Cell();
​    c2.setText("三行二列");
​    cln = new Column();
​    cln.getColumn().add(c1);
​    cln.getColumn().add(c2);
​    tbl.getLine().add(cln);

​    c1 = new Cell();
​    c1.setText("四行一列");
​    c2 = new Cell();
​    c2.setText("四行二列");
​    cln = new Column();
​    cln.getColumn().add(c1);
​    cln.getColumn().add(c2);
​    tbl.getLine().add(cln);

​    // writeValueAsString(obj)   返回 String
​    // writeValue(写到哪里, obj)
​    //  参数一可以是:  File对象 =》用来 写入文件
​    //  或者  Writer对象 =》 写入字符流流
​    //   或者 OutputStream对象 =》 写入字节流

​    String json = mapper.writeValueAsString(tbl);
​    writer.print(json);

​    List<Cell> cs = new ArrayList<>();
​    cs.add(c   1);
​    cs.add(c2);

​    System.out.println(mapper.writeValueAsString(cs));

​    // Annotation: @JsonIgnore 在类添加到属性前面(上一行),帮助忽略属性,使这个属性不会被转化为Json
​    //             @JsonFormat(pattern = "格式")//也是添加到定义属性前,表示parse成某种格式。

   // 比如:
//@JsonFormat(pattern = "yyyy-MM-dd") 

​    writer.flush();
​    writer.close();
}

}
//如果不用别人写好的工具包,需要自己用正则表达式parse成字符串然后在构造类,有挑战也麻烦。

cell类代码:

package myWeb;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Date;

public class Cell {
    private String text;
//    @JsonIgnore
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date date = new Date();

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

public Date getDate() {
    return date;
}

public void setDate(Date date) {
    this.date = date;
}

}

Colum类代码

package myWeb;

import java.util.ArrayList;
import java.util.List;

public class Column {
    private List<Cell> column = new ArrayList<Cell>();

    public List<Cell> getColumn() {
        return column;
    }

}

Table类代码:

package myWeb;

import java.util.ArrayList;
import java.util.List;

public class Table {
    private List<Column> line = new ArrayList<Column>();

    public List<Column> getLine() {
        return line;
    }

}
引入Jackson
  <!--jackson-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <scope>compile</scope>
    </dependency>
然后modules importD:\Maven\gittest\reposity\com\fasterxml\jackson\core\jackson-annotations\2.3.3\jackson-annotations-2.3.3.pom
D:\Maven\gittest\reposity\com\fasterxml\jackson\core\jackson-core\2.3.3\jackson-core-2.3.3.pom
D:\Maven\gittest\reposity\com\fasterxml\jackson\core\jackson-databind\2.3.3\jackson-databind-2.3.3.pom

然后project页面就有了,同样的邮件add as Library
登录MD5
package myWeb;

import java.security.MessageDigest;//加密用的库
import java.security.NoSuchAlgorithmException;

public class User {
    private String username;
    public User(String username){
        this.username = username;
    }
    public String getUsername(){
        return this.username;
    }

    public void setUsername(String username){
        this.username = username;
    }

    protected String md5(String words) throws NoSuchAlgorithmException {
        byte[] passwordBytes = MessageDigest.getInstance("md5").digest(words.getBytes());//加密,获得的数据是二进制的
        StringBuffer bfr = new StringBuffer();
//一个byte8位(可容纳两个16进制数),一个16进制数占4位
        for(byte b: passwordBytes){
            int temp = b & 255;//按位与(255二进制:11111111)

            if(temp < 16 && temp >= 0) {//小于16,0~F,只有一位,补0
                bfr.append("0");
                bfr.append(Integer.toHexString(temp));//变成16进制
            } else {
                bfr.append(Integer.toHexString(temp));
            }
        }

        return bfr.toString();
    }

    public boolean checkPassword(String password, String salt) {
        // 假装去读取了数据库
        // aaa: cccc
        if(this.username.equals("aaa")){
            String passwordMd5 = "";
            try {
                passwordMd5 = this.md5("cccc");
                passwordMd5 = this.md5(passwordMd5 + salt);
            } catch (NoSuchAlgorithmException e) {
                System.out.println(e.toString());
            }

            if(password.equals(passwordMd5)){
                return true;
            }
        }

        return false;
    }

}
在JS中这样使用
<script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js"></script>
直接使用MD5方法
let passwordMd5 = md5(passwordElement.value);
        passwordMd5 = md5(passwordMd5 + salt);
旅游网站

都是写死的,比较简单,稍微介绍一下

分页domain类
    private int totalCount; 
    private int totalPage;
    private int currentPage;
    private int pageSize;//一页几条 
    private List<T> list = new ArrayList<T>();
    //用于放route路线
传过来的参数是cid,与当前是第几页,与总页数,并且如果前端传值为空,这三个值是有默认值的,避免前后端互相“死锁”,也就是即时不传参数,后端默认值能够保证前端分页框架不崩溃。默认值:当前数据cid,第0条,当前页面:第一页
想获取本页内容,首先总数量是数据库查出来的。
    ------
        @Override
    public Page<Route> pageQuery(int cid, int currentPage, int pageSize) {
        Page<Route> pr = new Page<>();
        pr.setCurrentPage(currentPage);//传值,或在调用本方法时传默认值
        pr.setPageSize(pageSize);
        Map<Integer, List<Route>> allRoutes = (Map<Integer, List<Route>>) this.getApplication().getAttribute("allRoutes");
        List<Route> routes = allRoutes.get(cid);//写死的,相当于数据库查的,了解
        int totalCount = routes.size();//查的数据数量
        pr.setTotalCount(totalCount);
        int start = (currentPage - 1) * pageSize;//重要,比如一页有五条,第二页的开始下标就是(2-1)*5,第六条数据,下标是5
        pr.setList(new ArrayList<Route>());
    
        for(int i = start; i < start + pageSize && i < totalCount; i++){//i同时满足不超过总数量、不超过当前页下标(下标从0开始)
            pr.getList().add(routes.get(i));
        }
        int totalPage = totalCount % pageSize == 0 ? (totalCount / pageSize) : (totalCount / pageSize + 1);
    //重点 总页数算法设计,要学会 使用简介的三目运算代替多行判断代码。
        pr.setTotalPage(totalPage);

        return pr;
    }

做了个ServletContextListener,只要上下文一启动,就创建一个USER类将用户名密码set到类中。放入ServletContext中,可以说是写死的,只要服务器一启动,就写死创建一个用户。

注册就是简单的,写死的代码,获取上下文中的user,如果不为空,就返回true,空,返回错误信息。

返回信息类与json格式非常有意义

public class ResultInfo {
    private boolean flag;
    private String errorMsg;
    public  ResultInfo() {

    }

    public boolean isFlag() {return flag;}

    public void setFlag(boolean flag) { this.flag = flag;}

    public String getErrorMsg() {return errorMsg;}

    public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}
    
}
---以下是这个类的使用,用于返回信息:使用到了jackson
    ....省略无数行....
    ResultInfo info = new ResultInfo();
        if(flag){
            info.setFlag(true);
        } else {
            info.setFlag(false);
            info.setErrorMsg("注册失败");
        }

        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(info);
   resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().write(json);
登录

登录就是从上下文里或得到用户名密码检查一下是否相等。

req.getSession().setAttribute("user", u);//登录成功就塞进去,只有登录了才有,如果有些页面非登录用户不给看,可用filter检测session

Route,Category等实体类也一样,做了些假数据放进了上下文,模仿数据库查出来的

"/route/list"这个请求就是把所有List<Category> categories使用Jackson变成json发到前台
WEb后端项目工作流程

image-20220817215754485

idea tips

maven里面的Tomcat太老了 project Structure artifact中里面两个删掉,添加一个webapplication exploded from travel 然后再像往常那样配置Tomcat,beforelaunch,将build移除,建一个Maven的goal cleancompile 然后点击箭头挪到上面一小时32分钟,比较重要,自己看看

Tips:Project settings 中modules可将文件夹设置成sources这样文件夹变成蓝色模样,可以添加Java代码。 2.Project settings facets中web resource directories中可将文件夹添加进去,这样右键可写网页

标签: java mvc tomcat

本文转载自: https://blog.csdn.net/weixin_45279374/article/details/142502832
版权归原作者 听雨笑 所有, 如有侵权,请联系我们删除。

“青梅煮酒Java Web 笔记”的评论:

还没有评论