javaWeb第一版

为什么要有Tomcat

image-20240713215754626

那我们这个应用怎么会被客户端访问到呢?就需要用到Web应用服务器了。

因为我们有Web应用服务器,然后程序在这个服务里面所以就可以被客户端访问,就像数据库一样。

而我们说的Tomcat就是Web应用服务器。

当然还有Jboos,Jetty。。。

我们把写好的程序放进去,其他人就能通过ip地址端口号来访问到我们的java程序。

image-20240713220146796

当然要在一个局域网里面。

安装tomcat

1、自己去官网下载安装包解压

2、然后解压之后的文件夹

bin :存放各个平台下启动和停止Tomcat服务的脚本文件

conf: 存放各种Tomcat服务器的配置文件比如端口号

lib:里面放的就是jar文件(一些工具包)

logs:存放日志,记录服务运行的情况。

temp:存放运行时的临时文件。文件的上传和下载。

webapps: 存放允许客户端访问的资源。(我们的java程序)

work:存放Tomcat将JSP转换之后的Servlet文件。

利用集成开发环境操作Tomcat

image-20240713221816197

配置就不讲了

Servlet

什么是Servlet

是Java Web 开发的基石,与平台无关的服务器组件,他是运行在Servlet容器/Tomcat/Web应用服务器中,负责与客户端进行通信。

功能:

1、创建并返回给予客户请求的动态HTML页面

2、与数据库进行通信

3、如何使用Servlet

首先Servlet本身是一组接口,在javax.servlet,x指的是扩展出来的东西.

自定义一个类然后实现Servlet接口,这个类就具备了接受客户端请求以及做出响应的功能。

然后就有五个方法

init()方法,初始化方法

getServletConfig():获取Servlet的信息

service():处理客户端请求的方法

getServletInfo():返回字符串信息

destory():销毁的,比如释放IO流,拿到一些数据库连接对象之类的。

然后创建个java文件,但是怎么访问到我们写的java.class文件呢?

Tomcat里面有个规则就是WEB-INF是不允许访问的,所以我们只能通过映射去访问我们写的java文件

在web.xml里面加映射

image-20240713225625211

这样就可以请求后端的服务,那怎么实现对客户端的响应呢?

image-20240713225828125

这两个对象一个接受请求信息,一个用来响应信息。

我们直接对对象来进行操作

image-20240713225934126

当然还有另一种配置方式,这样就能简化开发

  • 基于XML的配置方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>myservlet</servlet-name>
    <servlet-class>com.bitzh.test.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>myservlet</servlet-name>
    <url-pattern>/test</url-pattern>
    </servlet-mapping>
    </web-app>
    • 基于注解的方式

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      package com.bitzh.test;

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

      /**
      * @Auther: oyy0v0
      * @Date: 2024/7/14 - 07 - 14 - 8:32
      * @Description: com.bitzh.test
      * @version: 1.0
      */
      @WebServlet("/test")
      public class MyServlet implements Servlet {
      @Override
      public void init(ServletConfig servletConfig) throws ServletException {

      }

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

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

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

      @Override
      public void destroy() {

      }
      }

上述两种配置方式完全一样,将代码进行映射,然后在浏览器就能访问到了

Servlet生命周期

1、首先判断存在不存在,不存在是通过反射机制,(先利用全路径动态的获取Class然后获取class的构造器然后实例化对象)创建Servlet对象,如果存在直接第三步

2、然后第一次访问的时候调用init方法,

3、然后接着是service方法

4、关闭的时候调用destory法,释放资源。

ServletConfig

该接口是用来描述Servlet基本信息的。

getServletName():用来返回Servlet类的全类名

getInitParameter(String key) 获取init参数的值(web.xml)

getInitParameterNames() 返回所有的name值,一般用作遍历初始化参数

getServletContext():返回Servlet的上下文,整个Servlet管理者

里面有对应的方法来获取信息,getContextPath()获取路径名,getServerInfo()获取服务器名字(版本号),这个侧重于全局,ServletConfig侧重于某一个Servlet实例

Servlet的层次接口

Servlet –>GenericServlet–>HttpServlet

Http请求有很多种类型,常用的是

GET 读取

POST 保存

PUT 修改

DELETE 删除

CRUD

doGet()方法,doPost()方法等等,这些都是在service中分出来的,对于请求进一步细化

image-20240714103916002

GenericServlet

这里是干什么的呢?

这个是屏蔽其他四个不需要用的方法,然后分发doGet和doPost方法。

JSP

JSP本质上就是一个Servlet,JSP主要是负责与用户交互,将最终的界面呈现给用户,HTML+JS+CSS+Java的混合文件。

只有Servlet的时候要返回一个页面

image-20240714113625662

很麻烦所以就来了JSP。JSP会先解析成java文件然后编译成.class文件其实也是一个Servlet自动的生成write方法。

那我们直接写html能直接访问吗?也是可以的。那既然html可以但是为什么要JSP呢?因为有些页面需要数据不是静态的页面,就是把java处理的东西加入到页面里面,只有在JSP里面可以。

当服务器接收到一个后缀是jsp的请求是,就会将请求交给jso引擎去处理,每个JSP页面第一次被访问的时候,就会翻译成一个Servlet文件,再由Web容器调用Servlet完成响应

单纯从开发的角度来看,JSP就是在HTML中嵌入java程序。

具体的嵌入方式有3种:

1、JSP脚本,执行java逻辑代码

  • ```jsp
    <% java代码 %>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    <%这里的代码不会转换%>

    - 2、JSP声明

    ```jsp
    <%!
    public String test(){
    return "hello world"
    }
    %>

    但是这里只能声明方法调用方法还是只能用第一种

    1
    2
    3
    <%
    System.out.println(test())
    %>
  • JSP表达式:把java对象直接输出到HTML中
1
<%= java变量%>

JSP内置对象9个

request :表示一次请求,HttpServletRequest

response: 表示一次响应,HttpServletResponse

pageContext: 页面上下文,获取页面信息,PageContext.

session:表示一次会话,保存用户信息,HttpSession

application: ServletContext,表示Web应用,全局对象,保存所有用户共享信息。

config:当前JSP对应的Servlet的ServletConfig对象,可以获取当前Servlet信息,

out:向浏览器输出数据,JspWriter

page:当前Jsp对应的Servlet对象

excerption:JSP页面发生的异常,Exception

常用的是request\response\session\application\pageContext

request常用的方法

1、getParameter(String key)

2、void setAttribute(String key,Object value) 通过键值对的形式保存数据

3、Object getAttribute(Object value)

image-20240714152422309

image-20240714152839104

image-20240714152847646

4、RequestDispatcher getRequestDispatcher(String path)返回一个RequestDispatcher 对象,该对象的forword方法用于请求转发。

5、String[] getParameterValues() 获取客户端传来的多个同名参数

6、 void setCharacterEncoding(String charset) 指定每个请求的编码

Http请求状态码

200:正常

404: 资源找不到

400:请求类型不匹配

500:java程序抛出异常

response常用方法

1、sendRedirect(String path) //重定向,页面之间的跳转。

这个和上面那个的区别就是这个不能传输数据!页面可以跳转。

转发和重定向的区别:转发getRequestDispatcher(String path)和重定向sendRedirect(String path),

转发是将同一个请求传给下一个页面,重定向是创建一个新的请求传给下一个页面。

转发:同一个请求在服务器之间传递,地址栏不变,页脚服务跳转。

重定向:由客户端发送一次新的请求来访问跳转后的目标资源,地址栏改变,也叫客户端跳转

如果两个页面之间需要request来传值,则必须使用转发,不能使用重定向。

用户登录,如果用户名和密码正确,则跳转到首页(转发),并且展示用户名,否则重新回到登录页面(重定向)。

登录界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/7/14
Time: 23:39
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/check.jsp" method="post">
用户名:<input type="text" name="username" /><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>

check.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/7/14
Time: 23:58
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
String username = request.getParameter("username");
String password = request.getParameter("password");
if(username.equals("admin") && password.equals("123123")){
request.setAttribute("name",username);
request.getRequestDispatcher("welcome.jsp").forward(request,response);
}else{
response.sendRedirect("login.jsp");
}
%>
</body>
</html>

主页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/7/15
Time: 0:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
String name = request.getParameter("username");
%>
欢迎回来<%=name%>
</body>
</html>

Session

完成用户会话的。

服务器无法识别每一次HTTP请求的出处(不知道来自于那个终端)他只会接收到一个请求信号,所以说就存在一个问题:有可能将用户的响应发送给其他人,必须有一种技术来让服务器知道请求来自于哪里,这就是会话技术。

会话:就是浏览器或者客户端和服务器之间发生的一系列连续的请求和响应的过程,打开浏览器进行操作到关闭浏览器的过程。

会话状态:指服务器和浏览器在会话过程中产生的状态信息,借助于会话的状态,服务器能够把属于同一次会话的请求和响应关联起来。

image-20240715002144965

实现会话有两种方式:

  • session(在服务器中记录的)
  • cookie(作用于客户端的)

但是cookie不是内置对象,内置对象是session

属于同一次会话的请求都有一个相同的标识符,sessionID

session常用的方法:

String getId(); 获取到当前的sessionID

void setMaxINactivetInterval(int interval) 设置session的失效时间,单位为秒

int getMaxinactiveinterval(); 获取最长的失效时间

void invalidate() 设置session立即失效

void setAttibute(String key,Object value) 通过键值对来存储数据

Object getAttribute(String key) 通过键获取对应的数据

void removeAttribute(String key) 通过键删除对应的数据

Cookie是服务端在HTTP响应中附带传给浏览器的一个小的文本文件,一旦浏览器保存了某个Cookie,在之后的请求和想用过程中,会讲此Cookie来回传递,这样就可以以Cookie完成客户端和服务端的数据交互。

Cookie是服务器创建的。

  • 创建Cookie

  • ```java
    Cookie cookie = new Cookie(“name”,”张三”);
    response.addCookie(cookie);

    1
    2
    3
    4
    5
    6
    7

    - 读取Cookie

    - ```java
    for (Cookie cookie : request.getCookies()) {
    out.write(cookie.toString());
    }

    Cookie常用的方法:

    void setMaxAge( int age) 设置Cookie的有效时间,单位为秒

    int getMaxAge() 获取Cookie的有效时间 一开始是-1一关浏览器就没了

    String getName() 获取name

    String getValue() 获取value

  • session:保存在服务器

    ​ 保存的数据时Object

    ​ 会随着会话的结束而销毁

    ​ 保存重要信息

    cookie : 保存在浏览器

    ​ 保存的数据时String类型

    ​ 可以长期保存在浏览器中,与会话无关

    ​ 保存不重要的信息

存储用户信息

session: setAttribute(“name”,”admin”) 存

getAttribute(“name” )取

生命周期:只要WEB应用重启就销毁,客户端:只要关闭浏览器就销毁

cookie:response.addCookie(new Cookie(“name”,”admin”)) 存

1
2
3
4
5
6
Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
if(cookie.getName().equals("name")){
out.write("欢迎回来"+cookie.getValue());
}
}

生命周期:不随服务器的重启而销毁,

客户端:默认是只要关闭浏览器就销毁,我们通过setMaxAge()方法设置有效期,一旦设置了有效期,则不随浏览器的关闭而销毁,而是有设置的时间来决定。

退出登录:sertMaxAge(0)就消失了

JSP内置对象的作用域

只讨论4个因为只有这四个

setAttribute,getAttribute有存数据和取数据的方法

Cookie不属于内置对象

page的作用域:对应的内置对象是pageContext

request作用域:对应的内置对象是request

session作用域:对应的内置对象是session

application作用域:对应的内置对象是application。

page < request < session < application

page只在当前页面有效。

request在一次请求内有效。

session在一次会话内有效。

appllication对应整个WEB应用的。

EL表达式

Expression Language 表达式语言,替代JSP页面中数据访问时的复杂编码。可以非常便捷的取出域对象中(有作用域的)保存的数据。,前提是一定要set进去,EL相当于简化getAttribute

${变量名},变量名就是对应的key值

EL对于4种域对象的查找顺序:

pageContext->request->session->application

按照上述顺序查找,找到立即返回,如果在application中找不到就返回null

2、当然也可以指定作用域进行查找

pageContext: ${pageScope.name}

request: ${requestScope.name}

sessionContext: ${sessionScope.name}

applicationContext: ${applicationScope.name}

对象的toString是用类名全地址@hash值拼起来,所以我们会重写toString

EL表达式只能在JSP里面使用。

EL表达式还能赋值。

image-20240716080613208绑定的是方法,不是绑定的属性。

数据级联:

${user.id}

${user[“id”]}

这两种一样的。

EL执行表达式

&& || ! <>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&& and

|| or

! not

== eq

!= ne

< lt
> gt
<= le
>= ge
empty 判断空

JSTL

JSP Standard Tag Library JSP 标准标签库,JSP为开发者提供的一系列的标签,使用这些标签可以完成一些列逻辑处理,比如循环遍历集合,让代码更简洁,不再出现JSP脚本穿插的情况。

实际开发中EL和JSTL结合使用,JSTL侧重于逻辑处理,EL侧重于展示。

JSTL的使用

1、需要导入jar包(两个jstl.jar,stander.jar)存放的位置是WEB-INF里面

2、在JSP页面开始的地方导入JSTL标准库

3、引进标签库

1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

image-20240716101050013

JSTL的优点:

1、提供了统一的标签

2、可以用于编写各种动态功能

常用标签:

set,out,remove,catch

set: 向域对象中添加数据

1
2
3
<%
<c:set var="name" value="tome" scope="requet"></c:set>
%>

默认存放顺序是从小到大

可以用scope来指定存放域

但是这个标签只能对简单的数字,字符串进行操作,不能操作对象。

image-20240716102239750

out:输出域对象中的数据

1
2
<c:set var="name" value="tom"></c:set>
<c:out value="${name}" default="未定义"></c:out>

remove标签

image-20240716102924609

catch标签:

image-20240716102901048

条件标签:if 、choose

image-20240716105041243

迭代标签:

image-20240716172808538

在迭代标签里面有begin \stop \step,起始位置,结束位置,步长

下标从0开始

还有varStatus来给序号

image-20240716173208205

格式化标签库常用的标签:

image-20240716173618344

函数标签库

image-20240716173928857

过滤器

Filter

为什么要用?

因为在不同的Servlet会遇到同样的设置,然后就用过滤器统一处理。功能:

1、用来拦截传入的请求和传出的响应

2、修改或以某种方式处理正在客户端和服务端之间交换的数据流

如何使用?

与使用Servlet类似,Filter是JAVA WEB提供的一个接口,开发者只需要自定义一个类并且实现接口即可。

为什么在Fileter可以只实现doFilter就行?因为接口里面的方法被default 修饰就可以不实现也可以,jdk8出来之后可以允许接口里面有方法的实现。

现在xml文件中配置过滤器

1
2
3
4
5
6
7
8
<filter>
<filter-name>character</filter-name>
<filter-class>com.bitzh.test.filter.CharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>character</filter-name>
<url-pattern>/login</url-pattern>
</filter-mapping>
1
2
3
4
5
6
7
public class CharacterFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
filterChain.doFilter(servletRequest,servletResponse);
}
}

一定要实现filterChain.doFilter才能把处理完的请求继续传下去。

否则这个请求或者响应无法向后传递。

filter的生命周期

在Tomcat启动的时候,

还是利用反射器在xml里面配置的东西找到反射路径然后获取Class,然后调用无参构造器然后实例化对象同时调用init方法初始化对象。doFilter方法调用多次,当Tomcat关闭的时候,调用destory方法销毁Filter对象

无参构造函数:只用一次,当Tomcat启动的时候,Filter进行配置

init方法:实例化对象完成之后

doFilter方法:调用多次,访问多少次调用多少次

destory方法:当Tomcat关闭的时候调用。

Filter的顺序和xml里面配置的顺序是一样的。

因为xml是从上到下顺序读取的。

这个也可以用注解来配置,那用注解的话没有办法来决定先后顺序了,这时候就是随机的。

实际开发中Filter的使用场景:

1、统一处理中文乱码

2、屏蔽敏感词

3、控制资源的访问权限

文件的上传下载

  • JSP

    1、input的type设置为file

    2、form表单的上传设置为post、get请求会将文件名传给服务端而不是文件本身

    3、form表单的enctype设置mulitpart/form-data,以二进制的形式传输

  • Servlet

fileupload组件

这个组件可以将所有的请求信息都解析成FileIte对象,可以通过对FileItem对象操作完成上传,面向对象的思想。

文件下载

image-20240717205811187

Ajax

Asynchronnous JavaScript And XML :异步的JavaScript和XML

AJAX不是新的编程语言,指的是一种交互方式,异步加载,客户端和服务器的数据交互更新在局部页面的技术,不需要刷新整个页面(局部刷新)

优点:

1、局部刷新、效率更高

2、用户体验更好

基于JQuery

一开始没用ajax的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/7/20
Time: 1:40
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script type="text/javascript" src="js/jquery-2.1.1.min.js"></script>
<title>test01</title>
</head>
<body>
${str}
<form action="/test" method="post">
<input type="text" name="username">
<input type="submit" value="提交">
</form>
</body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import javax.servlet.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;

/**
* @Auther: oyy0v0
* @Date: 2024/7/20 - 07 - 20 - 1:46
* @Description: PACKAGE_NAME
* @version: 1.0
*/
@WebServlet("/test")
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String str = "HelloWorld";
req.setAttribute("str",str);
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}

用了ajax之后,基于(jq)就不能用form来提交表单了,改用jQuery方式动态绑定事件来提交。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/7/20
Time: 1:40
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<script type="text/javascript" src="js/jquery-2.1.1.min.js"></script>
<title>test01</title>
<script type="text/javascript">
$(function (){
//拿到对象btn
var btn = $("#btn");
btn.click(function (){
//ajax语法
$.ajax({
url: '/test',
type: 'post',
//我们要传给后端的参数
data:'id=1',
//服务端给我们返回的数据类型
dataType: 'text',
//服务器响应的内容直接给弹出
success:function(data){
var text = $("#text");
text.before("<span>"+data+"</span><br/>");
}
})
});
})
</script>
</head>
<body>
${str}

<input type="text" name="username">
<input id="btn" type="button" value="提交">

</body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import javax.servlet.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;

/**
* @Auther: oyy0v0
* @Date: 2024/7/20 - 07 - 20 - 1:46
* @Description: PACKAGE_NAME
* @version: 1.0
*/
@WebServlet("/test")
public class MyServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String str = "HelloWorld";
resp.getWriter().write(str);
// req.setAttribute("str",str);
// req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}

Servlet这边不能跳转到jsp,只能将数据返回,不然的话返回的内容就是在再多写了一次页面

image-20240720130905971

我们只需要把数据返回给前端。

传统的WEB数据交互 VS AJAX数据交互

客户端请求的方式不同:

传统,浏览器发送的是同步请求(form,a标签)

AJAX,异步引擎对象发送异步请求

服务器响应的方式不同:

传统,响应一个完整JSP页面(视图—)

AJAX,响应需要的数据

  • 客户端处理方式不同:

传统:需要等待服务器完成响应并且重新加载整个页面之后才能继续后续的操作

image-20240720150115761

基于jQuery的AJAX语法

$.ajax({属性})

常用的属性参数:

url:请求的后端服务地址

type:请求方式,默认get

data:请求参数

dataType:服务器返回的数据类型,text/json

success:请求成功的回调函数

error:请求失败的回调函数

complete:请求完成的回调函数

先请求完成再成功,如果是失败就先进error在进完成

JSON

JavaScriptObjectNotation,一种轻量级数据交互格式完成js和java等后端开发语言对象数据之间的转换,客户端和服务器之间传输对象数据。

传输对象的话在后端需要我们手动的将java对象转化成为json对象,我们使用工具类json-lib

1
2
JSONObject jsonObject = JSONObject.fromObject(user);
resp.getWriter().write(jsonObject.toString());

常用案例:省市区三级联动,登录注册页面账号密码提示验证

JDBC

Java DataBase Connectivity是一个独立于特定数据库的管理系统,通用的SQL数据库存取和操作的公共接口。

定义了一组标准,为访问不同数据库提供了同一路径

image-20240720153924598

jdbc体系结构

JDBC接口包括两个层面:

  • 面向应用的API,程序员使用

  • 面向数据库的,供厂商开发数据库的驱动程序

![image-20240720155729149](https://oyy0v0pic.oss-cn-guangzhou.aliyuncs.com/image-20240720155729149.png)

JDBC API

提供者 java官方

内容:供开发者调用的接口

jaca.sql,javax.sql

  • DriverManager类
  • Connection接口
  • Statement接口
  • ResultSet接口

DriverManager

提供者:java官方

作用:管理不同的JDBC驱动

JDBC驱动

提供者:数据库厂商

作用:负责连接不同的数据库

JDBC的使用

1、加载数据库驱动、java程序和数据库之间的桥梁

2、获取Connection,java程序与数据库的一次连接

3、创建Statement对象,由Connection产生,执行SQL语句

4、如果需要接受返回值,创建ResultSet对象,保存Statement执行之后所查询到之后的结果

数据库连接池

JDBC的开发流程

  • 加载驱动(只需要加载一次)
  • 建立数据库连接(Connection)
  • 执行SQL语句(Statement)
  • ResultSet接受结果集(查询)
  • 断开连接,释放资源

这种方式,每次连接对象都通过DriverManager来获取,每次获取都需要向数据库申请获取连接,验证用户名和密码,执行完SQL语句后会断开连接,这样就会造成资源的浪费

数据库连接资源没有得到很好的重复利用。

可以使用数据库连接池来解决这个问题

数据库连接池的基本思想就是为数据库建立一个缓冲池,预先向缓冲池中放入一定数量的连接对象,当需要获取数据库连接的时候,只需要从缓冲池中取出一个对象,用完之后再放回到缓冲池中,供下一次请求使用,做到了资源的重复利用,允许程序重复使用一个现有的数据库连接对象,而不用重复创建。

当数据库连接池中没有空闲的连接时,新的请求会进入到等待队列,等待其他线程释放连接。

数据库连接池实现

JDBC的数据库连接池使用javax.sql.DataSource接口来完成。

DataSouce是Java官方提供的接口,使用的时候开发者不需要自己来实现接口,可以使用第三方工具,C3P0是一个常用的第三方实现,实际开发中直接使用C3P0即可完成数据库连接池操作。

1、导入jar包

2、创建C3P0

3、加载驱动

4、设置url,username,password

5、得到一个连接对象

传统方式拿到的Connection

image-20240720164545391

C3P0拿到的Connection

image-20240720164600027

6、其他的跟原来的一样

7、可以设置初始化连接个数

image-20240720185504321

8、设置最大连接数

image-20240720185830170

9、当连接对象不够时,再次申请连接对象个数

image-20240720185602699

10、设置最小连接数(设置空闲的最小下线限)

image-20240720185800084

假设一开始有20个用了十八个然后还剩两个这时候就该去申请了

一般我们把配置文件写在xml配置文件中

因为如果是java文件修改的话还需要重新编译但是xml文件不用重新编译

image-20240720190141015

然后在创建c3p0的时候把xml的name的属性值传入到构造器中即可完成数据库连接池的初始化

放在src的根目录就行同级。

DBUtiles

这个工具

在查询的时候比较麻烦

DBUtiles可以帮助开发者完成数据的封装(结果集到java的映射)

image-20240720191748963

1、导入jar包

ResultHandler接口用来处理结果集,可以将查询到的结果集转换成java对象,提供了四种实现类

  • BeanHandler 将结果集映射成Java对象
  • BeanListHandler 将结果集映射成List集合List
  • MapHandler 转换成Map集合
  • MapListHandler 转换成MapList集合

2、

image-20240720192812666

image-20240720193625572

javaWeb第二版(和第一版有重复)

JavaWeb

这个阶段会比较难,重点是理解和记忆,多写代码,没别的方法了熬过去就好,下面的内容不会很详细的写了,重要的是脑子记住

JavaEE

javaee平台规范了在开发企业级web应用中的技术标准

image-20240131213408398

Tomcat安装和应用

首先去官网下载tomcat,然后解压就可以使用,下载的时候下载windows.zip,下面有个sourceCode是Tomcat的源代码

然后安装,这个时候解压就可以使用,但是如果下载了很多版本的tomcat就需要在系统环境变量中配置catalina_home,就像java_home一样,将路径改到对应的jdk和对应的tomcat就好了

tomcat的启动

在tomcat里面有个startup.bat的文件就启动了,关闭的话就运行shutdown.bat文件

访问tomcat

因为tomcat是个web容器,虽然是本地的,但是本地可以自己访问到自己,所以可以在浏览器上输入地址来访问tomcat

如果在服务器上的话访问方式就是http://ip:port

如果是本地上访问的路径就是http://localhost:8080,这个8080是可以自己设置的,但是默认就是8080,port是端口号的意思,端口号就是每台计算机内识别要启动哪个程序的一个编号,都是唯一的。

Tomcat的结构认识

首先看到tomcat的文件目录,里面有很多,就跟java项目一样有一些配置目录,要了解一下每个目录都是放什么的

bin

首先就是bin目录,里面放的都是淫邪命令文件.sh结尾的是linux的命令,.bat结尾的就是windows命令,就像一开始使用的tomcat启动和关闭文件都是放在bin里面的

conf

其次就是conf目录,里面放的是一些配置文件,配置文件就是存放一些.xml文件,写一些依赖之类的,其中有些比较重要的文件,比如context.xml和server.xml和web.xml后面还会详细学一下xml怎么写,但是现在知道是一些配置文件就好了

lib

lib目录就是直接导入一些本地的jar包,需要加载的,而jar包就是一些java压缩好的一些函数库或者类库,里面的功能都已经写好了,直接导入就能用,我是这么理解的。

logs

logs文件就是用来存放tomcat运行时产生的日志文件,出错的时候能看出是什么地方发生了错误

temp

temp文件用来存放在tomcat运行时产生的临时文件

webapps

这个文件用来存放应用程序,当tomcat启动时会去加载webapps目录下的应用程序。那么war和jar有什么区别呢

javaweb打包的是war包,需要放到tomcat下面才能运行

springboot打包的是jar包,里面内置tomcat不需要放到tomcat下面就能运行,但是jar包没有静态资源比如index.jsp这类网页

work

work里面放的是编译过后的文件,比如index.jsp编译过后形成的文件

Tomcat部署项目

第一种方法

在Tomcat的webapps中创建一个自己项目的目录,然后在自己的项目文件放入index.jsp然后启动tomcat之后在浏览器里访问对应的路径就能访问得到了

第二种方法

添加配置文件,在webapps里面有个catalina文件夹,里面有localhost文件,在里面添加配置文件也就是xml文件,然后xml文件里面写

1
<Context path="/项目名" docBase="绝对路径"/>

这样子就可以在启动tomcat的时候,访问localhost的时候浏览器定位到你的项目那里了

Tomcat部署文件配置

 Tomcat由4个xml配置,分别是context.xml,web.xml,server.xml和tomcat-users.xml。每个文件都有自己的功能和配置方法

context.xml

context.xml是tomcat公用的环境配置,里面有路径之类的Tomcat服务器会定时扫描这个文件,路径或者什么的变了,会重新加载,不用重启tomcat。

web.xml

这里面是Web应用程序的配置文件,是所有web应用的父文件,里面是一些总的组件配置文件。

server.xml

里面是tomcat核心配置文件,里面对应的是具体组件,每一个元素对应tomcat的一个组件

tomcat-users.xml

配置访问Tomcat以及角色的配置文件。

控制台乱码的问题

就是由于tomcat在输出日志的时候编码格式是utf-8,但是windows得编码格式是GBK,所以我们只要在conf目录修改配置,找到logging的英文将utf-8修改成GBK就好了。

修改Tomcat监听端口

在server.xml里面修改port端口号就能修改监听的端口了

可以配置Tomcat并发数

在Connecter里面可以自己配置有

maxSpareThread最大并发数

minSpareThread最小并发数,一旦最大线程数超过500,tomcat就会关闭socket线程。

然后acceptCount是当所有线程都被占用时,可以放到处理队列中等待的数量,一旦超过这个数量就不会允许处理了。

配置Tomcat Manager

什么是Tomcat Manager这个是tomcat自带的,但是默认一般会禁用,除非我们自己设置,这个使我们用来管理tomcat里面的web应用程序的

配置tomcat访问用户

Tomcat Manager中没有访问用户所以要用到tomcat-user.xml中配置访问用户,配置需要两个部分,一个是角色配置,用户名和密码

Tomcat Manager中的角色分类

manager-gui角色:

允许访问HTML GUI和状态页面(即URL路径为/manager/html/*)

manager-script角色:

允许访问文本界面和状态页面(即URL路径为/manager/text/*)

manager-jmx角色:

允许访问JMX代理和状态页面(即URL路径为/manager/jmxproxy/*)

manager- status角色:

仅允许访问状态页面(即URL路径为/manager/status/*)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 <role   rolename="manager-gui"/>


<role rolename="manager-script"/>


<role rolename="manager-jmx"/>


<role rolename="manager-status"/>


<role rolename="admin-gui"/>


<role rolename="admin-script"/>


<user username="tomcat" password="tomcat"


roles="manager-gui,manager-script,manager-jmx,manager-status,admin-gui,admin-script"/>

Tomcat主要组件

Server组件

启动一个Server就是启动一个JVM然后他的端口号是8005,主要用来接收shotdown的指令,用来关闭tomcat,默认设置就是shotdown,server定义不能使用同一个端口,也就是如果启动了多个server那么端口就不能设置为8005

Service组件

Serviece主要关联一个引擎和多个连接器,name为Catalina此服务名称为Catalina。

Connector组件

支持处理不同请求的组件,也就是说主要处理连接器所连接的组件,以适应多种请求方式。默认只开启了http协议的连接器,在Tomcat中的连接器类型有四种

1、HTTP连接器

2、SSL连接器

3、AJP 1.3连接器

4、proxy连接器

image-20240203142751599

这里的port是监听的端口,什么是监听后面会说

protocol是连接器使用的协议

redirectPort就是客户端发来消息的时候就会把消息发到8443这个端口号这里

Enigine组件

Engine是Service的一个实例,就是servlet的引擎

Host组件

位于Engine容器中用于接受请求并进行相应处理的虚拟主机。

Context组件

Context是Host的子标签,代表指定的一个Web应用,每一个web应用都是war包或者文件目录,里面会写Web应用的存放位置

Tomcat处理请求的过程

image-20240203143540798

1、首先用户访问localhost:8080/test/index.jsp这种路径然后浏览器就会发送请求给tomcat,被监听的8080端口并处理HTTP/1.1协议

的Connecter获得,

2、Connecter会把请求交给他所在的Service的Engine来处理,并等待Engine的回应。

3、然后Engine获得这个localhost:8080/test/index.jsp路径,匹配所有的虚拟主机Host

4、然后Host会根据/test来匹配他所拥有的Context,然后Context是用来写web应用程序的路径的然后就会找到我们的程序

5、开始程序的时候构造HttpServletRequest对象和HttpServletResponse对象,然后开始业务逻辑的执行,将客户端传来的数据当做参数调用JspServlet的doGet()或者doPost()来获取参数,以及用里面的API来完成注册数据等操作

7、执行完这些后,Context会将执行后的结果通过HttpServletResponse对象返回给Host.

8、Host把HttpServletResponse返回给Engine

9、Engine把HttpServletResponse返回给Context

10、然后继续返回给客户就完成了。

Http协议

Http协议

协议 Protocol

A给B发送一条消息

内容 what do you want to eat? 今晚想吃什么

规则: 英文 中文 应用层协议 数据如何解析如何使用 http ftp….

方式:微信 QQ 短信 传输层协议 数据如何发送和接受 tcp udp

地址:微信号 QQ号 电话号 网络协议 数据接收和发送的位置 ip

Http协议就是超文本传输协议

是一个简单的请求响应协议,它通常运行在tcp之上,它指定了客户端可能发送给服务器什么样的信息以及得到什么响应。

HTTP协议的特点

1、支持客户端、服务器模式

HTTP协议支持客户端服务器模式,需要使用浏览器作为客户端来访问浏览器

2、通讯快速3、灵活4、无连接 每次请求一次,就是放一次链接

5、单向性:服务端永远等待客户端的请求

6、无状态:对失误处理没有记忆能力,传不过去只能重新传。

HTTP协议发展和版本

http1.0最早在1996年在网页中使用,内容简单,所以每次请求都与服务器建立一个TCP链接,服务器处理完之后就立刻断开连接了,服务器不跟踪每个客户端也不记录过去的请求,请求只能由客户端发起。

http1.1

这个时候HTTP已经默认使用Connection:keep-alive(长连接),也就是一次连接,可以多次请求。

这样就减少了建立和关闭连接所消耗的时间和延时

一个包含许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的链接。HTTP1.1还允许客户端不用等待上一次请求结果返回就发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次返回响应结果,以保证客户端能够区分出每次请求的响应内容,这样就减少了整个下载过程中所需要的时间。

http2.0

长连接

沿用了http1.1的优势

多路复用

HTTP2.0加强的是二进制传输,在HTTP1.x中,我们是通过文本的方式传输数据,在HTTP2.0中引入了新的编码机制,所传输的数据都会被分割,并采用二进制格式编码

多路复用,连接共享。不同的request可以使用同一个链接传输,(最后根据每个request上的id号组合成正常的请求)

HTTP2.0,有两个概念很重要:帧(frame)和流(stream)

帧是最小的数据单位,每个帧会标识出该帧属于哪个流,流是多个帧组成的数据流

多路复用的意思就是在一个TCP链接中存在多个流,可以同时发送多个请求,对端可以同过帧中的表示知道该帧属于哪个请求,在客户端,这些帧乱序发送!在1.x的时候是顺序发送的,这个时候是乱序发送,到对端后再根据每个帧首部 的流标识符重新组装。通过该技术,可以避免HTTP旧版本的队头阻塞问题,极大提高传输性能。

队头阻塞问题可以理解文由于是顺序发送,但是有可能排在第一的文件内容很大,导致一直在队头但是发送不出去,其他的已经弄好了,但是由于是顺序发送所以就造成了队头阻塞问题,而且在实际问题中并不知道那个文件是大那个文件是小的。

首部压缩

由于1.1header带有大量的信息,并且得重复传输,2.0使用encoder来减少需要传输的header的大小

服务端推送

在HTTP2.0中,服务段克已在客户端某个请求后,主动推送其他资源。

就是有一些请求客户端是一定会请求的那么这个时候,服务器就会主动推送给客户端,采用服务端push技术,提前给客户端推送必要的资源。

更安全

增加了黑名单机制,禁用了不安全的加密算法

HTTP请求

当在浏览器输入URL的时候,浏览器会发送一个request来去获取路径下的资源,这时候服务器会把response发送的消息给浏览器

然后浏览器会分析response中的html发现还有很多引用的文件比如css,js等

浏览器会自动再次发送request去获取图片,css,js等文件

等所有的文件都下载成功后,网页就显示出来了

request分成三个部分:第一个部分是request line,第二个部分是request header,第三个部分是request body

请求的主要部分

请求行 request line

get/myproject/img/logo.png HTTP/1.1

1、请求方式 默认get

2、资源路径

3、请求使用的协议

请求头 request header 键值对

请求体 request body

get方式请求,数据会直接放在URL地址后,请求体中没有数据

POST方式请求,请求体中会有数据

请求行

GET/course/id/1.html?a=3 HTTP/1.1

POST /login HTTP/1.1

请求头

请求头用于说明是谁或什么在发送请求,请求源于何处,或者客户端的喜好及能力。服务器可以根据请求头部给出的客户端信息,试着给客户端提供更好的响应。请求头中的信息的格式为 key :value

请求头中包含:

Host

客户端指定自己想要访问的web服务器的域名/IP 地址和端口号

Connection

连接方式。如果是close那么就是短连接的方式,如果是keep-alive就是长连接的方式,网络连接就是持久的。

Upgrade-Insecure-Requests

服务端是否支持https加密协议

Cache-Control

指定请求和响应遵循的缓存机制

User-Agent

浏览器表明自己的身份(是那种浏览器)

Accept

告诉WEB服务器自己接受什么类型,

1
2
*/*   //表示任何类型
type/* //表示该类型下的所有子类型

Accept-Encoding

浏览器申明自己接受的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)

Accept-Language

浏览器申明自己接受的语言。语言跟字符集的区别:中文是语言,中文有很多字符集,比如gbk,gb2312

Accept-Charset

浏览器告诉服务器自己能接受的字符集

Referer

浏览器向WEB服务器表明自己是从哪个网页URL获得点击当前请求中的网址/URL

Refresh

表示浏览器应该在多少时间之后刷新文档,以秒计时

Cookie

可以向服务端传递数据一种模型

请求体

客户端传递给服务器的数据。比如:表单使用post方式提交的数据,上传的文件数据等。

请求方式

GET

向指定的资源发出“显示”请求。GET请求中会将请求中传递的数据包含在URL中并在浏览器的地址栏中显示。GET请求传递数据时要求数据必须是ASCLL字符。GET请求可以被浏览器缓存。

POST

向指定资源提交数据,请求服务器进行处理。数据被包含在请求体中。POST请求传递数据时,数据可以试试ASCLL字符也可以是字节型数据,默认为字符型。POST请求默认情况下不会被浏览器所缓存。

HEAD

向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以再不必传输整个相应内容的情况下,就可以获取包含在响应消息头中的元信息。

PUT

向指定资源位置上传其最新内容

DELETE

请求服务器删除Request-URL所表示的资源

TRACE

回显服务器收到的请求,主要用于测试或诊断

OPTIONS

这个方法可以使服务器传回该资源所支持的所有HTTP请求方法。用‘*’来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。

CONNECT

HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)

GET和POST的区别

1、GET在浏览器回退时是无害的,而POST会再次提交请求

2、GET产生的URL地址可以被Bookmark(被保存为书签),而POST不可以。

3、GET请求只会被浏览器主动cache,而POST不会,除非手动设置。

4、GET请求只能进行url编码,而POST支持多种编码方式

5、GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留

6、GET请求在URL中传送的参数是有长度限制的,而POST则没有。对参数的数据类型GET值接受ASCLL字符,而POST即可是字符也可以是字节

7、GET比POST更不安全,因为参数直接暴露在 URL上,所以不能用来传递敏感信息

8、GET参数通过URL传递,POST放在Request body中

HTTP响应

响应的主要组成部分

响应行 HTTP/1.1 200

1、协议

2、响应状态码 200 >> OK 正常响应 304 重定向 404 请求的资源你没找到 500服务器出现异常没办法响应

响应头

响应体

响应行比请求行就多了个状态码少了个请求方式

状态码的话可以上网搜

响应头

响应头用于高脂浏览器当前响应中的详细信息,浏览器通过获取响应头中的信息可以知道应该如何处理响应结果。响应头中的信息格式是键值对的形式 key:value

Date

响应的Date使用的是GMT时间格式,表示响应消息送达时间

Server

服务器通过这个Server告诉浏览器服务器的类型

Vary

客户端缓存机制或者是缓存服务器在做缓存操作的时候,会用到Vary头,会读取响应头中的Vary内容,进行一些缓存的判断

Context-Length

表示内容长度。只有当浏览器使用持久HTTP链接时才需要这个数据

Context-Type

表示响应的文档属于什么MIME类型

响应体

响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,。。。

JAVAWEB项目部署

认识JAVAWEB项目结构

-project项目的根目录

-静态资源文件/jsp

-lib  jar包目录

-classes  java字节码目录

-web.xml  项目配置文件

image-20240204005127028

使用idea开发JAVAWEB项目

这个了解即可,主要是用来熟悉在tomcat是如何运行项目的。

在启动项目前要在Edit Configuerations对项目进行启动之前的配置

在Deployment中,确认部署的项目是不是我们要运行的项目

Application Context中指定的是我们项目访问的路径名

在On Update action:当代码改变的时候,需要IDEA为你做什么;选项为Update classes and resources,意义为:更新字节码和其他资源

On frame deactivation: 当失去焦点(比如你最小化IDEA窗口),需要IDEA做什么,选项为Update resources,意义为:更新其他资源

第一种 Idea部署JAVAWEB项目并运行的方式(掌握)

在idea中默认不会把web项目真正的部署到Tomcat的webapps目录中,而是通过每个web项目创建一个独立的Tomcat副本在Tomcat副本中通过的Tomcat的Context组件完成项目的目录指定,在Context组件的docBase属性中会指定Idea对WEB项目编译后的目录,这种方式就是以后springboot也是类似的。

Idea会通过执行Catalina.bat启动脚本启动Tomcat,通过启动参数来指定启动Tomcat副本运行指定目录中的项目

Catalina_Base:tomcat副本的工作目录

Catalina_HOME:Tomcat的安装目录

在Catalina.bat启动脚本运行时,会先去判断脚本中的CATALINA_HOME以及CATALINA_BASE中是否有默认值,如果没有则会直接读取系统环境变量中的值作为他的默认值。

Servlet

Servlet是Server Applet的简称,成为程序端小程序,Servlet主要功能在于能在服务器中执行并生成数据

Servlet技术特点:Servlet使用单进程多线程方式运行。

Servlet在应用程序中的位置

浏览器(客户端)根据HTTP协议(protocaol)连接HTTP Server 然后连接Servlet,Servlet连接DaTabase

静态资源和动态资源的区分

静态资源:每次访问都不需要运算,直接就可以返回的资源,比如HTML,CSS,JS

动态资源:每次访问都需要运算代码生成的资源如SEervlet,JSP,每次访问获得的结果坑是不一样的

Servlet在程序中处于一个什么样的地位

Servlet是可以接受Http请求并做出响应的一种技术,JAVA语言编写的一种动态资源

Servlet是前后端衔接的一种技术,不是所有的JAVA类都可以接受请求和做出响应,Servlet可以

在MVC模式中,Servlet作为Controller层主要技术,用于浏览器完成数据交互,控制交互逻辑

Servlet开发流程

1、创建一个JAVAWEB项目,并在项目中开发自己的Servlet,继承HttpServlet类

2、在类中重写service方法,里面有两个参数HttpServletRequest对象获得信息和HttpServletResponse对象响应

3、在web.xml中配置Servlet的映射路径,配上servlet和servlet-mapping

4、启动然后打开浏览器请求Servlet资源

了解HttpServletRequest对应的方法

image-20240204085215087

获取请求行信息
1
2
3
4
5
req.getRequestURL();//返回客户端浏览器发出请求时的完整URL
req.getRequestURI();//返回请求行中指定资源部分
req.getRemoteAddr();//返回请求的客户机的IP地址
req.getLocalAddr();//返回WEB服务器的IP地址
req.getLocalPort();//返回WEB服务器处理Http协议的连接器所监听的端口
获取请求头信息
1
2
3
4
req.getHeader("headerKey");//根据请求头中的key获取对应的value
String headerValue = req.getHeader("headerKey");
req.getHeaderNames();//获取请求头中所有的Key,该方法返回枚举类型
Enumeration <String> headerNames = req.getHeaderNames();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;


/**
* @Auther: oyy
* @Date: 2024/2/4 - 02 - 04 - 11:36
* @Description: PACKAGE_NAME
* @version: 1.0
*/
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Enumeration<String> headerNames = req.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
System.out.println(headerName+":"+req.getHeader(headerName));
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myServlet.do</url-pattern>
</servlet-mapping>
</web-app>
获取请求数据

在Servlet获取请求数据的方式

1
2
req.getParameter("key");//根据key获取指定的value
String str = req.getParameter("key");

获取复选框(checkbox组件)中的值

1
2
req.getParameterValues("checkboxkey");//获取复选框中的值,返回一个String[]
String keys = req.getParameterValues("checkboxkey");

获取所有提交数据的key

1
2
req.getParameterNames();//获取请求中所有的数据key,该方法返回一个枚举类型
Enumeration <String> parameterNames = req.getParameterNames();

获取Map结构获取提交数据

1
2
req.getParameterMap();//获取请求中所有的数据并存放到一个Map结构中,该方法返回的是一个Map,其中key为String类型Value为String[]类型
Map <String,String[]> parameterMap = req.getParameterMap();

设置请求编码

req.setCharacterEncoding(“utf-8”)

请求的数据包基于字节在网络上传输,但是Tomcat接收到的请求的数据包后会将数据包中的字节转换为字符,而Tomcat默认的编码格式不是utf-8所以如果有中文的话就会乱码,这时候就需要设置定请求编码了。

HttpServletResponse对象

回顾http响应:

http响应部分可以分为三个部分:响应行,响应头,响应体

1、响应行:响应协议 状态码 状态描述

                Http/1.1      200       OK

2、响应头:

Content-Type:响应内容的类型(MIME)

3、响应实体

服务器响应回来的内容

HttpServletResponse

HttpServletResponse对象代表服务器的响应。这个对象中封装了响应客户端浏览器的流对象,以及客户端浏览器响应的响应头、相应数据、响应状态码等信息。

响应的设置

ContentType响应头

resp.srtContentType(“MIME”);//该方法可以通过MIME-Type设置响应类型。

服务器通过MIME告知相应内容类型,而浏览器则通过MIME类型来确定如何处理文档。

设置字符型响应

resp.setContentType(“text/html”):

设置响应类型的文本型,内容含有html字符串,是默认的响应类型

resp.setContentType(“text/plain”):

设置响应类型为图片类型,图片类型为jpeg或jpg格式

resp.setContentType(“application/json”):

设置响应类型为图片类型,图片类型为gif格式

resp.setContentType(“image/gif”):

设置响应编码

response.setCharacterEncoding(“utf-8”);

设置服务端为浏览器产生响应的响应编码,服务端会根据此编码将响应内容的字符转换为字节。

response.setContextType(“text/html;charset=utf-8”);

设置服务器为浏览器产生响应的响应编码,服务端会根据此编码将响应内容的字符转换为字节。同时客户端浏览器会根据此编码方式显示响应内容。

在响应内容中添加附加信息(文件下载)

在实现文件下载时,我们需要修改响应头,添加附加信息。

1
2
response.setHeader("Content-Disposition","attachment;filename="+文件名);

Content-Disposition:attachment 该信息表示作为对下载文件的一个标识字段。不会在浏览器中显示而是直接做下载处理。

filename=文件名,表示指定下载文件的文件名。

告诉浏览器响应的数据是什么?浏览器根据响应头决定 数据如何应用

1
2
3
resp.setHeader("content-type","text/css");
resp.setContentType("text/html");//专门用于设置Content-Type 响应头
resp.getWriter().write("<h1>this is tag h1</h1>")

关于乱码问题

1、控制台乱码,在Tomcat的conf下的logging.properties中所有的UTF-8编码为GBK即可

2、post请求乱码 通过HttpServletRequest设置请求编码

1
req.setCharacterEncoding("UTF-8");

3、get请求乱码 需要手动进行编码解码,或者设置tomcat中的server.xml中的URL编码,tomcat9已经解决了该问题

1
2
3
4
<Connecter port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="utf-8"
>

4、响应乱码

通过HttpServletResponse设置响应编码

1
2
3
resp.setContentType("UTF-8");
//设置响应头,一遍浏览器知道以何种编码解析数据
resp.setContentType("text/html;charset=UTF-8");

Servlet继承结构

image-20240204201104043

Servlet接口

1、init(),创建Servlet对象后立即调用该方法完成其他初始化操作

2、service(),处理客户端请求,执行业务操作,利用响应对象响应客户端的请求。

3、destory(),在销毁Servlet对象之前调用该方法,释放资源

4、getServletConfig(),ServletConfig是容器向servlet传递参数的载体

5、getServletInfo(),获取servlet相关信息

ServletConfig接口

Servlet运行期间,需要一些辅助信息,这些信息可以再web.xml文件中,使用一个或多个元素,进行配置。当Tomcat初始化一个Servlet时,会将该Servlet的配置信息,封装到一个ServletConfig对象中,通过调用init(ServletConfig config)方法,将ServletConfig对称传递给Servlet

GenericServlet抽象类

GenericServlet是实现了Servlet接口的抽象类。在GenericServlet中进一步定义了Servlet接口的具体实现,其设计目的是为了和应用层协议解耦,在GenericServlet中包含一个Service抽象方法。我们也可以通过继承GenericServlet并实现Service方法实现请求的处理,但是需要将ServletRequest和ServletResponse转为HttpServletRequest和HttpServletResponse.

解耦就是是减少耦合的过程,使得模块或组件之间的依赖尽可能小。这样可以使得代码更加模块化、灵活、易于维护和扩展。

HttpServlet

继承自GenericServlet,针对于处理HTTP协议的请求所定制。在HttpServlet的service()方法中已经把ServletRequest和ServletResponse转为HttpServletResponse.直接使用HttpServletRequest和HttpServletResponse,不需要强转。实际开发中,直接继承HttpServlet,并根据请求方式复写doxxx()方法即可

image-20240204203255306

在我们自定义的Servlet中,如果想要区分请求方式,不同的请求方式使用不同的代码处理,那么我们重写doGet doPost即可

如果我们没有必要区分请求方式的差异,那么我们直接重写service方法即可

要么重写doGet 要么重写doPost 要么重写service 必须二选一,而且必须进行重写

Servlet生命周期

Servlet的生命周期是由容器管理的,分别经理四个阶段:

阶段 次数 时机

创建 1次 第一次请求

初始化 1次 实例化之后

执行服务 多次 每次请求

销毁 1 次 停止服务

1
2
3
4
new //实例化
init() //初始化
service() //执行服务
destory() //回收销毁

当客户端浏览器第一次请求Servlet时,容器会实例化这个Servlet,然后调用一次init方法,并在新线程中执行service方法处理请求。service方法执行完毕后容器不会销毁这个Servlet而是做缓存处理,当客户端浏览器再次请求这个Servlet时,容器会从缓存中直接找到这个Servlet对象,并再一次在新的线程中执行Service方法。当容器在销毁Servlet之前对调用一次destory方法。

注意:在Servlet中我们一般不要轻易使用成员变量,可能会造成线程安全问题

如果使用的话,应该尽量避免对成员变量产生修改,如果要产生修改要注意县城安全问题,如果我们自己添加线程安全编码处理,会严重印象效率,所以能不用成员变量就不用。

如果需要Servlet在服务器启动时就实例化并初始化,我们可以在servlet的配置中添加load-on-startup配置启动顺序,配置的数字为启动顺序,应避免冲突且应该>6.

Servlet处理请求的过程

当浏览器基于get方法请求我们创建Servlet时,我们自定义的Servlet中的doGet方法会被执行。

doGet方法能够被执行并处理get请求的原因是,容器在启动时会解析web工程中的WEB-INF目录中的web.xml文件,在该文件中我们配置了Servlet与URI的绑定,容器通过对请求的解析可以获取请求资源的URI,然后找到与该URI绑定的Servlet并做实例化处理。

在实例化时会使用Servlet接口类型作为引用类型的定义,并调用一次init方法,由于HttpServlet中重写了该方法所以最终执行的是HttpServlet中init方法,由于HttpServlet中重写了该方法所以最终执行的是HttpServlet中init方法(HttpServlet中的Init方法是一个空的方法体),然后在新的线程中调用service方法。

由于在HttpServlet中重写了Service方法所以最终执行的是HttpServlet中的service方法。

在service方法中通过request.getMethod()获取到请求方式进行判断如果是Get方法请求就执行doGet方法,如果是POST请求就执行doPost方法。

如果是基于GET方式提交的,并且在我们自定义的Servlet中有重写了HttpServlet中的doGet方法,那么最终会根据Java的多态特性转而执行我们自定义的Servlet中的doGet方法

ServletContext(重要)和ServletConfig(了解)

ServletContext对象

ServletContext对象介绍

ServletContext官方叫Servlet上下文。服务器会为每一个Web应用创建一个ServletContext对象。这个对象全局唯一,而且Web应用中的所有Servlet都共享这个对象。所以叫全局应用程序共享对象。

image-20240204232050031

ServletContext对象和作用

1、相对路径转绝对路径

2、获取容器的附加信息

3、读取配置信息

4、全局容器

ServletContext对象的使用

获取项目的部署名

context.getContextPath()

将项目的相对路径转化为项目的绝对路径(重要)

1
2
3
ServletContext servletContext1 = req.getServletContext();
String fileUpload = servletContext.getRealPath("fileUpload");

获得Servlet信息(重要)

1
String serverInfo = servletContext.getServerInfo();

获得Servlet版本号

1
2
servletContext.getMajorVersion();//获取servlet主版本号
servletContext.getMinorVersion();//副版本号
1
2
//获取web.xml中配置的全局信息,就是在<context-param>里面设置的param-name
String username = servletContext.getInitParameter("username");

获取web.xml中配置的全局的初始信息

1
2
3
4
5
Enumeration<String> onames = servletContext.getInitParameterNames();
while(pnams.hasMoreElements()){
String s = pnames.nextElement();
System.out.pringtln(e+":"+servletContext.getInitParameter(e));
}

向servletContext对象中添加数据

1
2
3
4
5
6
List<String> data = new ArrayList<>();
Collections.addAll(data,"张三","李四","王五");
servletContext.setAttribute("list",data);
//然后再另一个Servlet读取出来
List<String> list = (List<String>) servletContext.getAttribute("list");
System.out.println(list)

ServletConfig对象

每个Servlet都有自己独有的ServletConfig对象,ServletConfig对象专门为他自己的Servlet对象提供初始化服务,不能向ServletContext对象传输数据。

那里面可以传输什么初始化参数呢?

1
2
3
4
5
6
7
8
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
System.out.println(servletConfig.getInitParameter("brand"));

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>servlet3</servlet-name>
<servlet-class>com.bitzh.testServlet.Servlet3</servlet-class>
<init-param>
<!--初始化数据,在Config里面-->
<param-name>brand</param-name>
<param-value>ASUS</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servlet3</servlet-name>
<url-pattern>/servlet3.do</url-pattern>
</servlet-mapping>

url路径匹配规则

精确匹配

精确匹配是指

1
2
3
4
5
//<url-pattern>中配置的值必须与完全精确匹配
<servlet-mapping>
<servlet-name>demoServlet</servlet-name>
<url-pattern>/demo.do</url-pattern>
</servlet-mapping>

为什么加.do因为以后面有过滤器,后面会学到,来分辨请求的是动态资源还是静态资源

扩展名匹配

在url路径中我们有时候用通配符*.do,这个时候通配符表示任意字符,只要扩展名相同额都会被匹配和路径无关

注意在使用了通配符之后,不能在使用“/”了否则会报异常

路径匹配

当然如果写成也是可以的,以a路径为开头,后面是什么都行

1
<url-pattern>/a/*</url-pattern>

任意匹配

但是不包含jsp页面,包含所有路径

1
2
3
<url-pattern>/</url-pattern>
//这种写法包含jsp,但是绝对不这么写
<url-pattern>/*</url-pattern>

优先顺序

当一个url与多个Servlet的匹配规则可以匹配时,则按照 “ 精确路径 > 最长路径 >扩展名”这样的优先级匹配到对应的Servlet。谁精确先走谁。

注解模式开发Servlet

可以在Servlet类外面加@WebServlet()

然后可以点击进去看看有什么属性

里面有urlPatterns可以直接写映射路径,匹配多个路径的时候写个{“/”,“/a/b.do”}

还有loadOnStartup=6,来设置初始化顺序

还可以加初识化initParams={@WebInitParam(name=“brand”,value=“asus”)},这样就可以设置初始参数

请求转发

什么是请求转发呢?

举个例子,比如张三向李四借钱,李四没钱,但是王五有钱,这时候就有两种方案,第一种李四向王五借然后借给张三,这就是请求转发。

1、请求转发 有两种方法 forward inclue

2、响应重定向 这个时候李四告诉张三我也没钱,去王五那借,这时候张三自己去借王五,Servlet处理不好的时候,告诉浏览器找资源。

后台控制页面的两种主要手段。

先说请求转发

请求Servlet1但是Servlet1做不到,转发给Servlet2,直接由Servlet2响应。这时候Servlet1叫源组件,Servlet2叫目标组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bitzh.testServlet;

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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 9:26
* @Description: com.bitzh.testServlet
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet1 service invoked");
//网络上请求的参数
String money = req.getParameter("money");
System.out.println("money:"+money);

//请求转发给另一个servlet或者html或者jsp
//获得一个请求转发器
RequestDispatcher requestDispatcher = req.getRequestDispatcher("servlet2.do");
//由请求转发器发出转发动作
requestDispatcher.forward(req,resp);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bitzh.testServlet;

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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 9:26
* @Description: com.bitzh.testServlet
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet2 service invoked");
}
}

这个时候servlet2.do接收到的是servlet1的req和resp,然后再servlet2里面处理请求做出请求和响应,这个时候servlet2也能接受浏览器请求的参数,因为servlet1的req就是servlet2中的req没换过。

其实就是一种完全的委托,此时servlet1作出的响应就是完全无效的。当servlet1中的resp转换给servlet2的时候,resp会清空一次。

完全托管给目标组件处理。

include转发

如果是include转发会使servlet1转发给servlet2然后servlet2响应完会返回给servlet1之中,再由源组件响应给浏览器。

这时候可以在servlet1之中作出响应也没事,在转发前或者响应候都可以做出响应。

实际开发中常用forword();

总结

请求转发是一种服务器行为,是对浏览器屏蔽的因此浏览器的地址栏不会发生变化,请求的参数是可以从源组件传送给目标组件的,因为请求对象和响应对象没有重新创建而是传给了目标组件,可以请求转发到页面,可以帮助我们实现页面跳转。

通过请求转发,可以转发至WEB-INF受保护的对象资源里

请求转发只能在项目内部找资源不能找外部资源。

常用forword().

响应重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 10:42
* @Description: PACKAGE_NAME
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet3.do")
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet3 service invoked");
String money = req.getParameter("money");
System.out.println("money:"+money);
//响应重定向
resp.sendRedirect("servlet4.do");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 10:42
* @Description: PACKAGE_NAME
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet4.do")
public class Servlet4 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet4 service invoked");
}
}

总结

1、重定向是服务器给浏览器重新指定请求方向 是一种浏览器行为

2、参数在响应重定向并不能传递参数因为req和resp是一个新的不一样的,会再次产生印个新的,不会携带旧的

3、也可以完成页面跳转

4、重定向不能访问WEB-INF的资源

5、可以重定向到外部资源

当在重定向的时候怎么携带参数?

1
2
String money = req.getParameter("money");
resp.getRedirect("servlet4.do?money="+money);

需要手动将参数加上去就能携带参数发送了

路径问题

前端路径问题

image-20240205112848219

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--base标签的作用是在相对路径之前已经补充href里面的内容,如果base不写,那么默认就是文件所在的文件位置-->
<base href="">
</head>
<body>
this is a1.html
<br/>

<!--
相对路径:相对于自己所在的位置为出发点
绝对路径:以固定的位置去定位其他文件

相对路径写法,不以斜线开头,就是相对路径
绝对路径:以斜线开头在页面上/代表从项目的部署目录开始找
页面的绝对路径要有项目名,除非项目没有设置项目名
-->
<a href="a2.html" target="_self">相对路径跳转至a2</a>
<a href="../../b/b2/b1.html" target="_self">相对路径跳转至b1</a>
<br/>
<a href="/testServlet1_war_exploded/a/a2/a2.html" target="_self">相对路径跳转至a2</a>
<a href="/testServlet1_war_exploded/b/b2/b1.html" target="_self">相对路径跳转至b1</a>
</body>
</html>

请求转发路径问题

image-20240205115551103

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.bitzh.test;

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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 11:30
* @Description: com.bitzh.test
* @version: 1.0
*/
//都是从web开始的这个urlpatterns影响相对路径
@WebServlet(urlPatterns = "/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*请求转发路径的写法
* 相对基准路径:相对文件本身urlPattern决定了位置
* 绝对路径的写法:以项目为基准路径(不允许跨项目跨服务,只能在当前项目)
*
*
* */
/*相对路径访问a1*/
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("a/a2/a1.html");
/*绝对路径访问a1*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/a2/a1.html");
requestDispatcher.forward(req,resp);
}
}

响应重定向的路径问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bitzh.test;

import javax.servlet.RequestDispatcher;
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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 11:30
* @Description: com.bitzh.test
* @version: 1.0
*/
//都是从web开始的这个urlpatterns影响相对路径
@WebServlet(urlPatterns = "/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
* 响应重定向到a1.html
* 相对路径和绝对路径
*绝对路径要写项目部署名,除非没有给定项目部署名
*
* */
String contextPath = getServletContext().getContextPath();
resp.sendRedirect(contextPath+"/a/a2/a1.html");
}
}

会话管理概念引入

Cookie对象与HttpSession对象的作用是维护客户端浏览器与服务端的会话状态的两个对象。**由于HTTP协议是一个无状态的协议,所以服务端并不会记录当前客户端浏览器的访问状态,但是在有些时候我们是需要服务端能够记录客户端浏览器的访问状态的**,如获取当前客户端浏览器的访问服务端的次数时就需要会话状态的维持。在Servlet中提供了Cookie对象与HttpSession对象用于维护客户端与服务端的会话状态的维持。二者不同的是Cookie是通过客户端浏览器实现会话的维持,而HttpSession是通过服务端来实现会话状态的维持。

image-20240205150255916

Cookie和Session的引入

image-20240205151118814

Cookie不能跨服务,那个服务发送过来的Cookie就带到那个服务去

Cookie的使用

3.2.1 Cookie对象的特点

Ø Cookie使用字符串存储数据

Ø Cookie使用Key与Value结构存储数据

Ø 单个Cookie存储数据大小限制在4097个字节

Ø Cookie存储的数据中不支持中文,Servlet4.0中支持

Ø Cookie是与域名绑定所以不支持跨一级域名访问

Ø Cookie对象保存在客户端浏览器内存上或系统磁盘中

Ø Cookie分为持久化Cookie(保存在磁盘上)与状态Cookie(保存在内存上)

Ø 浏览器在保存同一域名所返回Cookie的数量是有限的。不同浏览器支持的数量不同,

Chrome浏览器为50个

Ø 浏览器每次请求时都会把与当前访问的域名相关的Cookie在请求中提交到服务端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.bitzh.test;

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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 15:31
* @Description: com.bitzh.test
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过响应对象,向浏览器相应一些Cookie
Cookie c1 = new Cookie("age","10");
c1.setMaxAge(60);//秒
resp.addCookie(c1);
}
}

读取Cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.bitzh.test;

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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 15:46
* @Description: com.bitzh.test
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//读取请求中的Cookie
Cookie[] cookies = req.getCookies();
if(null!=cookies){
for (Cookie cookie : cookies){
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}

}
}

Cookie跨域问题(了解即可)

image-20240205155248030

Cookie记录访问次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.bitzh.test;

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;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 15:56
* @Description: com.bitzh.test
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet3.do")
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如果第一次访问当前servlet,向浏览器响应一个cookie("servlet3","1")
//如果多次访问,就在次数上+1
Cookie[] cookies = req.getCookies();
boolean flag = false;
if(null!=cookies){
for (Cookie cookie : cookies) {
String cookieName = cookie.getName();
if(cookieName.equals("servlet3")){
//创建Cookie
Integer value = Integer.parseInt(cookie.getValue())+1;
Cookie c1 = new Cookie("servlet3",String.valueOf(value));
resp.addCookie(c1);
System.out.println("欢迎第"+value+"访问");
flag = true ;
}
}
}
if (!flag){
System.out.println("欢迎你第一次访问");
Cookie c = new Cookie("servlet3","1");
resp.addCookie(c);
}
}
}

Session的使用

HttpSession

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.bitzh.test;

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 javax.servlet.http.HttpSession;
import java.io.IOException;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 16:25
* @Description: com.bitzh.test
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得HttpSession对象 包存更多数据保存在服务端的技术
//一般保存用户的权限等
/*
* getSession方法执行内容
* 1、从request中尝试获取JSESSIONID的Cookie
* 2、如果获取失败,认为上次回话已经结束,在这里要开启新的会话,创建一个新的HttpSession并返回
* 3、将新的HttpSession对象的JSESSIONID以Cookie的形式放在Response对象,响应给浏览器
*
* 如果获取成功
* 根据JSESSION在服务器 内找对应HttpSession对象
* 1、找到了返回HttpSession
* 2、没找到,创建新的HttpSession并将SESSIONID以Cookie的形式放在Response对象,响应给浏览器
* 吧
* */
HttpSession httpSession = req.getSession();
//向里面存放数据
httpSession.setAttribute("username","oyy");
httpSession.setAttribute("level","A");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.bitzh.test;

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 javax.servlet.http.HttpSession;
import java.io.IOException;

/**
* @Auther: oyy
* @Date: 2024/2/5 - 02 - 05 - 17:32
* @Description: com.bitzh.test
* @version: 1.0
*/
@WebServlet(urlPatterns = "/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
Object username = session.getAttribute("username");
Object level = session.getAttribute("level");
System.out.println("username:"+username);
System.out.println("level:"+level);

}
}

那些情况会结束会话

1、浏览器没有携带JSESSIONID (浏览器关闭)(手动清除)

2、服务器端丢失HttpSession (服务器重启,到达最大不活动时间,手动销毁)

在web.xml中设置

1
2
3
4
5
<session-config>

<session-timeout>1</session-timeout>

</session-config>
1
2
3
4
5
httpSession.invalidate();//手动设置HttpSession删除
HttpSession session = req.getSession();
seesion.getCreationTime();//返回时间的毫秒数
seesion.getLastAccessedTime();//最后一次访问时间
session.getMaxInactiveInterval();//最大不活动时间

案例:通过HttpSession判断用户是否登录

需求:实现登陆一次即可,在一次会话内,可以反复多次访问WEB-INF/welcome.html,如果没有登陆过,跳转到登录页,登陆成功后,可以访问项目结构。

组件介绍:

index.html

登录信息页面

welcome.html

登陆成功之后可以访问的资源

LoginServlet

用来校验登录的,登陆成功将用户信息存户HttpSession,否则回到登录页

WelcomeServlet

用来向welcome.html中跳转的,同时验证登录,登录过,可以直接跳转,否则回到登录页。

User

用来存储一个用户的信息的实体类对象

image-20240206114632163

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.bitzh.pojo;

import java.io.Serializable;

/**
* @Auther: oyy
* @Date: 2024/2/6 - 02 - 06 - 8:51
* @Description: com.bitzh.pojo
* @version: 1.0
*/
public class User implements Serializable {
private String realname;
private String password;
private String username;
private String uid;

@Override
public String toString() {
return "User{" +
"realname='" + realname + '\'' +
", password='" + password + '\'' +
", username='" + username + '\'' +
", uid='" + uid + '\'' +
'}';
}

public User() {
}

public User(String realname, String password, String username, String uid) {
this.realname = realname;
this.password = password;
this.username = username;
this.uid = uid;
}

public String getRealname() {
return realname;
}

public void setRealname(String realname) {
this.realname = realname;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getUsername() {
return username;
}

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

public String getUid() {
return uid;
}

public void setUid(String uid) {
this.uid = uid;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.bitzh.servlet;

import com.bitzh.pojo.User;

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 javax.servlet.http.HttpSession;
import java.io.IOException;

/**
* @Auther: oyy
* @Date: 2024/2/6 - 02 - 06 - 8:46
* @Description: com.bitzh.servlet
* @version: 1.0
*/
@WebServlet(urlPatterns = "/loginServlet.do")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
//如果用户密码为oyy 123
if("oyy".equals(username) && "123".equals(password)){
//登陆成功跳转至welcome.html
//将用户信息放HttpSession中
User user = new User(null,"123","oyy",null);
HttpSession session = req.getSession();
session.setAttribute("user",user);
resp.sendRedirect("mainServlet.do");
System.out.println(username);
}else{
System.out.println(password);
resp.sendRedirect("login.html");

}

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.bitzh.servlet;

import com.bitzh.pojo.User;

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 javax.servlet.http.HttpSession;
import java.io.IOException;

/**
* @Auther: oyy
* @Date: 2024/2/6 - 02 - 06 - 8:42
* @Description: com.bitzh.servlet
* @version: 1.0
*/
@WebServlet(urlPatterns = "/mainServlet.do")
public class MainServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//跳转至welcome.html
//判断有没有登陆,如果登陆过就允许,没有就返回到登录页 看HttpSession有没有登录信息
HttpSession session = req.getSession();
User user = (User) session.getAttribute("user");
if(null!=user){
req.getRequestDispatcher("/WEB-INF/welcome.html").forward(req,resp);
}else{
resp.sendRedirect("login.html");
}
}
}

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
this is the main page
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="get" action="loginServlet.do">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" >
</form>
</body>
</html>

域对象

什么是域对象?

能放数据并存储并传递的区域,那些对象就是域对象。

Servlet三大域对象

Request域 HTTPServletRequest 一次请求/请求转发
Session域 HTTPSession 一次会话(跨请求)
Application域 ServletContext 任意一次请求和会话(跨会话)

setAttribute(name,value); 设置修改数据
getAttribute(name);获得数据的方法
removeAttribute(name);移除数据的方法

JSP四大域对象

Page域
Request域
Session域
Application域

Request域

只有请求转发的时候能携带数据传输

在浏览器输入的数据存到req的域里面然后在servlet中也能继续增加数据,如果增加的数据键相同,那么就会以最后的键为主然后覆盖掉前面那个键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.msb.testRequest;
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.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet(urlPatterns = "/addToRequest.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向request域中添加数据
List<String> x=new ArrayList<>();
Collections.addAll(x, "a","b","c");
req.setAttribute("list", x);
req.setAttribute("gender","boy");
req.setAttribute("gender","girl");
req.setAttribute("name","晓明");
// 请求转发
req.getRequestDispatcher("/readFromRequest.do").forward(req,resp);
// 重定向
//resp.sendRedirect("readFromRequest.do");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.msb.testRequest;
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.util.List;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet(urlPatterns="/readFromRequest.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 移除域中的互数据
req.removeAttribute("gender");
// 从request域中读取数据
List<String> list =(List<String>) req.getAttribute("list");
System.out.println(list);
System.out.println(req.getAttribute("gender"));
System.out.println(req.getAttribute("name"));
//获取Request中的请求参数
System.out.println(req.getParameter("username"));
System.out.println(req.getParameter("password"));
}
}

Session域传递对象

就是将浏览器的数据存放到httpSession中,可以重定向也可以请求转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.msb.testSession;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet(urlPatterns = "/addToSession.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向Session域中添加数据
HttpSession session = req.getSession();
List<String> x=new ArrayList<>();
Collections.addAll(x, "a","b","c");
session.setAttribute("list", x);
session.setAttribute("gender","boy");
session.setAttribute("gender","girl");
session.setAttribute("name","晓明");
// 重定向
resp.sendRedirect("readFromSession.do");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.msb.testSession;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet(urlPatterns="/readFromSession.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
// 移除域中的互数据
//session.removeAttribute("gender");
// 从request域中读取数据
List<String> list =(List<String>) session.getAttribute("list");
System.out.println(list);
System.out.println(session.getAttribute("gender"));
System.out.println(session.getAttribute("name"));
//获取Request中的请求参数
System.out.println(req.getParameter("username"));
System.out.println(req.getParameter("password"));
}
}

如果cookie被删除或者浏览器关闭重启就不能实现了

Application域(应用域)

ServletContext

有效范围

当前web服务内,跨请求,跨会话

生命周期

创建 项目启动

使用 项目运行任何时间有效

销毁 项目关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.msb.testApplication;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet(urlPatterns = "/addToApplication.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向Application域中添加数据
ServletContext application = req.getServletContext();
List<String> x=new ArrayList<>();
Collections.addAll(x, "a","b","c");
application.setAttribute("list", x);
application.setAttribute("gender","girl");
application.setAttribute("name","晓明");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.msb.testApplication;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @Author: Ma HaiYang
* @Description: MircoMessage:Mark_7001
*/
@WebServlet(urlPatterns="/readFromApplication.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext application = this.getServletContext();
// 从application域中读取数据
List<String> list =(List<String>) application.getAttribute("list");
System.out.println(list);
System.out.println(application.getAttribute("gender"));
System.out.println(application.getAttribute("name"));
}
}

JSP

JSP(全称Java Server Pages)是由Sun公司主导创建的一种动态网页技术标准。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet("/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int h = Integer.parseInt(req.getParameter("h"));
int l = Integer.parseInt(req.getParameter("l"));
StringBuilder sbd=new StringBuilder();
sbd.append("<html lang='en'><head><meta charset='UTF-8'><title>Title</title><style>");
sbd.append("table{border: 1px solid green;width: 50%;margin: 0px auto;}");
sbd.append("table td{border: 1px solid blue;}</style></head><body><table>");
for (int i = 1; i <=h ; i++) {
sbd.append("<tr>");
for (int j = 1; j <=l ; j++) {
sbd.append("<td>");
sbd.append(String.valueOf(i));
sbd.append(String.valueOf(j));
sbd.append("</td>");
}
sbd.append("</tr>");
}
sbd.append("</table></body></html>");
// 设置响应内容和编码
resp.setContentType("text/html;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
// 响应内容给浏览器
PrintWriter writer = resp.getWriter();
writer.print(sbd.toString());
}
}

动态资源: 通过运算而生成的资源 Servlet JSP
静态资源: 每次访问获得的都是不需要现生成的资源 HTML img mp3 mp4 js css … …

image-20240207093723315

总结:Servlet作为动态资源,在JAVA代码中通过字符串形式响应数据,通过字符串拼接HTML文档特别繁琐,不利于后期的维护,容易出现问题,如果用于向浏览器响应页面资源操作非常繁琐,且非常不利于页面的更新和维护,所以Servlet不可以作为页面资源,一般专门用接收用户端数据,向用户端响应数据,控制前后端页面跳转,交互逻辑等.在MVC模式下,作为控制层使用

JSP作为资源引入

其实就是在jsp中写java代码让页面和后端分开。

Servlet同样也可以向浏览器动态响应HTML,但是需要大量的字符串拼接处理,在JAVA代码上大量拼接HTML字符串是非常繁琐耗时的一件事,它涉及到HTML本身的字符串处理,还涉及到css样式代码和文件,以及js脚本代码和文件,HTML中的各种外部引入路径等等,处理起来相当的麻烦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<%@ page import="java.io.PrintWriter" %>
<%--
Created by IntelliJ IDEA.
User: Mark70
Date: 2021/1/11
Time: 13:16
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
table{border: 1px solid green;width: 50%;margin: 0px auto;}
table td{border: 1px solid blue;}
</style>
</head>
<body>
<%
int h = Integer.parseInt(request.getParameter("h"));
int l = Integer.parseInt(request.getParameter("l"));
StringBuilder sbd=new StringBuilder();
sbd.append("<table>");
for (int i = 1; i <=h ; i++) {
sbd.append("<tr>");
for (int j = 1; j <=l ; j++) {
sbd.append("<td>");
sbd.append(String.valueOf(i));
sbd.append(String.valueOf(j));
sbd.append("</td>");
}
sbd.append("</tr>");
}
sbd.append("</table>");
out.print(sbd.toString());
%>
</body>
</html>

image-20240207094412438

JSP中如何穿插JAVA代码
在JSP页面上,随机生成一个1-100 的一个分数,然后根据分数显示分数等级
100-90 A 89-80 B 79-70 C 69 -60 D 60- E

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<%@ page import="java.io.PrintWriter" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
ctrl +shift + /
JSP中通过<%%>来穿插JAVA代码
<%=变量/值%>将变量/值打印到页面上的标签显示的位置
--%>
<%
int score =(int)(Math.random()*101);
%>
分数:
<%--
<%
//PrintWriter out = response.getWriter();
out.print(score);
%>
--%>
<%=score%>
<br/>
等级:
<%
int grade =score/10;
switch (grade){
case 10:
case 9:
%>
<%="A"%>
<%
break;
case 8:
%>
<%="B"%>
<%
break;
case 7:
%>
<%="C"%>
<%
break;
case 6:
%>
<%="D"%>
<%
break;
default:
%>
<%="E"%>
<%
}
%>
</body>
</html>

JSP原理

JSP看似是HTML代码,看似是页面,但是事实上是一种后台技术,当我们第一发送请求一个JSP资源时,JSP加载引擎会帮助我们将一个.JSP文件转换成一个.java文件,相当于自动的给我们生成了一个Servlet并将页面上HTML代码编入到这个Servlet中,然后运行这个Servlet,将数据响应给浏览器.JSP的本质其实就是一个Servlet,.JSP中的HTML代码相当于是我们向浏览器响应的HTML内容的模板

JSP执行过程

JSP的执行过程大致可以分为两个时期:转译时期和请求时期

转译时期(Translation Time):

JSP网页转译成Servlet,生成.java文件,然后进行编译生成.class字节码文件

请求时期(Request Time):

运行.class字节码文件,处理请求。

具体过程

1、客户端发出Request请求

2、JSP Container 将JSP转译成Servlet的源代码.java文件

3、将产生的Servlet源代码经过编译后.生成字节码.class文件

4、将.class字节码文件加载进入内存并执行,其实就是在运行一个Servlet

5、通过Response对象将数据响应给浏览器

当我们的项目中有一个test2.JSP文件

image-20240207111440226

当我们第一次请求test2.JSP时,会将JSP文件进行转化,转化成JAVA文件,文件在我们c盘的Tomcat副本中

JSP的继承结构

JSP文件转换成JAVA代码之后,它默认继承了HttpJSPBase,实现了JSPSourceDependent,和JSPSourceImports两个接口,其中HttpJSPBase又继承了HttpServlet ,也就是说,JSP本质上就是一个Servlet

HttpJSPBase代码

HttpJSPBase重写了init,service和destory方法,并且自定义了 jspInit, jspService,_ jspDestory,然后在重写的init方法中调用了_JSPInit,在重写的service方法中调用了_jspService,在重写的destory方法中调用了_jspDestory.

那么我们JSP文件编译成JAVA代码后,继承HttpJspBase重写的方法是jspInit, jspService,_ jspService

image-20240207111603344

通过查看代码我们发现,我们页面上所有HTML相关的代码全部被转化成了字符串,并在_JSPService方法中,通过输出流的形式响应给了浏览器,<%%>中的代码也在该方法中穿插执行.

当JSP网页在执行时,JSP Container 会做检查工作,如果发现JSP网页有更新修改时,JSP Container 才会再次编译JSP成 Servlet; 如果JSP没有更新时,就直接执行前面所产生的Servlet.**,也就是说,当我们在JSP上修改了代码时,不需要频繁的更新和重启项目,直接访问就可以完成更新

JSP加载引擎

查看tomcat web.xml我们发现,这里默认配置了一个JSP的加载引擎—JSPServlet

image-20240207111808959

通过上述代码查看我们发现,请求JSP是都会被JSP加载引擎所匹配,那么该引擎有什么作用?

转译JSP页面:

将JSP页面翻译成一个Servlet,这个Servlet是一个java文件,同时也是一个完整的java程序

编译JSP对应java文件:

JSP引擎调用java编译器对这个Servlet进行编译,得到可执行文件class

请求处理阶段:

JSP引擎调用java虚拟机来解释执行class文件,生成向客户端发送的应答,然后发送给客户端

JSP的性能问题

有人都会认为JSP的执行性能会和Servlet相差很多,其实执行性能上的差别只在第一次的执行。因为JSP在执行第一次后,会被编译成Servlet的类文件,即.class,当再重复调用执行时,就直接执行第一次所产生的Servlet,而不再重新把JSP编译成Servelt。除了第一次的编译会花较久的时间之外,之后JSP和同等功能的Servlet的执行速度就几乎相同了。

JSP慢的原因不仅仅是第一次请求需要进行转译和编译,而是因为JSP作为一种动态资源,本质上就是Servlet,它是需要运行代码才会生成资源,和HTML本身资源已经存在,直接返回,着本质上的差异,另外,JSP转译之后,内部通过大量IO流形式发送页面内容,IO流本身是一种重量级操作,是比较消耗资源的

前后端分离

前后端分离属于软件架构的一种。其核心思想是把前端项目(Node.js实现的)和后端项目独立部署到不同的服务器上,前端项目在通过Ajax请求服务器端项目Restful接口实现数据交互。

使用前后端分离架构的项目在项目组中往往配备前端工程师和后端工程师。后端工程师就是我们,对于我们我们来说,不需要在项目中编写页面了,写到控制器返回数据即可,最后把项目部署到服务器上。而前端项目中主要是一些HTML、JavaScript、图片等资源,如果希望能够独立运行就需要借助基于Node.js的一些前端框架。

image-20240207113206221

2代码组织形式

在传统架构模式中,前后端代码存放于同一个代码库中,甚至是同一工程目录下。页面中还夹杂着后端代码。前后端工程师进行开发时,都必须把整个项目导入到开发工具中。而前后端分离模式在代码组织形式上有以下两种:

半分离
前后端共用一个代码库,但是代码分别存放在两个工程中。后端不关心或很少关心前端元素的输出情况,前端不能独立进行开发和测试,项目中缺乏前后端 交互的测试用例。

分离
前后端代码库分离,前端代码中有可以进行Mock测试(通过构造虚拟测试对 象以简化测试环境的方法)的伪后端,能支持前端的独立开发和测试。而后端代码中除了功能实现外,还有着详细的测试用例,以保证API的可用性,降低 集成风险

image-20240207113255784

3数据接口规范流程

在开发期间前后端共同商定好数据接口的交互形式和数据格式。然后实现前后端的并行开发,其中前端工程师再开发完成之后可以独自进行mock测试,而后端也可以使用接口测试平台进行接口自测,然后前后端一起进行功能联调并校验格式,最终进行自动化测试。

image-20240207113339509

前后端分离常用框架

对于前端工程师来说常用的就是Vue.js和React.js和angularJS。他们是前端工程师常用的三大框架。

Vue.js 小巧,灵活,功能却很强大。在市场上占有率更高,属于成熟稳定的框架。在课程中讲解此框架,后面的项目的前端页面也是基于Vue实现

React相比Vue.js更新一些,近几年有追赶Vue.js的架势。更适合做移动项目。

AngularJS相比Vue更加大量一些。只有在一些大型项目中才可能被使用。

Vue是一个渐进式的JavaScript框架与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,与现代化的工具以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

前后端分离有什么好处

1前后端明确的工作职责

通过将开发团队前后端分离化,让前后端工程师只需要专注于前端或后端的开发工作,使得前后端工程师实现自治,培养其独特的技术特性,然后构建出一个全栈式的精益开发团队。

2提升开发效率

前后端分离以后,可以实现前后端代码的解耦,只要前后端沟通约定好应用所需接口以及接口参数,便可以开始并行开发,无需等待对方的开发工作结束。与此同时,即使需求发生变更,只要接口与数据格式不变,后端开发人员就不需要修改代码,只要前端进行变动即可。如此一来整个应用的开发效率必然会有质的提升。

3完美应对复杂多变的前端需求

如果开发团队能完成前后端分离的转型,打造优秀的前后端团队,开发独立化,让开发人员做到专注专精,开发能力必然会有所提升,能够完美应对各种复杂多变的前端需求。

4增强代码可维护性

前后端分离后,应用的代码不再是前后端混合,只有在运行期才会有调用依赖关系。

JSP的变量和注释问题

在JSP上可以通过<%%> 和<%!%>两种方式书写代码,那么两种方式中书写的java代码在转译之后生成的java文件中的位置是不一样,一个在_JSPService方法中,一个作为类的成员,以定义变量举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>

</head>
<body>
<%--局部变量--%>
<%
int a =10;
%>
<%--成员变量--%>
<%!
int b =10;
%>
</body>
</html>

因为JSP本质就是Servlet,在servlet中我们是不推荐定义一些成员变量的,所以我们也不推荐在JSP中定义局部变量,容易出现线程安全问题

JSP允许在HTML编码中嵌入java代码,那么在JSP上除了HTML中可以简单的注释以外,还有自己的注释方式,在JSP中的注释格式为<%— —%>,不同的注释方式之间时有差异的,接下来我们就对比一下这些差异

image-20240207113813275

JSP使用建议

JSP的使用建议

JSP和Servlet本质上是相同的,JSP页面功能和Servlet后台功能是完全能够互换的,但是JSP的编码风格是在HTML中嵌入少量JAVA代码,它用于显示数据比较方便,如果Servlet上嵌入HTML字符串处理就比较麻烦

Servlet更适合专门编写JAVA代码,JSP更擅长展示数据,Servlet更适合做后台程序,所以在分层上,我们往往将Servlet作为控制层Controller使用,JSP作为视图层view使用,可以让Servlet将数据发送给JSP,然后在JSP上展示数据

image-20240207113908125

JSP指令标签

page指令标签

image-20240207121441017

jsp标签语法

1
<%@ directive   attribute="value" %>

image-20240207121520067

JSP指令的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%--告知浏览器以什么格式和编码解析 响应的数据--%>

<%@ page contentType="text/html;charset=UTF-8" %>

<%--设置JSP页面转换的语言--%>

<%@ page language="java"%>

<%--导包--%>

<%@ page import="com.msb.entity.User" %>

<%--在转换成java代码时使用的编码 一般不用设置--%>

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

<%--指定错误页 当页面发生错误时 指定跳转的页面--%>

<%@ page errorPage="error500.JSP" %>
<%--指定当前页为异常提示页 当前页面可以接收异常对象 --%>

<%@page isErrorPage="true" %>

errorPage是一种处理错误提示页的功能除了JSP有的错误提示页功能 javaEE中自带其他错误提示页处理功能,具体配置如下

在web.xml 配置各种错误的提示页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20



<error-page>

<error-code>500</error-code>

<location>/error500.JSP</location>

</error-page>



<error-page>

<error-code>404</error-code>

<location>/error404.JSP</location>

</error-page>
include指令标签

include就是包含的意思,那有什么用呢

就比如有很多页面但是每个页面都想他的上面有某些东西,就把这种东西放到一个位置,然后用include标签引入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%--静态引入使用的是 include 指令标签

被引入的JSP页面不会生成java代码 被引入的网页和当前页生成代码后形成了一个java文件--%>

<%@include file="head.JSP"%>

<%--动态引入 JSP标签中的 include选项

被引入的JSP页面会生成独立的java代码

在生成的java代码中 使用JSPRuntimeLibrary.include(request, response, "head.JSP", out, false);引入其他页面
--%>

<jsp:include page="head.JSP"/>

image-20240207141536228

taglib标签

taglib指令标签

JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。

Taglib指令的语法:

1
<%@ taglib   uri="uri" prefix="prefixOfTag" %>

JSP内置对象

什么是内置对象

因为JSP的本质是Servlet,在JSP文件经过转译之后,生成JAVA代码,在运行时,JSP给我们准备好了九个可以直接使用而不用我们自己去new的对象,这九个对象我们称之为内置对象.内置对象完全有JSP自行去维护,我们直接使用即可

九大内置对象

image-20240207144715388

四大域对象

pageContext page域 当前页面内可用

request reqeust域 单次请求

session session域 单次会话

application application 域项目运行

响应对象

response 响应对象

输出流对象

out 打印流对象

其他三个对象

servletConfig:由于JSP本身也是一个Servlet,所以容器也会给我们准备一个ServletConfig

page 就是他this对象 当前JSP对象本身

exception 异常对象,在错误提示页上使用,当isErrorpage=true 才具有该对象

九大内置对象的使用

Servlet代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.bitzh.servlet;
import com.bitzh.pojo.User;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;
/**
* @Author:
* @Description: MircoMessage:Mark_7001
*/
@WebServlet("/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向三个域中放入数据
/*向请求域中放入数据*/
User user=new User(1,"李雷","123456");
req.setAttribute("user",user);
req.setAttribute("msg","requestMessage");
/*向session域中放入数据*/
List<User> users =new ArrayList<>();
User user1=new User(1,"韩梅梅","123456");
User user2=new User(1,"小明","123456");
User user3=new User(1,"小红","123456");
Collections.addAll(users,user1,user2,user3);
HttpSession session = req.getSession();
session.setAttribute("users",users);
session.setAttribute("msg","sessionMessage");
/*向application域中放入数据*/
ServletContext application = getServletContext();
Map<String,User> map =new HashMap<>();
map.put("a",user1);
map.put("b",user2);
map.put("c",user3);
application.setAttribute("userMap",map);
application.setAttribute("msg","applicationMessage");
// 跳转至jsp
req.getRequestDispatcher("showInfo.jsp").forward(req,resp);
}
}

JSP代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ page import="com.bitzh.pojo.User" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Map" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--向pageContext域中放数据--%>
<%
pageContext.setAttribute("msg", "pageContextMessage");
pageContext.setAttribute("user", new User(1,"大黄","abcdefg"));
%>
<%--从域中取出数据--%>
pageContext:<br/>
msg:<%=pageContext.getAttribute("msg")%><br/>
username:<%=((User)pageContext.getAttribute("user")).getName()%><br/>
request域中的数据:<br/>
msg:<%=request.getAttribute("msg")%><br/>
username:<%=((User)request.getAttribute("user")).getName()%><br/>
session域中的数据:<br/>
msg:<%=session.getAttribute("msg")%><br/>
username:<%=((List<User>)session.getAttribute("users")).get(0).getName()%><br/>
application域中的数据:<br/>
msg:<%=application.getAttribute("msg")%><br/>
username:<%=((Map<String,User>)application.getAttribute("userMap")).get("a").getName()%><br/>
</body>
</html>

案例

JSP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/2/7
Time: 15:45
To change this template use File | Settings | File Templates.
--%>
<%@ page import="java.util.List" %>
<%@ page import="com.bitzh.pojo.Emp" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style>
table{
border: 3px solid blue;
width: 80%;
margin: 0px auto;
}
td,th{
border: 2px solid green;
}
</style>
</head>
<body>
<table cellspacing="0px" cellpadding="0px">
<tr>
<th>编号</th>
<th>姓名</th>
<th>上级编号</th>
<th>职务</th>
<th>入职日期</th>
<th>薪资</th>
<th>补助</th>
<th>部门号</th>
<th>薪资等级</th>
</tr>
<%
List<Emp> emps = (List<Emp>) request.getAttribute("emps");
for (Emp emp : emps) {
%>
<tr>
<td><%=emp.getEmpno()%></td>
<td><%=emp.getEname()%></td>
<td><%=emp.getMgr()%></td>
<td><%=emp.getJob()%></td>
<td><%=emp.getHiredate()%></td>
<td><%=emp.getSal()%></td>
<td><%=emp.getComm()%></td>
<td><%=emp.getDeptno()%></td>
<td><%--out.print("<td>")--%>
<%
Double sal = emp.getSal();
if(sal<=500){
out.print("A");
}else if( sal <=1000){
out.print("B");
}else if( sal <=1500){
out.print("C");
}else if( sal <=2000){
out.print("D");
}else if( sal <=3000){
out.print("E");
}else if( sal <=4000){
out.print("F");
}else {
out.print("G");
}
%>
</td>
</tr>
<%
}
%>
</table>
</body>
</html>


impl层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.bitzh.dao.impl;

import com.bitzh.dao.EmpDao;
import com.bitzh.pojo.Emp;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
* @Auther: oyy
* @Date: 2024/2/7 - 02 - 07 - 15:49
* @Description: com.bitzh.dao.impl
* @version: 1.0
*/
public class EmpDaoImpl implements EmpDao {
private String url="jdbc:mysql://127.0.0.1:3306/mytestdb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asiz/Shanghai";
private String username="root";
private String password="root";
Connection connection =null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
@Override
public List<Emp> findAll() {
List<Emp> list = new ArrayList<>();
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(url, username, password);
preparedStatement = connection.prepareStatement("select * from emp");
resultSet = preparedStatement.executeQuery();
while(resultSet.next()){
Integer empno=resultSet.getInt("empno");
Integer deptno=resultSet.getInt("deptno");
Integer mgr=resultSet.getInt("mgr");
String ename=resultSet.getString("ename");
String job=resultSet.getString("job");
Double sal=resultSet.getDouble("sal");
Double comm=resultSet.getDouble("comm");
Date hiredate=resultSet.getDate("hiredate");
Emp emp =new Emp( empno, ename, job, mgr, hiredate, sal, comm, deptno);
list.add(emp);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
if(null!=resultSet){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null!=preparedStatement){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(null!=connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
}

Dao层

1
2
3
public interface EmpDao {
List<Emp> findAll();
}

Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet(urlPatterns = "/servlet1.do")
public class Servlet1 extends HttpServlet {

EmpDao empDao = new EmpDaoImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取数据
List<Emp> list = empDao.findAll();
//将数据放到请求域
req.setAttribute("emps",list);
//请求转发至jsp
req.getRequestDispatcher("showEmp.jsp").forward(req,resp);
}
}

EL表达式

Expression Language

EL表达式中定义了一些可以帮助我们快捷从域对象中取出数据的写法,基本语法为

1
${域标志.数据名.属性名(可选)}

四个域标志关键字分别为

requestScope request域

sessionScope session域

applicationScope application域

pageScope page域

当然也可以直接获得浏览器中的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<%@ page import="com.bitzh.pojo.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--向pageContext域中放数据--%>
<%
pageContext.setAttribute("msg", "pageContextMessage");
pageContext.setAttribute("userx", new User(1,"大黄","abcdefg"));
%>
<%--
从域中取出数据
El表达式在获取对象属性值得时候,是通过对象的属性的get方法获取的
保证对象的要获取的属性必须有对应get方法才可以
EL表达式在使用时是不需要import其他类的
El如果获取的是NULL值,是不展示任何信息的
--%>
pageContext域中的数据:<br/>
msg:${pageScope.msg}<br/>
username:${pageScope.userx.name}<br/>
<hr/>
request域中的数据:<br/>
msg:${requestScope.msg}<br/>
username:${requestScope.user.name}<br/>
<hr/>
session域中的数据:<br/>
msg:${sessionScope.msg}<br/>
username:${sessionScope.users[1].name}<br/>
<hr/>
application域中的数据:<br/>
msg:${applicationScope.msg}<br/>
username:${applicationScope.userMap.a.name}<br/>
<hr/>
<%--EL表达式在取出数据的时候是可以省略域标志的
EL表达式会自动依次到四个域中去找数据
--%>
PageContext username:${userx.name}<br/>
Request username:${user.name}<br/>
Session username:${users[1].name}<br/>
Application username:${userMap.a.name}<br/>
<hr/>
<%--
${数据的名字}如果省略域标志,取数据的顺序如下
pageContext
request
session
application
--%>
${msg}
<hr/>
<%--
移除域中的数据
--%>
<%
//pageContext.removeAttribute("msg");// pageContext.removeAttribute()方法会移除四个域中的所有的同名的数据
//request.removeAttribute("msg");
%>
pagecontextMsg:${pageScope.msg}<br/>
requestMsg:${requestScope.msg}<br/>
sessionMsg:${sessionScope.msg}<br/>
applicationMsg:${applicationScope.msg}<br/>
<hr/>
<%--
EL表达式获取请求中的参数
--%>
username:${param.username}<br/>
hobby:${paramValues.hobby[0]}
hobby:${paramValues.hobby[1]}
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
EL表达式对运算符的支持

在EL表达式中, 支持运算符的使用

算数运算符: + - * / %

比较运算符:

== eq equals

> gt greater then

< lt lower then

>= ge greater then or equals

<= le lower then or equals

!= ne not equals

逻辑运算符: || or && and

三目运算符: ${条件 ?表达式1 : 表达式2}

判空运算符: empty
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
+两端如果有字符串,会尝试将字符串转换成数字之后进行加法运算
/如果除以0 结果为Infinity 而不是出现异常
%如果和0取余数,那么会出现异常
--%>
算数运算符:
<hr/>
${10 + 10}<br/>
${"10" + 10}<br/>
${"10" + "10"}<br/>
<%--${"10a" + 10}<br/>--%>
${10/0}<br/>
<%-- ${10%0}<br/>--%>
关系运算符/比较运算符
<%--
比较运算符推荐写成字母形式,不推荐使用 == >= <=
--%>
<hr/>
${10 == 10}<br/>
${10 eq 10}<br/>
${10 gt 8}<br/>
逻辑运算符
<hr/>
${ true || false}<br/>
${ true or false}<br/>
${ true && false}<br/>
${ true and false}<br/>
条件运算符/三目运算符
<hr/>
${(100-1)%3==0?10+1:10-1}<br/>
判断空运算符
<%--empty 为null 则为true--%>
<% //向域中放入数据
pageContext.setAttribute("a",null);
pageContext.setAttribute("b","");
int[] arr ={};
pageContext.setAttribute("arr",arr);
List list =new ArrayList();
pageContext.setAttribute("list",list);
%>
<hr/>
${empty a}<br/>
${empty b}<br/><%--字符串长度为0 则认为是空--%>
${empty arr}<br/><%--数组长度为0 认为不是空--%>
${empty list}<br/><%--集合长度为0 认为是空--%>
${list.size() eq 0}<br/><%--集合长度为0 认为是空--%>
</body>
</html>

JSTL核心标签

认识JSTL

为什么需要学习JSTL

通过之前的案例我们发现,就算在JSP中可以使用EL表达式取出域对象中的数据,但是仍然避免不了还是要在页面中书写一些java代码.这种嵌入JAVA代码的处理比较繁琐,容易出错,且代码不容易维护.

什么是JSTL

JSTL(Java server pages standarded tag library,即JSP标准标签库)是由JCP(Java community Proces)所制定的标准规范,它主要提供给Java Web开发人员一个标准通用的标签库,并由Apache的Jakarta小组来维护。

使用JSTL的好处:

开发人员可以利用JSTL和EL来开发Web程序,取代传统直接在页面上嵌入Java程序的做法,以提高程序的阅读性、维护性和方便性。在jstl中, 提供了多套标签库, 用于方便在jsp中完成或简化相关操作.

JSTL标签库的组成部分

核心标签库: core, 简称c

格式化标签库: format, 简称fmt

函数标签库: function, 简称fn

JSTL的使用前提

1、需要在项目中导入jstl-1.2.jar ,jstl在后台由java代码编写, jsp页面中通过标签进行使用, 使用标签时, 会自动调用后台的java方法,

2、标签和方法之间的映射关系在对应的tld文件中描述. 需要在页面中通过taglib指令引入对应的标签库, uri可以在对应的tld文件中找到

1
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

操作域对象的标签:

向域对象中放入数据 setAttribute

从域对象中取出数据 getAttribute

从域对象中移除数据 removeAttribute

c:set/out/remove标签的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--分别向四个域中放入数据--%>
<%--<%
request.setAttribute("msg", "requestMessage");
%>--%>
<%--
c:set
scope 指定放数据的域 可选值 page request session application
var 数据的名称
value 数据
--%>
<c:set scope="page" var="msg" value="pageMessage"></c:set>
<c:set scope="request" var="msg" value="requestMessage"></c:set>
<c:set scope="session" var="msg" value="sessionMessage"></c:set>
<c:set scope="application" var="msg" value="applicationMessage"></c:set>
<%--移除指定域中的值--%>
<%-- <c:remove var="msg" scope="page"></c:remove>
<c:remove var="msg" scope="request"></c:remove>--%>
<c:remove var="msg" scope="session"></c:remove>
<c:remove var="msg" scope="application"></c:remove>
<%--通过EL表达式取出域中的值--%>
<hr/>
${pageScope.msg}<br/>
${requestScope.msg}<br/>
${sessionScope.msg}<br/>
${applicationScope.msg }<br/>
<hr/>
<%--通过c:out标签获取域中的值--%>
<c:out value="${pageScope.msg}" default="page msg not found"/>
<c:out value="${requestScope.msg}" default="request msg not found"/>
<c:out value="${sessionScope.msg}" default="session msg not found"/>
<c:out value="${applicationScope.msg}" default="application msg not found"/>
</body>
</html>

MVC模式引入

MVC是一种项目架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而得到更好的开发和维护效率。

在MVC模式中,应用程序被划分成了模型(Model)、视图(View)和控制器(Controller)三个部分。其中,模型部分包含了应用程序的业务逻辑和业务数据;视图部分封装了应用程序的输出形式,也就是通常所说的页面或者是界面;而控制器部分负责协调模型和视图,根据用户请求来选择要调用哪个模型来处理业务,以及最终由哪个视图为用户做出应答。

MVC模式的这三个部分的职责非常明确,而且相互分离,因此每个部分都可以独立的改变而不影响其他部分,从而大大提高了应用的灵活性和重用性。

image-20240208102115237

过滤器和监听器

过滤器

image-20240208102343225

Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能 处理编码。

它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

过滤器如何实现功能

1在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。

2在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

3 Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,Web服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,doFilter方法中有一个filterChain对象,用于继续传递给下一个filter,在传递之前我们可以定义过滤请求的功能,在传递之后,我们可以定义过滤响应的功能

可实现的功能:权限设计,敏感词汇拦截

三步使用filter

1、开发后台资源

2、开发filter

3、在web.xml中配置filter拦截那些资源

开发servlet

开发filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.msb.filter;
import javax.servlet.*;
import java.io.IOException;
/**
* @Author:
* @Description:
*/
public class MyFilter implements Filter {
// 初始化方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
// 作出过滤的方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter doFilter 对请求作出过滤");
// 通过一行代码 放行请求
// 放行请求,交给过滤器链继续进行过滤 最后到达资源
filterChain.doFilter(servletRequest, servletResponse);

System.out.println("Filter doFilter 对响应作出过滤");

servletResponse.getWriter().print("filter 追加一些数据");
}
// 销毁方法
@Override
public void destroy() {
}
}

配置filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>myServlet1</servlet-name>
<servlet-class>com.msb.servlet.MyServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet1</servlet-name>
<url-pattern>/myServlet1.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>myServlet2</servlet-name>
<servlet-class>com.msb.servlet.MyServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet2</servlet-name>
<url-pattern>/myServlet2.do</url-pattern>
</servlet-mapping>
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.msb.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<!--对那个/些资源的请求和响应进行过滤-->
<!--<url-pattern>/myServlet1.do</url-pattern>-->
<servlet-name>myServlet1</servlet-name>
<servlet-name>myServlet2</servlet-name>
<!--<url-pattern>/</url-pattern>
<url-pattern>/*</url-pattern>-->
</filter-mapping>
</web-app>

过滤器的生命周期

同servlet对象一样,Filter对象的创建也是交给web服务器完成的,在web服务器创建和使用及最后销毁filter时,会调用filter对应的方法
构造方法:

实例化一个Filter对象的方法

初始化方法:

public void init(FilterConfig filterConfig);

和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

拦截请求方法

public void doFilter

这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。

销毁方法:

public void destroy();

Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

1 WEB 容器启动时,会对Filter进行构造并初始化 一次
2 每次请求目标资源时,都会执行doFilter的方法
3 WEB容器关闭是,会销毁Filter对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.bitzh.filter;

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



/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 10:33
* @Description: com.bitzh.filter
* @version: 1.0
*/
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}

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

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("追加一些数据");
servletResponse.getWriter().print("追加数据");
}
}

过滤器链的使用

在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。

image-20240208144114193

web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

使用过滤器链的好处是我们可以将不同的过滤功能分散到多个过滤器中,分工明确,避免一个过滤器做太多的业务处理,降低了代码的耦合度,这体现了单一职责的设计原则,应用了责任链的代码设计模式.

决定过滤器的执行顺序是由filter-mapping标签决定

首先是1的过滤请求,然后2的过滤请求,然后到了service方法,然后2的过滤响应,1的过滤相应,然后1的销毁,2的销毁

多个Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.bitzh.filter;

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



/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 10:33
* @Description: com.bitzh.filter
* @version: 1.0
*/
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("1初始化");
}

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

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("1过滤请求");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("1追加一些数据");
servletResponse.getWriter().print("1追加数据");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.bitzh.filter;

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

/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 14:47
* @Description: com.bitzh.filter
* @version: 1.0
*/
public class MyFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2 初始化");
}

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

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("2的过滤请求");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("2的过滤响应");

}
}

配置Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">






<filter>
<filter-name>filter1</filter-name>
<filter-class>com.bitzh.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/myServlet.do</url-pattern>
</filter-mapping>
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.bitzh.filter.MyFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/myServlet.do</url-pattern>
</filter-mapping>
</web-app>

过滤器初始化参数配置

同servlet一样,filter也可以通过web.xml进行初始化配置,在初始化时,将参数封装进入FilterConfig并在调用init方法时作为实参传入,我们可以在init方法中获取参数.FilterConfig接口为我们提供了如下功能

1
2
3
4
5
6
7
8
9
10
String getFilterName();//得到filter的名称。


String getInitParameter(String name);//返回定名称的初始化参数的值。如果不存在返回null.


Enumeration getInitParameterNames();//返回过滤器的所有初始化参数的名字的枚举集合。


public ServletContext getServletContext();//返回Servlet上下文对象的引用。

一开始可以再web.xml中配置一开始的filter的初始化参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">






<filter>
<filter-name>filter1</filter-name>
<filter-class>com.bitzh.filter.MyFilter</filter-class>
<init-param>
<param-name>name</param-name>
<param-value>xiaoming</param-value>
</init-param>
<init-param>
<param-name>gender</param-name>
<param-value>nan</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/myServlet.do</url-pattern>
</filter-mapping>
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.bitzh.filter.MyFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/myServlet.do</url-pattern>
</filter-mapping>
</web-app>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.bitzh.filter;

import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;


/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 10:33
* @Description: com.bitzh.filter
* @version: 1.0
*/
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("1初始化");
//这里可以获取初始化的一些参数
String name = filterConfig.getInitParameter("name");
System.out.println(name);
Enumeration<String> pNames = filterConfig.getInitParameterNames();
while (pNames.hasMoreElements()){
String pName = pNames.nextElement();
System.out.println(pName + ":" + filterConfig.getInitParameter(pName));
}
}

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

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("1过滤请求");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("1追加一些数据");
servletResponse.getWriter().print("1追加数据");
}
}

用注解来写过滤器

image-20240208150947127

1
2
3
4
@WebFilter(urlPatterns = "/myServlet1.do" ,servletNames = "myServlet1",initParams = {
@WebInitParam(name = "realname" ,value = "张三"),
@WebInitParam(name = "charset" ,value = "UTF-8")
})

如果是根据注解模式的话,那么顺序就根据filter的名字来控制顺序通常用filter0_功能来为filter,命名

案例开发之POST乱码处理

Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Filter0_EncodingFilter implements Filter {
private String charset;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding(charset);
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
charset = filterConfig.getInitParameter("charset");
}
@Override
public void destroy() {
}
}

配置页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title%sSourceCode%lt;/title>
</head>
<body>
please login ... ... <br/>
<form action="loginController.do" method="post">
用户名:<input type="text" name="user"> <br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>

配置过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.msb.filter.Filter0_EncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
</web-app>

案例开发之登录验证

需求:通过过滤器控制,只有登录过之后可以反复进入welcome.jsp欢迎页,如果没有登录,提示用户进入登录页进行登录操作

项目结构

image-20240208175146418

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.bitzh.controller;

import com.bitzh.pojo.User;

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

/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 17:11
* @Description: com.bitzh.controller
* @version: 1.0
*/
@WebServlet("/login.do")
public class LoginController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取name和pwd
String name = req.getParameter("user");
String pwd = req.getParameter("pwd");
System.out.println(name);
System.out.println(pwd);
//把name和pwd存到httpsession
User user = new User(name,pwd);
req.getSession().setAttribute("user",user);
//然后重定向到登录成功页面
resp.sendRedirect("welcome.jsp");
}
}

Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.bitzh.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 17:34
* @Description: com.bitzh.filter
* @version: 1.0
*/
@WebFilter(urlPatterns = "/*")// 任何资源都要进行过滤,
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)servletRequest;
HttpServletResponse resp=(HttpServletResponse) servletResponse;
//无论是否登录过,都要放行的资源 登录页 登录校验Controller 和一些静态资源
String requestURI = req.getRequestURI();
System.out.println(requestURI);
if(requestURI.contains("login.jsp")|| requestURI.contains("login.do")){
// 直接放行
filterChain.doFilter(req,resp);
// 后续代码不再执行
return;
}
// 需要登录之后才能访问的资源,如果没登录,重定向到login.jsp上,提示用户进行登录
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
if(null != user){// 如果登录过 放行
filterChain.doFilter(req,resp);
}else{// 没有登录过,跳转至登录页
resp.sendRedirect("login.jsp");
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}

User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.bitzh.pojo;

import java.io.Serializable;

/**
* @Auther: oyy
* @Date: 2024/2/8 - 02 - 08 - 17:08
* @Description: pojo
* @version: 1.0
*/
public class User implements Serializable {
private String name;
private String pwd;

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}

public User() {
}

public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
}

JSP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/2/8
Time: 17:19
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
欢迎${user.name}登录
</body>
</html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/2/8
Time: 17:20
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="login.do" method="post">
用户名:<input type="text" name="user"> <br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="提交">
</form>
</body>
</body>
</html>

监听器

什么是监听器

类似于前端的回见绑定,java中的监听器用于监听web应用中某些对象,信息的创建,销毁,增加,修改,删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。

监听器怎么分类

按监听对象划分

1、ServletContext对象监听器

2、HttpSession对象监听器

3、ServletRequest对象监听器

按监听的事件划分

1、对象自身的创建和销毁的监听器

2、对象中属性的创建和消除的监听器

3、session中的某个对象的状态变化的监听器

一共有哪些监听器?分别处理什么事情?

java中一共给我们提供八个监听器接口,分别用于监听三个域对象,每个监听器都有专门监听的事件

Request

1
2
ServletRequestListener   //处理request对象创建和销毁
ServletRequestAttributeListener //处理域对象中的数据添加 替换 删除

Session

1
2
3
4
HttpSessionListener    //处理session对象创建和销毁
HttpSessionAttributeListener //处理session域对象中的数据添加修改删除
HttpSessionBindingListener //处理session对象监听器绑定和解绑定接口
HttpSessionActivationListener //处理session对象钝化和活化状态接口

Application

1
2
ServletContextListener   //处理application对象创建和销毁
ServletContextAttributeListener //处理application域对象中的数据增加 修改 删除

监听器怎么使用

两步使用

1、定义监听器,根据需求实现对应接口

2、在web.xml中配置监听器,让监听器工作

Request域监听器

Request域中共有两个监听器接口,分别是

1、ServletRequestListener

2、ServletRequestAttributeListener

定义监听器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.bitzh.listener;

import javax.servlet.*;
import javax.servlet.annotation.WebListener;

/**
* @Auther: oyy
* @Date: 2024/2/9 - 02 - 09 - 8:45
* @Description: com.bitzh.listener
* @version: 1.0
*/
@WebListener
public class MyRequestListener implements ServletRequestListener , ServletRequestAttributeListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
//监听HttpServletRequest对象的销毁 项目中任何一个request对象的销毁都会触发该方法的执行
ServletRequest servletRequest = sre.getServletRequest();
System.out.println("request销毁了"+servletRequest.hashCode());
}

@Override
public void requestInitialized(ServletRequestEvent sre) {
//监听HttpServletRequest对象的创建和初始化 项目中任何一个request对象的创建和初始化都会触发该方法的执行
ServletRequest servletRequest = sre.getServletRequest();
System.out.println("request创建了"+servletRequest.hashCode());
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
// 任何一个Request对象中调用 setAttribute方法增加了数据都会触发该方法
ServletRequest servletRequest = srae.getServletRequest();
String name = srae.getName();
Object value = srae.getValue();
System.out.println("request"+servletRequest.hashCode()+"对象增加了数据:"+name+"="+value);
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
// 任何一个Request对象中调用 removeAttribute方法移除了数据都会触发该方法
ServletRequest servletRequest = srae.getServletRequest();
String name = srae.getName();
Object value = srae.getValue();
System.out.println("request"+servletRequest.hashCode()+"对象删除了数据:"+name+"="+value);
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
// 任何一个Request对象中调用 setAttribute方法修改了数据都会触发该方法
ServletRequest servletRequest = srae.getServletRequest();
String name = srae.getName();
Object value = srae.getValue();
Object newValue=servletRequest.getAttribute(name);
System.out.println("request"+servletRequest.hashCode()+"对象增修改了数据:"+name+"="+value+"设置为:"+newValue);
}
}

配置web.xml

1
2
3
<listener>
<listener-class>com.bitzh.listener.MyRequestListener</listener-class>
</listener>

Session监听域

Session域共有四个监听接口

HttpSessionListener

HttpSessionAttributeListener

HttpSessionBindingListener

HttpSessionActivationListener

监听器代码

HttpSessionListener

HttpSessionAttributeListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.bitzh.listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
* @Auther: oyy
* @Date: 2024/2/9 - 02 - 09 - 10:08
* @Description: com.bitzh.listener
* @version: 1.0
*/
@WebListener
public class MySessionListener implements HttpSessionListener, HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("任何一个数据添加");
}

@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("任何一个数据删除");
}

@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("任何一个数据修改");
}

@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("任何一个Session对象创建");
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("任何一个Session对象删除");
}
}

HttpSessionBindingListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.bitzh.listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

/**
* @Auther: oyy
* @Date: 2024/2/9 - 02 - 09 - 10:14
* @Description: com.bitzh.listener
* @version: 1.0
*/

/*可以监听具体的某个session对象的事件的
*
* HttpSessionListener 只要在web.xml中配置或者通过@WebListener注解就可以注册监听所有的Session对象
* HttpSessionBindingListener 必须要通过setAttribute方法和某个session对象绑定之后,监听的某个Session对象
* */
public class MySessionBindingListener implements HttpSessionBindingListener {
/*绑定方法*/
/*
在servlet中操作
session.setAttribute("mySessionBindingListener",new MySessionBindingListener())
*/
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("监听器和某个session对象绑定了");
}

@Override
public void valueUnbound(HttpSessionBindingEvent event) {
//解除绑定方法
/*当发生下面情况,会触发该方法的运行
* 1、session.invalidate();让session不可用
* 2、session到达最大不活动时间,session对象回收
* 3、session.removeAttribute("mySessionBindingListener")手动解除绑定
*
* */

}
}

HttpSessionActivationListener

1
2
3
4
5
6
7
8
9
10
public class MySessionActivationListener implements HttpSessionActivationListener {
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("session即将钝化");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("session活化完毕");
}
}

Tomcat会在session一段时间内不被使用时钝化session对象,所谓钝化session,就是把session通过序列化的方法保存到硬盘文件中。当用户再使用session时,Tomcat还会把钝化的对象再活化session,所谓活化就是把硬盘文件中的session在反序列化中放回内存。当session对象被tomcat钝化时,session中存储的对象也被钝化,当session被活化时,也会把session中存储的对象(javabean对象)活化。如果某个类(javabean对象)实现了HttpSessionActiveationListener接口后,当对象随着session被钝化和活化时,监听器接口中的方法就会被调用。钝化时会在tomcat/work/Catalina/localhost/项目/mysession/文件下生成一个后缀为.session的文件,网页中一个被钝化的session就对应一个.session文件(而上面的序列化是一个.ser文件存在所有的session),在活化时此文件也不会消失(不同于上述的.ser文件消失)。当然要看到上述效果,应该先配置tomcat钝化session的参数,在tomcat/conf/cata/oma/localhost目录下

原文链接:https://blog.csdn.net/weixin_38753309/article/details/84454920

Application域监听器

Application域共有两个监听器接口,分别是

ServletContextListener
ServletContextAttributeListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyApplicationListener implements ServletContextListener , ServletContextAttributeListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext创建并初始化了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext销毁了");
}
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("ServletContext增加了数据");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("ServletContext删除了数据");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("ServletContext修改了数据");
}
}

案例开发:记录请求日志

需求:记录每次请求中如下的信息并存储进入日志文件

请求的来源

浏览器所在电脑IP

请求的资源URL

请求发生的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.bitzh.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @Auther: oyy
* @Date: 2024/2/9 - 02 - 09 - 10:45
* @Description: com.bitzh.listener
* @version: 1.0
*/
/*
*
*
* 请求的来源

浏览器所在电脑IP

请求的资源URL

请求发生的时间
* */
@WebListener
public class RequestLogListener implements ServletRequestListener {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void requestDestroyed(ServletRequestEvent sre) {

}

@Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
String remoteHost = req.getRemoteHost();
String url = req.getRequestURL().toString();
String date = simpleDateFormat.format(new Date());
try {
PrintWriter pw = new PrintWriter(new FileOutputStream(new File("d:/bitzh.txt"),true));
pw.println(remoteHost+" "+url+" "+date );
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}

}
}

案例统计实时在线人数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.bitzh.listener;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
* @Auther: oyy
* @Date: 2024/2/9 - 02 - 09 - 11:01
* @Description: com.bitzh.listener
* @version: 1.0
*/
public class OnLineNumberListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
//向application中增加一个

HttpSession session = se.getSession();
ServletContext servletContext = session.getServletContext();
Object attribute = servletContext.getAttribute("count");
if(null==attribute){
servletContext.setAttribute("count",1);
}else{
int count = (int)attribute;
servletContext.setAttribute("count",++count);
}

}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
//向application中减少一个
HttpSession session = se.getSession();
ServletContext servletContext = session.getServletContext();
Object attribute = servletContext.getAttribute("count");
int count = (int) attribute;
servletContext.setAttribute("count",--count);

}
}

然后准备创建的servlet和销毁session的servlet

准备的jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title%sSourceCode%lt;/title>
</head>
<body>
当前在线人数为:${applicationScope.count}
</body>
</html>

项目重启免登录

Session序列化和反序列化

1、序列化与反序列

把对象转化为字节序列的过程称为序列化(保存到硬盘,持久化)

把字节序列转化为对象的过程称为反序列化(存放于内存)

将登录信息放到文件中保存然后启动的时候恢复

2、序列化的用途

把对象的字节序列永久保存到硬盘上,通常放到一个文件中。

把网络传输的对象通过字节序列化,方便传输本节作业

3、实现步骤

要想实现序列化和反序列化需要手动配置

A、新建文件如图所示:

image-20240209112742834

B、 Context.xml中文件如下

1
2
3
4
5
6
7
8
9
10
11
12

<?xml version="1.0" encoding="UTF-8"?>

<Context>

<Manager className="org.apache.catalina.session.PersistentManager">

<Store className="org.apache.catalina.session.FileStore" directory="d:/session"/>

</Manager>

</Context>

C、注意实体类必须实现serializable 接口

1 准备实体类

1
2
3
4
5
6
import java.io.Serializable;

public class User implements Serializable {
private String username;
private String pwd;

2、开发登录信息输入页面

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title%sSourceCode%lt;/title>
</head>
<body>
<form action="loginController.do" method="post">
用户名:<input type="text" name="user"> <br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>

3开发登录信息验证Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet("/loginController.do")
public class LoginController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("user");
String pwd = req.getParameter("pwd");
// user
User user =new User(username,pwd);
// session
HttpSession session = req.getSession();
session.setAttribute("user", user);
}
}

4 开发校验当前是否已经登录的Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet(urlPatterns = "/loginCheckController.do")
public class LoginCheckController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 判断是否登录
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
Object listener = session.getAttribute("listener");// 获得对应的监听器
String message ="";
if(null != user){
message="您已经登录过";
}else{
message="您还未登录";
}
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println(message);
}
}

5测试, 先登录,然后请求loginCheckController.do 校验是否登录过,然后重启项目,再起请求loginCheckController.do 校验是否登录过,发现重启后,仍然是登录过的

6监听钝化和活化

准备监听器

1
2
3
4
5
6
7
8
9
10
public class MySessionActivationListener implements HttpSessionActivationListener, Serializable {
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println(se.getSession().hashCode()+"即将钝化");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println(se.getSession().hashCode()+"已经活化");
}
}

登录时绑定监听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet("/loginController.do")
public class LoginController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("user");
String pwd = req.getParameter("pwd");
// user
User user =new User(username,pwd);
// session
HttpSession session = req.getSession();
session.setAttribute("user", user);
// 绑定监听器
session.setAttribute("listener", new MySessionActivationListener());
}
}

分页

什么是分页?

简单来说:大量数据无法一次性全部显示在网页上?怎么办?只能选取其中的一部分,将大量数据分成好几段,每一段我们用一个网页显示,也就是一页,在页面上我们可以手动控制我们要选择的页面.分页就是将大量数据分成很多页显示的一种处理手段.

分页有什么好处?

   1通过分页,我们不用一次性将所有的数据查出来,只需先查出来一部分,可以减少数据库的IO数据量的传输,降低数据库读写压力,从而提高数据库响应速度

    2页面也不用一次性显示所有的数据,可以减少浏览器和服务器之间大量数据的IO传输,从而提高服务器的响应速度

    3我们可能值需要很多信息中少数的几条,那么传输其他多余的数据就是无形之中对于资源的浪费,分页可以减少资源的浪费

数据库上如何实现分页查询?

select * from student limit 0,5

sql语句通过limit关键字实现数据的分页查询, limit后面可以放两个整数作为参数,前一个参数的意义为从那条数据开始查询,后一个参数的意义是连续取出多少条

如果查询 第n 页,每页x条 数据那么sql语句应该写成Select from student limit (n-1)x,x

分页查询的sql语句代码公式为:SELECT FROM emp LIMIT (页码数-1)页大小,页大小

第一点 : index ,size start =(index-1)*size;

第二点: maxpage = if(total%size==0){maxpage=total/size}else {maxpage=total/size+1}

分页实现的思路

目标效果:

image-20240209140153056

浏览器向后台发送的信息应该是什么?

参数1:要查询的是第几页

参数2:页大小—size

其他参数: 查询条件

服务器向浏览器返回的数据应该是什么?

数据1:当前页的所有信息 List

数据2:当前第几页 currentPage

数据3:信息总条数 totalsize

数据4:总页码数 totalpage

数据5:页大小 size

AJAX

同步和异步

首先用户向HTTP服务器提交一个处理请求。接着服务器端接收到请求后,按照预先编写好的程序中的业务逻辑进行处理,比如和数据库服务器进行数据信息交换。最后,服务器对请求进行响应,将结果返回给客户端,返回一个HTML在浏览器中显示,通常会有CSS样式丰富页面的显示效果。

image-20240209150735103

优点

可以保留浏览器后退按钮的正常功能。在动态更新页面的情况下,用户可以回到前一个页面状态,浏览器能记下历史记录中的静态页面,用户通常都希望单击后退按钮时,就能够取消他们的前一次操作,同步交互可以实现这个需求.

缺点

1同步交互的不足之处,会给用户一种不连贯的体验,当服务器处理请求时,用户只能等待状态,页面中的显示内容只能是空白。

2因为已经跳转到新的页面,原本在页面上的信息无法保存,好多信息需要重新填写

什么是异步交互

指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。异步不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好

image-20240209150807275

优点

1前端用户操作和后台服务器运算可以同时进行,可以充分利用用户操作的间隔时间完成运算

2页面没有跳转,响应回来的数据直接就在原页面上,页面原有信息得以保留

缺点

可能破坏浏览器后退按钮的正常行为。在动态更新页面的情况下,用户无法回到前一个页面状态,这是因为浏览器仅能记录的始终是当前一个的静态页面。用户通常都希望单击后退按钮,就能够取消他们的前一次操作,但是在AJAX这样异步的程序,却无法这样做。

AJAX的最大的特点: 异步访问,局部刷新

AJAX关键技术

使用CSS构建用户界面样式,负责页面排版和美工

使用DOM进行动态显示和交互,对页面进行局部修改

使用XMLHttpRequest异步获取数据

使用JavaScript将所有的元素绑定在一起

AJAX之验证用户名是否被占用

JS表单验证只能校验格式是否正确,但是无法验证用户名是否已经存在,这个就需要后台程序接受到数据后通过查询才能够完成的,那么这里就非常适用于使用异步方式校验,保证用于数据提交后,业务完成的成功率.提升用于体验感

image-20240210112740449

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.bitzh.controller;

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;

/**
* @Auther: oyy
* @Date: 2024/2/10 - 02 - 10 - 11:59
* @Description: com.bitzh.controller
* @version: 1.0
*/
@WebServlet(urlPatterns = "/unameCheckServlet.do")
public class UnameCheckServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uname = req.getParameter("uname");
System.out.println(uname);
String unameInfo = "";
if("oyy".equals(uname)){
unameInfo="该用户名被占用";
}else{
unameInfo="√";
}
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().print(unameInfo);

}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<%--
Created by IntelliJ IDEA.
User: pc
Date: 2024/2/9
Time: 14:18
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<script>
//获取框中内容
var xhr ;
function checkUname(){
var unameDOM = document.getElementById("unameInput");
var unameText = unameDOM.value;
var unameInfo = document.getElementById("unameInfo");
if(null == unameText || unameText== ''){
unameInfo.innerText="用户名不为空";
return;
}
unameInfo.innerText="";

//发送异步请求
//获取XMLHttpRequest对象,该对象可以帮助我们发送异步请求
xhr = new XMLHttpRequest();
//使用xhr对象xhr.open("请求方式","请求的URL",是否开始异步)
xhr.open("GET","unameCheckServlet.do?uname="+unameText,true);
//设置回调函数
xhr.onreadystatechange = showReturnInfo;
//正式发送请求
//因为是get方式请求参数加到url后面如果是post方式请求要用xhr.send方式发送参数
xhr.send(null);

}
function showReturnInfo(){
if(xhr.readyState==4 &&xhr.status==200){
var returnInfo = xhr.responseText;
alert(returnInfo);
}
}
</script>
</head>
<body>
<form action="myServlet.do">
用户名:<input type="text" id="unameInput" name="uname" onblur="checkUname()"><span id="unameInfo" style="color: red"></span><br>
密码:<input type="password" name="pwd"><br/>
<input type="submit">
</form>
</body>
</html>

认识JSON格式

总结:

如果响应的数据是一个对象或者对象集合,数据处理起来会非常麻烦,可以使用JSON格式处理.

JSON(JavaScriptObject Notation, JS 对象简谱) 是一种轻量级的数据交换格式

1轻量级,在这里用它不是为了厉害的功能代码,而是为了实现数据转换

2 Json 格式既能考虑到前端对象的特点 同时也能兼顾后台对象信息的特点

3 Json 格式可以被前端直接识别并解析成对象

4 jQuery形式实现AJAX默认前后端传递数据的格式就是JSON

JSON格式创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>

//{“属性名”:"属性值"}
var person = {"name":"zhangsan","age":10}
alert(person.name)

var persons = [{"name":"zhangsan","age":10},{"name":"lili","age":10}]

for(var i = 0 ; i < persons.length ; i++){
var person = person[i];
cosole.log(person.name)
}
var personStr='{"name":"zhangsan","age":10}'//是一个字符串
//可以直接把上面这种格式的字符串直接转换成对象
var p = JSON.parse(personStr)
</script>


这样要是后台响应给前端,前端就能直接获得了!

JSON格式传递数据

先导入一个jar包叫gson然后利用里面的api就可以将后端创建的对象直接弄成一个json格式的str了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebServlet("/testServlet.do")
public class TestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User user1 =new User("晓明1",10,"男",new Date());
User user2 =new User("晓明2",10,"男",new Date());
User user3 =new User("晓明3",10,"男",new Date());
User user4 =new User("晓明4",10,"男",new Date());
User user5 =new User("晓明5",10,"男",new Date());
ArrayList<User> list =new ArrayList<>();
Collections.addAll(list,user1,user2,user3,user4,user5);
// 响应普通文本数据
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
GsonBuilder gsonBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss");
Gson gson = gsonBuilder.create();
String str = gson.toJson(list);
System.out.println(str);
resp.getWriter().print(str);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title%sSourceCode%lt;/title>
<script>
var xhr ;
function testData(){
xhr =new XMLHttpRequest();
xhr.open("GET","testServlet.do",true);
xhr.onreadystatechange=showReturnInfo;
xhr.send(null);
}
function showReturnInfo(){
if(xhr.readyState==4 && xhr.status==200){
var info =xhr.responseText;
var users=JSON.parse(info)
for (var i = 0; i <users.length ; i++) {
var user =users[i];
console.log(user.uname)
console.log(user.age)
console.log(user.gender)
console.log(user.birthday)
}
}
}
</script>
</head>
<body>
<input type="button" value="测试" onclick="testData()">
</body>
</html>

MyBatis

总之,框架是一个半成品,已经对基础的代码进行了封装并提供相应的API,开发者在使用框架是直接调用封装好的API可以省去很多代码编写,从而提高工作效率和开发速度。

ORM,Object-Relationl Mapping,对象关系映射,它的作用是在关系型数据库和对象之间作一个映射,这样我们在具体的操作数据库的时候,只要像平时操作对象一样操作它就可以了,ORM框架会根据映射完成对数据库的操作,就不需要再去和复杂的SQL语句打交道了。

Mybatis初次使用

初次使用MyBatis

但凡使用框架分三步

1、导入jar文件 maven

2、处理配置文件

3、开发业务代码

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.bitzh</groupId>
<artifactId>mybatisTest01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>
<!--mysqlConnecter-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!--mybatis 核心jar 包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--日志工具包-->

</dependencies>

</project>

然后再recourse中导入配置然后修改一些依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mytestdb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>

然后在recourses中创建对应实现的xml文件来映射,但是xml文件要放在recourses中因为编译的时候idea不会编译xml文件要放在recourses中才可以编译,然后最好是同一类型的包结构下放xml文件

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="aaa">

</mapper>

然后创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bitzh.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
* @Auther: oyy
* @Date: 2024/2/13 - 02 - 13 - 9:23
* @Description: com.bitzh.pojo
* @version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
private Integer deptno;
private String dname;
private String loc;
}

然后创建接口

1
2
3
4
5
6
7
8
9
10
11
package com.bitzh.mapper;

/**
* @Auther: oyy
* @Date: 2024/2/13 - 02 - 13 - 9:21
* @Description: com.bitzh.mapper
* @version: 1.0
*/
public interface DeptMapper {
}

然后测试开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.bitzh.test;

import com.bitzh.pojo.Dept;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
* @Auther: oyy
* @Date: 2024/2/13 - 02 - 13 - 9:31
* @Description: com.bitzh.test
* @version: 1.0
*/
public class Test1 {
@Test
public void testFindAll(){
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory = ssfb.build(resourceAsStream);
SqlSession sqlsession = factory.openSession();

//调用sql语句
List<Dept> list = sqlsession.selectList("findAll");
for (Dept dept : list) {
System.out.println(dept);

}

sqlsession.close();
}
}

MyBatis配置详解

项目中添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
log4j2
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
log4j1
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

在mybatis.cfg.xml中配置MyBatis所使用的具体日志实现。如果不指定将自动搜索。可能会搜到log4j,但是如果优先搜到了其他的日志实现呢,所以还是设置为好。这一来log4j就跑不了了。

log4j 1

将log4j.properties文件负责到src下。另外在其中可以将全局的日志级别调高,避免大量debug信息的干扰。同时将对映射文件的操作调低,可以用来显示SQL语句的调试信息。开发阶段,建议启动控制的日志。

1
2
3
4
5
6
7
8
9
#定义全局日志级别调试阶段推荐debug debug  error warn info debug
log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=d:/bitzh.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

在核心配置文件中可以选择的其他日志处理方式

log4j 2

将log4j2.xml文件负责到resources下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
</Console>
<RollingFile name="RollingFile" filename="log/test.log"
filepattern="${logPath}/%d{YYYYMMddHHmmss}-fargo.log">
<PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n" />
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="20" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>

核心配置文件中可以指定日志打印方式

image-20240213095519171

关于事务配置

image-20240213201943009

映射文件加载方式

image-20240213202028563

类别名

image-20240213202510591

外部属性配置文件存储数据库连接信息

image-20240213203848186

里面新建然后找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8" ?>
<!-- xml文档约束 约束xml文档中可以有哪些标签,哪些属性,以及标签的包含关系和顺序....
dtd 约束
schema 约束
-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"></properties>
<settings>
<!--设置日志处理方式-->
<setting name="logImpl" value="LOG4J"/>
</settings>
<!--设置实体类别名-->
<typeAliases>
<!--
通过包扫描给所有的实体类起别名
给指定报名下的所有类起别名
默认每个实体类的别名是首字母小写的类名
Dept dept
Emp emp
-->
<package name="com.bitzh.pojo"/>
</typeAliases>
<!--配置数据库链接信息-->
<environments default="mysql">
<!--数据源1-->
<environment id="mysql">
<transactionManager type="JDBC"/>
<!--一个数据源-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
</environment>
</environments>
<!--加载映射文件的-->
<mappers>
<mapper resource="com/bitzh/mapper/DeptMapper.xml"/>
</mappers>
</configuration>

SqlSession三种查询方法

Mybatis普通模式分开发

image-20240214100055334

传统开发模式很少用了但是为了后面更加理解先学一下,后期也是同样的操作,就是在实现类里面调用SqlSession对象里的API来实现增删改查的操作

准备Mapper映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="EmpMapper">
<!--
返回单个对象
public Emp findOne();
id 相当于方法名
resultType 相当于返回值类型
!!!sql语句的查询结果用哪个类来进行封装 如果返回值类型是集合,这里写的也是集合中的元素对应的类,不是集合本身作为类型
paramaterType 参数类型
SQL语句就是具体的方法体的实现
-->
<!--返回单个对象-->
<select id="findOne" resultType="emp">
select * from emp where empno = 7499
</select>

<!--
返回多个对象List集合
查询全部的员工信息
public List<Emp> findAll()
-->
<select id="findAll" resultType="emp">
select * from emp
</select>
<!--返回多个对象的Map集合
把查询出来的数据中的某一列作为键,整条数据封装的对象作为值
public Map<key,Emp> findEmpMap()
<empno,Emp>
<key,Emp>
-->
<select id="findEmpMap" resultType="map">
select * from emp
</select>
</mapper>

sqlMapConfig中导入EmpMapper映射文件

1
2
3
4
5
6
<!--加载mapper映射文件-->
<mappers>
<mapper resource="com/msb/mapper/DeptMapper.xml"/>
<mapper resource="com/msb/mapper/EmpMapper.xml"/>
</mappers>

这里现在测试模块中写,到时候在impl中写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.bitzh.test;

import com.bitzh.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @Auther: oyy
* @Date: 2024/2/14 - 02 - 14 - 10:23
* @Description: com.bitzh.test
* @version: 1.0
*/
public class Test2 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testSelectOne(){
// 查询单个对象
System.out.println("sqlSession查询单个对象");
Emp emp = sqlSession.selectOne("findOne");
System.out.println(emp);
}
@Test
public void testSelectList(){
// 查询多个对象的List集合
System.out.println("sqlSession查询对象List集合");
List<Emp> emps = sqlSession.selectList("EmpMapper.findAll");

}
@Test
public void testSelectMap(){
// 查询多个对象的Map集合
System.out.println("sqlSession查询对象Map集合");
Map<Integer, Emp> empMap = sqlSession.selectMap("findEmpMap", "EMPNO");
Set<Integer> empnos = empMap.keySet();
for (Integer empno : empnos) {
System.out.println(empno+" :" +empMap.get(empno));
}
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}

sqlSession中传递参数的三种方式

1 单个基础数据类型作为参数

2 多个基础数据类型的map 集合作为参数

3 引用类型作为参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="EmpMapper2">
<!--
参数为一个基本数据类型
根据员工工号查询员工的全部信息,返回单个员工对象
public Emp findByEmpno(int empno);
parameterType 在有参数情况下也是可以省略不写 mybatis 可以根据实际情况自动判断
如果要写parameterType 那么就要写对
在SQL语句上可以使用${} #{} 代表参数的占位
如果参数是单个基本数据类型,{}中名字可以随便写,见名知意
${} 代表mybatis底层使用Statment语句对象,参数是以字符串拼接的形式设置
#{} 代表mybatis底层使用的preparedStatment语句对象,参数使用?作为占位符处理
#{} 以后常用
-->
<select id="findByEmpno" resultType="emp" parameterType="int">
select * from emp where empno = #{empno}
</select>
<!--
参数为map集合
查询指定部门号和指定最低薪资的员工信息
20 号部门 且工资在1500以上的员工信息
public List<Emp> findEmpByDeptnoAndSal(int deptno,double sal);
< > 最好要进行转译处理,参照HTML转译 w3school在线文档中有转译符号对应规则
Map<String,Object> args=new HashMap<>();
args.put("deptno", 20);
args.put("sal", 1500.0);
#{}中写的是map集合中,参数的键
-->
<select id="findEmpByDeptnoAndSal" resultType="emp" parameterType="map">
select * from emp where deptno = #{deptno} and sal &gt;= #{sal}
</select>
<!--
参数为对象
emp >>> deptno sal
参数是我们自定义的类型,那么 #{}中写的是参数的属性名
-->
<select id="findEmpByDeptnoAndSal2" resultType="emp" parameterType="emp">
select * from emp where deptno = #{deptno} and sal &gt;= #{sal}
</select>
</mapper>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Test3 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testSingleArg(){
// 测试单个基本数据类型作为参数
Emp emp = sqlSession.selectOne("findByEmpno", 7499);
System.out.println(emp);
}
@Test
public void testMapArg(){
// 测试Map集合作为参数
Map<String,Object> args=new HashMap<>();
args.put("deptno", 20);
args.put("sal", 3000.0);
List<Emp> emps = sqlSession.selectList("findEmpByDeptnoAndSal", args);
emps.forEach(System.out::println);
}
@Test
public void testEmpArg(){
// 测试Map集合作为参数
Emp arg =new Emp();
arg.setDeptno(10);
arg.setSal(2000.0);
List<Emp> emps = sqlSession.selectList("findEmpByDeptnoAndSal2", arg);
emps.forEach(System.out::println);
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}

SqlSession实现CRUD完成增删改

提交

Mapper.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="EmpMapper3">
<!--增删改的返回值都是int
public int addEmp(Emp emp){
}
resultType无需指定,没有resultType
-->
<insert id="addEmp" parameterType="emp">
insert into emp values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})

</insert>


</mapper>

sqlMapConfig增加依赖

1
2
3
4
5
6
<mappers>
<mapper resource="com/bitzh/mapper/DeptMapper.xml"/>
<mapper resource="com/bitzh/mapper/EmpMapper.xml"/>
<mapper resource="com/bitzh/mapper/EmpMapper2.xml"/>
<mapper resource="com/bitzh/mapper/EmpMapper3.xml"/>
</mappers>

test文件中增加实现或者用测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.bitzh.test;

import com.bitzh.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @Auther: oyy
* @Date: 2024/2/14 - 02 - 14 - 10:23
* @Description: com.bitzh.test
* @version: 1.0
*/
public class Test4 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testInsert(){
Emp emp = new Emp(null,"oyy","SALESMAN",7839,new Date(),3100.0,200.0,10);
int rows = sqlSession.insert("addEmp", emp);
System.out.println(rows);
//手动提交
sqlSession.commit();
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}

或者在sqlSessionfactory.openSession(true)增加属性true来自动commit

修改**和删除**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.bitzh.test;

import com.bitzh.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @Auther: oyy
* @Date: 2024/2/14 - 02 - 14 - 10:23
* @Description: com.bitzh.test
* @version: 1.0
*/
public class Test4 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testInsert(){
Emp emp = new Emp(null,"oyy","SALESMAN",7839,new Date(),3100.0,200.0,10);
int rows = sqlSession.insert("addEmp", emp);
System.out.println(rows);
//手动提交
sqlSession.commit();
}
@Test
public void testUpdate(){
Emp emp = new Emp();
emp.setEname("xm");
emp.setEmpno(7934);
int rows = sqlSession.update("updateEmp", emp);
System.out.println(rows);
//手动提交
sqlSession.commit();
}
@Test
public void testDelete(){
Emp emp = new Emp();
emp.setEname("xm");
int rows = sqlSession.delete("deleteEmp", emp);
System.out.println(rows);
//手动提交
sqlSession.commit();
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="EmpMapper3">
<!--增删改的返回值都是int
public int addEmp(Emp emp){
}
resultType无需指定,没有resultType
-->
<insert id="addEmp" parameterType="emp">
insert into emp values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})

</insert>
<!--
public int updateEmp(Emp emp){


-->
<update id="updateEmp" parameterType="emp">
update emp set ename = #{ename} where empno = #{empno}
</update>

<!--public int deleteEmp(int empno)-->
<delete id="deleteEmp" parameterType="String">
delete from emp where ename = #{ename}
</delete>
</mapper>

Mybatis代理模式开发

前面已经使用MyBatis完成了对Emp表的CRUD操作,都是由SqlSession调用自身方法发送SQL命令并得到结果的,实现了MyBatis的入门。

但是却存在如下缺点:

  1. 不管是selectList()、selectOne()、selectMap(),都是通过SQLSession对象的API完成增删改查,都只能提供一个查询参数。如果要多个参数,需要封装到JavaBean或者Map中,并不一定永远是一个好办法。

  2. 返回值类型较固定。

  3. 只提供了映射文件,没有提供数据库操作的接口,不利于后期的维护扩展。

在MyBatis中提供了另外一种成为Mapper代理(或称为接口绑定)的操作方式。在实际开发中也使用该方式。下面我们就是要Mapper代理的方式来实现对Emp表的CRUD操作吧,还有完成多个参数传递、模糊查询、自增主键回填等更多的技能实现。搭建好的项目框架如图所示,相比而言,增加了接口EmployeeMapper。但是却会引起映射文件和测试类的变化。

优点:

1有接口 模块之间有规范了

2参数的处理多样了,接口中的方法参数列表由我们自己决定

3通过代理模式由mybatis提供接口的实现类对象 我们不用写实现类了

代理模式浅析

mybatis是如何通过代理模式实现查询的

这条语句的底层使用了动态代理模式,动态创建一个EmployeeMapper的一个代理对象并赋给接口引用。所以在MyBatis中不需要显式提供Mapper接口的实现类,这也是简单的地方。

image-20240214192222668

mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.bitzh.mapper;

import com.bitzh.pojo.Emp;

import java.util.List;

/**
* @Auther: oyy
* @Date: 2024/2/14 - 02 - 14 - 18:54
* @Description: com.bitzh.mapper
* @version: 1.0
*/
public interface EmpMapper {
/**
* 该方法用于查询全部的员工信息
* @return 全部员工信息封装的Emp对象的list集合
*/
List<Emp> findAll();
}

映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bitzh.mapper.EmpMapper">
<!--1、接口的名字和映射的名字必须保持一致,不包含拓展名
2、Mapper映射文件的namespace必须是接口的全路径名
3、sql语句的id必须是对应方法的名
4、DeptMapper映射文件应该和接口编译之后放在同一目录下
-->
<!--public List<Emp> findAll()-->
<select id="findAll" resultType="emp">
select * from emp
</select>
</mapper>

加载映射代码

1
2
3
4
5
<!--加载映射文件的-->
<mappers>
<!--通过类的全路径来找Mapper映射文件-->
<mapper class="com.bitzh.mapper.EmpMapper"/>
</mappers>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package test;

import com.bitzh.mapper.EmpMapper;
import com.bitzh.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @Auther: oyy
* @Date: 2024/2/14 - 02 - 14 - 10:23
* @Description: com.bitzh.test
* @version: 1.0
*/
public class Test2 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testFindAll(){
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> emps = empMapper.findAll();
emps.forEach(System.out::println);
}

@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}

代理模式简析

image-20240215092750503

自动生成的Proxy

代理接口下的参数问题

1单个基本数据类型

2多个基本数据类型

3单个引用数据类型

4map集合数据类型

5多个引用数据类型

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface EmpMapper {
/**
* 该方法用于查询全部的员工信息
* @return 全部员工信息封装的Emp对象的List集合
*/
List<Emp> findAll();
/**
* 根据员工编号查询单个员工信息的方法
* @param empno 员工编号
* @return 如果找到了返回Emp对象,找不到返回null
*/
Emp findByEmpno(int empno);
/**
* 根据员工编号和薪资下限去查询员工信息
* @param empno 员工编号
* @param sal 薪资下限
* @return 多个Emp对象的List集合
*/
List<Emp> findByDeptnoAndSal(@Param("deptno") int deptno,@Param("sal") double sal);
List<Emp> findByDeptnoAndSal2(Map<String,Object> map);
List<Emp> findByDeptnoAndSal3(Emp emp);
List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
}

mapper映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bitzh.mapper.EmpMapper">
<!--
1 接口的名字和Mapper映射为文件名字必须保持一致(不包含拓展名)
2 Mapper映射文件的namespace必须是接口的全路径名
3 sql语句的id必须是对应方法的名
4 DeptMapper映射文件应该和接口编译之后放在同一个目录下
-->
<!--List<Emp> findAll();-->
<select id="findAll" resultType="emp" >
select * from emp
</select>
<!--
单个基本数据类型作为方法参数
#{}中可以随便写,遵循见名知意
Emp findByEmpno(int empno);
-->
<select id="findByEmpno" resultType="emp" >
select * from emp where empno =#{empno}
</select>
<!--
多个基本数据类型作为方法参数
List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
方式1 arg* arg0 arg1 arg2 数字是索引,从0开始
方式2 param* param1 param2 param3 数字是编号,从1开始
使用别名
List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
通过@Param注解使用别名之后,就不能再使用arg* 但是可以继续使用param*
-->
<select id="findByDeptnoAndSal" resultType="emp">
<!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
<!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
<!-- select * from emp where deptno =#{deptno} and sal >= #{sal}-->
</select>
<!--
参数是map,{}写键的名字
-->
<select id="findByDeptnoAndSal2" resultType="emp" parameterType="map" >
<!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
<!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
select * from emp where deptno =#{deptno} and sal >= #{sal}
</select>
<!--单个引用类型,{}中写的使用对象的属性名-->
<select id="findByDeptnoAndSal3" resultType="emp" parameterType="emp" >
select * from emp where deptno =#{deptno} and sal >= #{sal}
</select>
<!--
多个引用类型作为方法参数
List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名
-->
<select id="findByDeptnoAndSal4" resultType="emp" >
<!-- select * from emp where deptno =#{arg0.deptno} and sal >= #{arg1.sal} -->
select * from emp where deptno =#{param1.deptno} and sal >= #{param2.sal}
<!-- select * from emp where deptno =#{empa.deptno} and sal >= #{empb.sal}-->
</select>
</mapper>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Test1 {
public static void main(String[] args) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession(true);
/*
* 帮助我们生成一个接口下的实现类对象的
*
* */
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
List<Emp> emps = mapper.getAllEmp();
for(Emp emp:emps) {
System.out.println(emp);
}
// 1单个基本数据类型作为方法参数
Emp emp = mapper.getByEmpno(7902);
System.out.println(emp);
// 2多个基本数据类型作为方法参数
List<Emp> emps2 = mapper.getByDeptnoAndSal(10, 1500);
for(Emp em:emps2) {
System.out.println(em);
}
// 3单个引用类型作为方法参数
Emp condition=new Emp();
condition.setDeptno(10);
condition.setSal(1500.0);
List<Emp> emps3 = mapper.getByDeptnoAndSal2(condition);
for(Emp em:emps3) {
System.out.println(em);
}
// 4多个引用类型作为方法参数
Emp condition1=new Emp();
condition1.setDeptno(10);
Emp condition2=new Emp();
condition2.setSal(1500.0);
List<Emp> emps4 = mapper.getByDeptnoAndSal3(condition1,condition2);
for(Emp em:emps4) {
System.out.println(em);
}
sqlSession.close();
}
}

Mybatis模糊查询

在进行模糊查询时,在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。

接口

1
2
3
4
5
6
/**
* 根据名字做模糊查询
* @param name 模糊查询的文字
* @return Emp对象List集合
*/
List<Emp> findByEname( String name);

mapper映射文件

1
2
3
4
5
<!--List<Emp> getByName(String name);-->
<select id="findByEname" resultType="emp" >
select * from emp where ename like concat('%',#{name},'%')
</select>

Mybatis主键自增回填

就是在mysql中的主键设定为自增,但是自增之后我们不知道他的id是多少,这时候我们又需要用到它自增后的那个值,所以就需要学一下主键自增回填。

跟之前的操作类似然后再xml中加上这个就能获得自增的值

方式一,会将其返回给deptno

1
2
3
4
5
<mapper namespace="com.bitzh.mapper.DeptMapper">
<insert id="addDept" useGeneratedKeys="true" keyProperty="deptno">
insert into dept values(default,#{dname},#{loc})
</insert>
</mapper>

方式二:

1
2
3
4
5
6
<insert id="addDept2" parameterType="dept">
<selectKey order="AFTER" keyProperty="deptno" resultType="int">
select @@identity
</selectKey>
insert into dept values(null,#{dname},#{loc})
</insert>

实现CRUD查询总结

EmpMapper接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 增加员工信息
* @param emp 存储新增员工信息的Emp对象
* @return 对数据库数据产生影响的行数
*/
int addEmp(Emp emp);
/**
* 根据员工编号修改员工姓名的方法
* @param empno 要修改的员工编号
* @param ename 修改之后的新的员工名字
* @return 对数据库数据产生影响的行数
*/
int updateEnameByEmpno(@Param("empno") int empno,@Param("ename") String ename);
/**
* 根据员工编号删除员工信息
* @param empno 要删除的员工编号
* @return 对数据库数据产生影响的行数
*/
int deleteByEmpno(int empno);

EmpMapper映射 文件

1
2
3
4
5
6
7
8
9
10
11
12
<!--int addEmp(Emp emp);-->
<insert id="addEmp" >
insert into emp values(DEFAULT ,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>
<!--int updateEnameByEmpno(@Param("empno") int empno,@Param("ename") String ename);-->
<update id="updateEnameByEmpno" >
update emp set ename =#{ename} where empno =#{empno}
</update>
<!--int deleteByEmpno(int empno);-->
<update id="deleteByEmpno" >
delete from emp where empno =#{empno}
</update>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Test3 {
private SqlSession sqlSession;
@Before
public void init(){
SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
InputStream resourceAsStream = null;
try {
resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
sqlSession=factory.openSession();
}
@Test
public void testAddEmp(){
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
mapper.addEmp(new Emp(null, "TOM", "SALESMAN", 7521, new Date(), 2314.0, 100.0, 10));
sqlSession.commit();
}
@Test
public void testUpdateEnameByEmpno(){
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
mapper.updateEnameByEmpno(7938, "TOM");
sqlSession.commit();
}
@Test
public void testDeletByEmpno(){
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
mapper.deleteByEmpno(7938);
sqlSession.commit();
}
@After
public void release(){
// 关闭SQLSession
sqlSession.close();
}
}

动态SQL

if标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bitzh.mapper.EmpMapper2">
<!--List<Emp> findByCondition(Emp emp);-->
<select id="findByCondition" resultType="emp">
select * from emp where 1=1
<if test="empno != null">
and empno = #{empno}
</if>
<if test="ename != null and ename != '' ">
and ename = #{ename}
</if>
</select>
</mapper>
用于处理where关键字和and
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<select id="findEmpByCondition" resultType="emp">
select * from emp
<where>
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
<if test="job != null and job != ''">
and job= #{job}
</if>
<if test="mgr != null ">
and mgr= #{mgr}
</if>
<if test="hiredate != null ">
and hiredate= #{hiredate}
</if>
<if test="sal != null">
and sal= #{sal}
</if>
<if test="comm != null ">
and comm =#{comm}
</if>
<if test="deptno != null ">
and deptno= #{deptno}
</if>
</where>
</select>
choose标签

前面的when条件成立 后面的 when就不再判断了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<select id="findEmpByCondition2" resultType="emp">
select * from emp
<where>
<choose>
<when test="empno != null">
and empno= #{empno}
</when>
<when test="ename != null and ename != ''">
and ename= #{ename}
</when>
<when test="job != null and job != ''">
and job= #{job}
</when>
<when test="mgr != null ">
and mgr= #{mgr}
</when>
<when test="hiredate != null ">
and hiredate= #{hiredate}
</when>
<when test="sal != null">
and sal= #{sal}
</when>
<when test="comm != null ">
and comm =#{comm}
</when>
<when test="deptno != null ">
and deptno= #{deptno}
</when>
</choose>
</where>
</select>
set标签

接口

1
int updateEmpByCondition(Emp emp);

映射文件

1
2
3
4
5
6
7
8
9
10
<!--int updateEmpByCondition(Emp emp);-->
<update id="updateEmpByCondition">
update emp
<set>
<if test="ename != null and ename != '' ">
, ename = #{ename}
</if>
</set>
where empno = #{empno}
</update>

测试代码

1
2
3
4
5
6
7
8
9
10
@Test
public void testupdateEmpByCondition(){
EmpMapper2 empMapper2 = sqlSession.getMapper(EmpMapper2.class);
Emp emp = new Emp();
emp.setEmpno(7935);
emp.setEname("oyy1");
int rows = empMapper2.updateEmpByCondition(emp);
System.out.println(rows);
sqlSession.commit();
}
trim标签

trim标签处理set

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<update id="updateEmpByCondition2" >
update emp
<!--prefix 要增加什么前缀
prefixOverrides 要去除什么前缀
suffix 要增加什么后缀
suffixOverrides 要去除什么后缀
set 是trim的一种特殊情况
-->
<trim prefix="set" suffixOverrides="," >
<if test="ename != null and ename != ''">
ename= #{ename},
</if>
<if test="job != null and job != ''">
job= #{job},
</if>
<if test="mgr != null ">
mgr= #{mgr},
</if>
<if test="hiredate != null ">
hiredate= #{hiredate},
</if>
<if test="sal != null">
sal= #{sal},
</if>
<if test="comm != null ">
comm =#{comm},
</if>
<if test="deptno != null ">
deptno= #{deptno},
</if>
</trim>
where empno = #{empno}
</update>

trim标签处理where

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<select id="findEmpByCondition" resultType="emp">
select * from emp
<trim prefix="where" prefixOverrides="and">
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
<if test="job != null and job != ''">
and job= #{job}
</if>
<if test="mgr != null ">
and mgr= #{mgr}
</if>
<if test="hiredate != null ">
and hiredate= #{hiredate}
</if>
<if test="sal != null">
and sal= #{sal}
</if>
<if test="comm != null ">
and comm =#{comm}
</if>
<if test="deptno != null ">
and deptno= #{deptno}
</if>
</trim>
</select>
bind标签

一般用于处理模糊查询的模板

image-20240215222822901

sql标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<sql id="empColumn">empno,ename,job,mgr,hiredate,sal,comm,deptno</sql>
<sql id="baseSelect">select <include refid="empColumn"></include> from emp</sql>
<!--List<Emp> findByCondition(Emp emp);-->
<select id="findByCondition" resultType="emp">
<include refid="baseSelect"></include>
<trim prefix="where" prefixOverrides="and">
<if test="empno != null">
and empno =#{empno}
</if>
<if test="ename != null and ename != ''">
<bind name="likePattern" value="'%'+ename+'%'"/>
and ename like #{likePattern}
</if>
<if test="job != null and job != ''">
and job =#{job}
</if>
<if test="mgr != null">
and mgr =#{mgr}
</if>
<if test="hiredate != null">
and hiredate =#{hiredate}
</if>
<if test="sal != null">
and sal =#{sal}
</if>
<if test="comm != null">
and comm =#{comm}
</if>
<if test="deptno != null">
and deptno =#{deptno}
</if>
</trim>
</select>
foreach标签

image-20240216102334129

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--List<Emp> findByEmpnos1(int[] empnos);
collection="" 遍历的集合或者是数组
参数是数组,collection中名字指定为array
参数是List集合,collection中名字指定为list
separator="" 多个元素取出的时候 用什么文字分隔
open="" 以什么开头
close="" 以什么结尾
item="" 中间变量名
for(Person per:PersonList)
-->
<select id="findByEmpnos1" resultType="emp">
select * from emp where empno in
<foreach collection="array" separator="," open="(" close=")" item="deptno">
#{deptno}
</foreach>
</select>
<!-- List<Emp> findByEmpnos2(List<Integer> empnos);-->
<select id="findByEmpnos2" resultType="emp">
select * from emp where empno in
<foreach collection="list" separator="," open="(" close=")" item="deptno">
#{deptno}
</foreach>
</select>

Mybatis实现多表查询

前面已经使用MyBatis完成了对Emp表的CRUD操作,不管是使用SqlSession直接操作,还是使用Mapper代理方式,都只是完成了对单个数据库表的操作。这肯定是远远不够的。

在实际开发中,经常会将来自多张表的数据在一个位置显示。比如查询并显示的员工信息中会有来自部门表、岗位表的数据,而后台一般是定义一个方法

关联查询

手动处理映射关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<mapper namespace="com.msb.mapper.EmpMapper">
<!--手动处理数据库查询字段和封装实体类属性之间的映射关系
1 主键一般使用id属性不是主键用result
2 当属性名和查询出的数据表字段名相同 可以不写映射关系
-->
<resultMap id="empMap" type="emp">
<!--<id property="empno" column="empno"></id>-->
<result property="name" column="ename"></result>
<!--<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>-->
</resultMap>
<select id="findByEmpno" resultMap="empMap" >
select * from emp where empno =#{empno}
</select>
</mapper>
多表查询

image-20240216110426508

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CREATE TABLE `projects`  (
`pid` int(2) NOT NULL AUTO_INCREMENT,
`pname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`money` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `projects` VALUES (1, ' ***大学OA', 500000);
INSERT INTO `projects` VALUES (2, '学生选课系统', 100000);
INSERT INTO `projects` VALUES (3, '讲师测评系统', 20000);
INSERT INTO `projects` VALUES (4, '线上问答系统 ', 20000);
CREATE TABLE `projectrecord` (
`empno` int(4) NOT NULL,
`pid` int(2) NOT NULL,
PRIMARY KEY (`empno`, `pid`) USING BTREE,
INDEX `fk_project_pro`(`pid`) USING BTREE,
CONSTRAINT `fk_emp_pro` FOREIGN KEY (`empno`) REFERENCES `emp` (`EMPNO`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_project_pro` FOREIGN KEY (`pid`) REFERENCES `projects` (`pid`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `projectrecord` VALUES (7369, 1);
INSERT INTO `projectrecord` VALUES (7521, 1);
INSERT INTO `projectrecord` VALUES (7369, 2);
INSERT INTO `projectrecord` VALUES (7499, 2);
INSERT INTO `projectrecord` VALUES (7521, 2);
INSERT INTO `projectrecord` VALUES (7369, 3);
INSERT INTO `projectrecord` VALUES (7499, 3);
INSERT INTO `projectrecord` VALUES (7521, 3);
INSERT INTO `projectrecord` VALUES (7369, 4);
INSERT INTO `projectrecord` VALUES (7499, 4);
一对一关联查询

需求:根据编号查询员工信息及所在的部门信息

实体类添加一个部门作为属性

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
// 组合一个Dept对象作为自己的属性
private Dept dept;
}

接口

1
2
3
4
5
6
7
8
public interface EmpMapper {
/**
* 根据员工编号查询员工的所有信息并携带所在的部门信息
* @param empno 要查询的员工编号
* @return Emp对象,组合了Dept对象作为属性,对部门信息进行存储
*/
Emp findEmpJoinDeptByEmpno(int empno);
}

映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bitzh.mapper.EmpMapper">
<!--Emp findEmpJoinDeptByEmpno(int empno);-->
<resultMap id="empJoinDept" type="emp">
<!--设置emp本身的八个属性的映射关系-->
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
<!--
association 处理一对一
封装一对一信息关系的标签
property emp类的属性名
javaType 用哪个类的对象给属性赋值
-->
<association property="dept" javaType="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
</association>
</resultMap>
<select id="findEmpJoinDeptByEmpno" resultMap="empJoinDept" >
select * from
emp e
left join dept d
on e.deptno =d.deptno
where empno = #{empno}
</select>
</mapper>

一对多关联查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.msb.mapper.DeptMapper">
<!--Dept findDeptJoinEmpsByDeptno(int deptno);-->
<resultMap id="deptJoinEmps" type="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
<!--处理一对多关系的标签-->
<collection property="empList" ofType="emp" >
<!--设置emp本身的八个属性的映射关系-->
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
</collection>
</resultMap>
<select id="findDeptJoinEmpsByDeptno" resultMap="deptJoinEmps">
select * from dept d left join emp e on d.deptno =e.deptno where d.deptno =#{deptno}
</select>
</mapper>

多对多关联查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.msb.mapper.ProjectMapper">
<!--Project findProjectJoinEmpsByPid(int pid);-->
<resultMap id="projectJoinEmps" type="project">
<id column="pid" property="pid"></id>
<result column="pname" property="pname"></result>
<result column="money" property="money"></result>
<!--一对多 集合属性 collection-->
<collection property="projectRecords" ofType="projectRecord">
<id column="empno" property="empno"></id>
<id column="pid" property="pid"></id>
<!--一对一 -->
<association property="emp" javaType="emp">
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
<result property="hiredate" column="hiredate"></result>
<result property="mgr" column="mgr"></result>
<result property="comm" column="comm"></result>
<result property="deptno" column="deptno"></result>
</association>
</collection>
</resultMap>
<select id="findProjectJoinEmpsByPid" resultMap="projectJoinEmps">
select * from
project p
left join projectrecord pr
on p.pid = pr.pid
left join emp e
on e.empno = pr.empno
where p.pid= #{pid}
</select>
</mapper>

积极加载

不管需不需要都立刻加载出来

功能1:查询所有员工的信息(多对一关联)

经过对比,发现经过在映射文件中配置,测试类的代码大大简化了,无序手动进行关联查询和组装数据了。

功能2:查询10号部门及其该部门员工信息。

Dept和Emp实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
private Integer deptno;
private String dname;
private String loc;
// 当前部门下的所有员工对象的List集合
private List<Emp> empList;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Double sal;
private Double comm;
private Integer deptno;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.bitzh.mapper;
import com.bitzh.pojo.Dept;


public interface DeptMapper {
Dept findDeptByDeptno(int deptno);
}

package com.bitzh.mapper;
import com.bitzh.pojo.Emp;
import java.util.List;

public interface EmpMapper {
List<Emp> findEmpsByDeptno(int deptno);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bitzh.mapper.DeptMapper">
<!--Dept findDeptByDeptno(int deptno);
select="com.bitzh.mapper.EmpMapper.findEmpsByDeptno" 调用的另一个SQL语句
javaType="list" 实体类的属性数据类型
column="deptno" 给另一个SQL语句传入的参数列
jdbcType="INTEGER" 参数对应JDBC的数据类型
fetchType="eager" 加载方式 eager 积极加载 lazy延迟加载-->
<resultMap id="deptJoinEmps" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<collection property="empList"
select="com.bitzh.mapper.EmpMapper.findEmpsByDeptno"
javaType="list"
column="deptno"
jdbcType="INTEGER"
fetchType="eager"
>
</collection>
</resultMap>
<select id="findDeptByDeptno" resultMap="deptJoinEmps">
select * from dept where deptno =#{deptno}
</select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bitzh.mapper.EmpMapper">
<!--List<Emp> findEmpsByDeptno(int deptno);-->
<select id="findEmpsByDeptno" resultType="emp">
select * from emp where deptno =#{deptno}
</select>
</mapper>

懒加载

什么是懒加载

懒加载,即延迟加载(Lazyload)。简单来说就是一个长页面中需要展示很多图像的时候,如果在进入页面的时候一次性把所有图片加载完,需要很长的时间。为了提升用户体验,我们使用懒加载,当图片出现在浏览器可视区域时,才加载图片。例如各种电商页面。

延迟加载,又称按需加载。延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中。

延迟加载的好处:先从单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在时间上的分配更加均匀,而且单表要比关联查询多张表速度要快。

延迟加载的设置

第一步:全局开关:在sqlMapConfig.xml中打开延迟加载的开关。配置完成后所有的association和collection元素都生效

1
2
3
4
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings>

lazyLoadingEnabled:是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态

aggressiveLazyLoading:当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载,

第二步:分开关:指定的association和collection元素中配置fetchType属性。eager:表示立刻加载;lazy:表示延迟加载。将覆盖全局延迟设置。

使用注解实现CRUD

一些简单的查询可以直接使用注解

1
2
3
4
5
6
7
8
9
10
11
public interface DeptMapper {
Dept findDeptByDeptno(int deptno);
@Select("select * from dept where deptno =#{deptno}")
Dept findByDeptno(int deptno);
@Update("update dept set dname =#{dname}, loc =#{loc} where deptno =#{deptno}")
int updateDept(Dept dept);
@Insert("insert into dept values(DEFAULT,#{dname},#{loc})")
int addDept(Dept dept);
@Delete("delete from dept where deptno =#{deptno}")
int removeDept(int deptno);
}

1.使用注解没有实现Java代码和SQL语句的解耦

2.无法实现SQL语句的动态拼接

3.进行多表的查询时定制ResultMap比较麻烦

image-20240216195317480

是一种临时存储少量数据至内存或者是磁盘的一种技术.减少数据的加载次数,可以降低工作量,提高程序响应速度

缓存的重要性是不言而喻的。mybatis的缓存将相同查询条件的SQL语句执行一遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询SQL时候不在执行SQL与数据库交互,而是直接从缓存中获取结果,减少服务器的压力;尤其是在查询越多、缓存命中率越高的情况下,使用缓存对性能的提高更明显。

MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能。MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。

一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。

一级缓存

一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。

缓存中的数据使用键值对形式存储数据

namespace+sqlid+args+offset>>> hash值作为键,查询出的结果作为值

image-20240216202718466

二级缓存

1) 全局开关:在sqlMapConfig.xml文件中的标签配置开启二级缓存

2) ```xml

<settings>

    <setting name="cacheEnabled" value="true"/>

</settings>
1
2
3
4
5
6
7
8
9
10

cacheEnabled的默认值就是true,所以这步的设置可以省略。

2) 分开关:在要开启二级缓存的mapper文件中开启缓存:

```xml
<mapper namespace="com.bitzh.mapper.EmployeeMapper">

<cache/>
</mapper>

在写测试类的时候,增删改之后需要提交才会出现二级缓存

1
<select id="findByEmpno" resultType="emp" useCache="true" flushCache="true">//这里开启二级缓存开关

在使用二级缓存的时候要将实体类实现序列化接口

三方缓存

分布式缓存框架:我们系统为了提高系统并发和性能,一般对系统进行分布式部署(集群部署方式)不适用分布缓存, 缓存的数据在各个服务单独存储,不方便系统开发。所以要使用分布式缓存对缓存数据进行集中管理.ehcache,redis ,memcache缓存框架。

Ehcache:是一种广泛使用的开源java分布式缓存。主要面向通用缓存,javaEE 和 轻量级容器。它具有内存和磁盘存储功能。被用于大型复杂分布式web application的

这里的三方缓存是作为二级缓存使用的

导入依赖的jar文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>

去各自的sql映射文件里,开启二级缓存,并把缓存类型指定为EhcacheCache

在资源目录下放置一个缓存配置文件,文件名为: ehcache.xml 内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<diskStore path="D:\msb\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!-- Cache配置
· name:Cache的唯一标识
· maxElementsInMemory:内存中最大缓存对象数。
· maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大。
· eternal:Element是否永久有效,一但设置了,timeout将不起作用。
· overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。
· timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
· timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。
· diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
· diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
· memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 -->

逆向工程(了解)

MyBatis的一个主要的特点就是需要程序员自己编写SQL,那么如果表太多的话,难免会很麻烦,所以MyBatis官方提供了一个逆向工程,可以针对单表自动生成MyBatis执行所需要的代码(包括mapper.xml,mapper.java,pojo)。一般在开发中,常用的逆向工程方式是通过数据库的表生成代码。

在pom.xml中导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>

<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 日志包,方便查看执行信息-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<!-- 代码生成工具jar -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>

配置逆向工程配置文件 在resources目录下放置一个名为generatorConfig.xml的配置文件,文件内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
password="123">
</jdbcConnection> -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"
userId="root"
password="root">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.msb.pojo"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.msb.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.msb.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->

<table tableName="dept" domainObjectName="Dept"
enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false" >
<columnOverride column="id" javaType="Integer" />
</table>

</context>
</generatorConfiguration>

在resources目录下放置一个名为log4j.properties的配置文件,文件内容如下

1
2
3
4
5
6
7
8
9
log4j.rootLogger=debug,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=d:/msb.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n

运行逆向工程代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class GeneratorSqlmap {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("D:\\ideaProjects\\reverse\\target\\classes\\generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}

mybatis配置目录问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
<!--告诉maven将项目源码中的xml文件也进行编译,并放到编译目录中-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>

这样就可以将配置文件直接放在java文件目录里面了

文章作者: oyy0v0
文章链接: https://oyy0v0.top/2024/07/13/2024-7-13%20javaWeb/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 oyy0v0😼

评论
公告
欢迎来来到我的博客
目录
  1. javaWeb第一版
    1. 安装tomcat
    2. 利用集成开发环境操作Tomcat
    3. Servlet
      1. Servlet生命周期
      2. ServletConfig
      3. Servlet的层次接口
      4. GenericServlet
    4. JSP
      1. JSP内置对象9个
      2. Http请求状态码
      3. response常用方法
    5. Session
    6. Cookie
      1. Cookie 和Session的区别
    7. JSP内置对象的作用域
    8. EL表达式
    9. JSTL
    10. 过滤器
      1. filter的生命周期
      2. 文件的上传下载
      3. 文件下载
    11. Ajax
    12. 传统的WEB数据交互 VS AJAX数据交互
    13. 基于jQuery的AJAX语法
    14. JSON
    15. JDBC
      1. jdbc体系结构
      2. JDBC的使用
      3. 数据库连接池
      4. 数据库连接池实现
      5. DBUtiles
  2. javaWeb第二版(和第一版有重复)
    1. JavaWeb
      1. JavaEE
      2. Tomcat安装和应用
      3. Tomcat的结构认识
      4. Tomcat部署项目
      5. Tomcat部署文件配置
      6. Tomcat主要组件
      7. Http协议
        1. Http协议
        2. HTTP请求
          1. 请求行
          2. 请求头
          3. 请求体
        3. HTTP响应
      8. JAVAWEB项目部署
      9. 使用idea开发JAVAWEB项目
        1. 第一种 Idea部署JAVAWEB项目并运行的方式(掌握)
      10. Servlet
        1. Servlet开发流程
        2. 了解HttpServletRequest对应的方法
          1. 获取请求行信息
          2. 获取请求头信息
          3. 获取请求数据
        3. HttpServletResponse对象
        4. 关于乱码问题
        5. Servlet继承结构
        6. Servlet生命周期
        7. ServletContext对象和作用
        8. ServletContext对象的使用
        9. ServletConfig对象
        10. url路径匹配规则
        11. 注解模式开发Servlet
      11. 请求转发
      12. 响应重定向
      13. 路径问题
        1. 前端路径问题
        2. 请求转发路径问题
        3. 响应重定向的路径问题
      14. 会话管理概念引入
        1. Cookie和Session的引入
        2. Cookie的使用
        3. Session的使用
        4. 案例:通过HttpSession判断用户是否登录
      15. 域对象
        1. Request域
        2. Session域传递对象
        3. Application域(应用域)
      16. JSP
        1. JSP作为资源引入
        2. JSP原理
        3. 前后端分离
        4. JSP的变量和注释问题
        5. JSP使用建议
        6. JSP指令标签
          1. page指令标签
          2. include指令标签
          3. taglib标签
        7. JSP内置对象
          1. 九大内置对象
          2. 九大内置对象的使用
        8. EL表达式
        9. JSTL核心标签
      17. MVC模式引入
      18. 过滤器和监听器
        1. 过滤器
        2. 三步使用filter
        3. 过滤器的生命周期
        4. 过滤器链的使用
        5. 过滤器初始化参数配置
        6. 用注解来写过滤器
        7. 案例开发之POST乱码处理
        8. 案例开发之登录验证
        9. 监听器
        10. Request域监听器
        11. Session监听域
        12. Application域监听器
        13. 案例开发:记录请求日志
        14. 案例统计实时在线人数
        15. 项目重启免登录
      19. 分页
        1. 分页实现的思路
      20. AJAX
        1. 同步和异步
        2. AJAX关键技术
        3. AJAX之验证用户名是否被占用
        4. 认识JSON格式
      21. MyBatis
      22. Mybatis初次使用
        1. 初次使用MyBatis
        2. MyBatis配置详解
        3. 关于事务配置
        4. 映射文件加载方式
        5. 类别名
        6. 外部属性配置文件存储数据库连接信息
        7. SqlSession三种查询方法
        8. sqlSession中传递参数的三种方式
        9. SqlSession实现CRUD完成增删改
        10. Mybatis代理模式开发
        11. 代理接口下的参数问题
        12. Mybatis模糊查询
        13. Mybatis主键自增回填
        14. 实现CRUD查询总结
        15. 动态SQL
          1. if标签
          2. 用于处理where关键字和and
          3. choose标签
          4. set标签
          5. trim标签
          6. bind标签
          7. sql标签
          8. foreach标签
      23. Mybatis实现多表查询
        1. 关联查询
          1. 手动处理映射关系
          2. 多表查询
            1. 一对一关联查询
            2. 一对多关联查询
        2. 积极加载
        3. 懒加载
        4. 使用注解实现CRUD
        5. 一级缓存
        6. 二级缓存
        7. 三方缓存
        8. 逆向工程(了解)
        9. mybatis配置目录问题