javaWeb第一版
为什么要有Tomcat
那我们这个应用怎么会被客户端访问到呢?就需要用到Web应用服务器了。
因为我们有Web应用服务器,然后程序在这个服务里面所以就可以被客户端访问,就像数据库一样。
而我们说的Tomcat就是Web应用服务器。
当然还有Jboos,Jetty。。。
我们把写好的程序放进去,其他人就能通过ip地址端口号来访问到我们的java程序。
当然要在一个局域网里面。
安装tomcat 1、自己去官网下载安装包解压
2、然后解压之后的文件夹
bin :存放各个平台下启动和停止Tomcat服务的脚本文件
conf: 存放各种Tomcat服务器的配置文件比如端口号
lib:里面放的就是jar文件(一些工具包)
logs:存放日志,记录服务运行的情况。
temp:存放运行时的临时文件。文件的上传和下载。
webapps: 存放允许客户端访问的资源。(我们的java程序)
work:存放Tomcat将JSP转换之后的Servlet文件。
利用集成开发环境操作Tomcat
配置就不讲了
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里面加映射
这样就可以请求后端的服务,那怎么实现对客户端的响应呢?
这两个对象一个接受请求信息,一个用来响应信息。
我们直接对对象来进行操作
当然还有另一种配置方式,这样就能简化开发
基于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;@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中分出来的,对于请求进一步细化
GenericServlet 这里是干什么的呢?
这个是屏蔽其他四个不需要用的方法,然后分发doGet和doPost方法。
JSP JSP本质上就是一个Servlet,JSP主要是负责与用户交互,将最终的界面呈现给用户,HTML+JS+CSS+Java的混合文件。
只有Servlet的时候要返回一个页面
很麻烦所以就来了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内置对象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)
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请求的出处(不知道来自于那个终端)他只会接收到一个请求信号,所以说就存在一个问题:有可能将用户的响应发送给其他人,必须有一种技术来让服务器知道请求来自于哪里,这就是会话技术。
会话:就是浏览器或者客户端和服务器之间发生的一系列连续的请求和响应的过程,打开浏览器进行操作到关闭浏览器的过程。
会话状态:指服务器和浏览器在会话过程中产生的状态信息,借助于会话的状态,服务器能够把属于同一次会话的请求和响应关联起来。
实现会话有两种方式:
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 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
Cookie 和Session的区别 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表达式还能赋值。
绑定的是方法,不是绑定的属性。
数据级联:
${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" %>
JSTL的优点:
1、提供了统一的标签
2、可以用于编写各种动态功能
常用标签:
set,out,remove,catch
set: 向域对象中添加数据
1 2 3 <% <c:set var ="name" value="tome" scope="requet" ></c:set> %>
默认存放顺序是从小到大
可以用scope来指定存放域
但是这个标签只能对简单的数字,字符串进行操作,不能操作对象。
out:输出域对象中的数据
1 2 <c:set var="name" value="tom"></c:set> <c:out value="${name}" default="未定义"></c:out>
remove标签
catch标签:
条件标签:if 、choose
迭代标签:
在迭代标签里面有begin \stop \step,起始位置,结束位置,步长
下标从0开始
还有varStatus来给序号
格式化标签库常用的标签:
函数标签库
过滤器 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、控制资源的访问权限
文件的上传下载
fileupload组件
这个组件可以将所有的请求信息都解析成FileIte对象,可以通过对FileItem对象操作完成上传,面向对象的思想。
文件下载
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;@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 (){ var btn = $("#btn" ); btn.click(function (){ $.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;@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); } }
Servlet这边不能跳转到jsp,只能将数据返回,不然的话返回的内容就是在再多写了一次页面
我们只需要把数据返回给前端。
传统的WEB数据交互 VS AJAX数据交互 客户端请求的方式不同:
传统,浏览器发送的是同步请求(form,a标签)
AJAX,异步引擎对象发送异步请求
服务器响应的方式不同:
传统,响应一个完整JSP页面(视图—)
AJAX,响应需要的数据
传统:需要等待服务器完成响应并且重新加载整个页面之后才能继续后续的操作
基于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数据库存取和操作的公共接口。
定义了一组标准,为访问不同数据库提供了同一路径
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
C3P0拿到的Connection
6、其他的跟原来的一样
7、可以设置初始化连接个数
8、设置最大连接数
9、当连接对象不够时,再次申请连接对象个数
10、设置最小连接数(设置空闲的最小下线限)
假设一开始有20个用了十八个然后还剩两个这时候就该去申请了
一般我们把配置文件写在xml配置文件中
因为如果是java文件修改的话还需要重新编译但是xml文件不用重新编译
然后在创建c3p0的时候把xml的name的属性值传入到构造器中即可完成数据库连接池的初始化
放在src的根目录就行同级。
DBUtiles 这个工具
在查询的时候比较麻烦
DBUtiles可以帮助开发者完成数据的封装(结果集到java的映射)
1、导入jar包
ResultHandler接口用来处理结果集,可以将查询到的结果集转换成java对象,提供了四种实现类
BeanHandler 将结果集映射成Java对象
BeanListHandler 将结果集映射成List集合List
MapHandler 转换成Map集合
MapListHandler 转换成MapList集合
2、
javaWeb第二版(和第一版有重复) JavaWeb 这个阶段会比较难,重点是理解和记忆,多写代码,没别的方法了熬过去就好,下面的内容不会很详细的写了,重要的是脑子记住
JavaEE javaee平台规范了在开发企业级web应用中的技术标准
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连接器
这里的port是监听的端口,什么是监听后面会说
protocol是连接器使用的协议
redirectPort就是客户端发来消息的时候就会把消息发到8443这个端口号这里
Enigine组件
Engine是Service的一个实例,就是servlet的引擎
Host组件
位于Engine容器中用于接受请求并进行相应处理的虚拟主机。
Context组件
Context是Host的子标签,代表指定的一个Web应用,每一个web应用都是war包或者文件目录,里面会写Web应用的存放位置
Tomcat处理请求的过程
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服务器自己接受什么类型,
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 项目配置文件
使用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对应的方法
获取请求行信息 1 2 3 4 5 req.getRequestURL(); req.getRequestURI(); req.getRemoteAddr(); req.getLocalAddr(); req.getLocalPort();
获取请求头信息 1 2 3 4 req.getHeader("headerKey" ); String headerValue = req.getHeader("headerKey" );req.getHeaderNames(); 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;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" ); String str = req.getParameter("key" );
获取复选框(checkbox组件)中的值
1 2 req.getParameterValues("checkboxkey" ); String keys = req.getParameterValues("checkboxkey" );
获取所有提交数据的key
1 2 req.getParameterNames(); Enumeration <String> parameterNames = req.getParameterNames();
获取Map结构获取提交数据
1 2 req.getParameterMap(); 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" ); 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继承结构
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()方法即可
在我们自定义的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都共享这个对象。所以叫全局应用程序共享对象。
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(); servletContext.getMinorVersion();
1 2 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); 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 > <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;@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); 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;@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;@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;@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);
需要手动将参数加上去就能携带参数发送了
路径问题 前端路径问题
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 ="" > </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 >
请求转发路径问题
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;@WebServlet(urlPatterns = "/servlet1.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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;@WebServlet(urlPatterns = "/servlet1.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String contextPath = getServletContext().getContextPath(); resp.sendRedirect(contextPath+"/a/a2/a1.html" ); } }
会话管理概念引入 Cookie对象与HttpSession对象的作用是维护客户端浏览器与服务端的会话状态的两个对象。**由于HTTP协议是一个无状态的协议,所以服务端并不会记录当前客户端浏览器的访问状态,但是在有些时候我们是需要服务端能够记录客户端浏览器的访问状态的**,如获取当前客户端浏览器的访问服务端的次数时就需要会话状态的维持。在Servlet中提供了Cookie对象与HttpSession对象用于维护客户端与服务端的会话状态的维持。二者不同的是Cookie是通过客户端浏览器实现会话的维持,而HttpSession是通过服务端来实现会话状态的维持。
Cookie和Session的引入
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;@WebServlet(urlPatterns = "/servlet1.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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;@WebServlet(urlPatterns = "/servlet2.do") public class Servlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); if (null !=cookies){ for (Cookie cookie : cookies){ System.out.println(cookie.getName()+":" +cookie.getValue()); } } } }
Cookie跨域问题 (了解即可)
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;@WebServlet(urlPatterns = "/servlet3.do") public class Servlet3 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie[] cookies = req.getCookies(); boolean flag = false ; if (null !=cookies){ for (Cookie cookie : cookies) { String cookieName = cookie.getName(); if (cookieName.equals("servlet3" )){ 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;@WebServlet(urlPatterns = "/servlet1.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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;@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 session = req.getSession();seesion.getCreationTime(); seesion.getLastAccessedTime(); session.getMaxInactiveInterval();
案例:通过HttpSession判断用户是否登录 需求:实现登陆一次即可,在一次会话内,可以反复多次访问WEB-INF/welcome.html,如果没有登陆过,跳转到登录页,登陆成功后,可以访问项目结构。
组件介绍:
index.html
登录信息页面
welcome.html
登陆成功之后可以访问的资源
LoginServlet
用来校验登录的,登陆成功将用户信息存户HttpSession,否则回到登录页
WelcomeServlet
用来向welcome.html中跳转的,同时验证登录,登录过,可以直接跳转,否则回到登录页。
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 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;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;@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" ); if ("oyy" .equals(username) && "123" .equals(password)){ 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;@WebServlet(urlPatterns = "/mainServlet.do") public class MainServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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;@WebServlet(urlPatterns = "/addToRequest.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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); } }
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;@WebServlet(urlPatterns="/readFromRequest.do") public class Servlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.removeAttribute("gender" ); List<String> list =(List<String>) req.getAttribute("list" ); System.out.println(list); System.out.println(req.getAttribute("gender" )); System.out.println(req.getAttribute("name" )); 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;@WebServlet(urlPatterns = "/addToSession.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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;@WebServlet(urlPatterns="/readFromSession.do") public class Servlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); List<String> list =(List<String>) session.getAttribute("list" ); System.out.println(list); System.out.println(session.getAttribute("gender" )); System.out.println(session.getAttribute("name" )); 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;@WebServlet(urlPatterns = "/addToApplication.do") public class Servlet1 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 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;@WebServlet(urlPatterns="/readFromApplication.do") public class Servlet2 extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext application = this .getServletContext(); 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;@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 … …
总结: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>
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 ); %> 分数: <%-- <% 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文件
当我们第一次请求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
通过查看代码我们发现,我们页面上所有HTML相关的代码全部被转化成了字符串,并在_JSPService方法中,通过输出流的形式响应给了浏览器,<%%>中的代码也在该方法中穿插执行.
当JSP网页在执行时,JSP Container 会做检查工作,如果发现JSP网页有更新修改时,JSP Container 才会再次编译JSP成 Servlet; 如果JSP没有更新时,就直接执行前面所产生的Servlet.**,也就是说,当我们在JSP上修改了代码时,不需要频繁的更新和重启项目,直接访问就可以完成更新
JSP加载引擎
查看tomcat web.xml我们发现,这里默认配置了一个JSP的加载引擎—JSPServlet
通过上述代码查看我们发现,请求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的一些前端框架。
2代码组织形式
在传统架构模式中,前后端代码存放于同一个代码库中,甚至是同一工程目录下。页面中还夹杂着后端代码。前后端工程师进行开发时,都必须把整个项目导入到开发工具中。而前后端分离模式在代码组织形式上有以下两种:
半分离 前后端共用一个代码库,但是代码分别存放在两个工程中。后端不关心或很少关心前端元素的输出情况,前端不能独立进行开发和测试,项目中缺乏前后端 交互的测试用例。
分离 前后端代码库分离,前端代码中有可以进行Mock测试(通过构造虚拟测试对 象以简化测试环境的方法)的伪后端,能支持前端的独立开发和测试。而后端代码中除了功能实现外,还有着详细的测试用例,以保证API的可用性,降低 集成风险
3数据接口规范流程
在开发期间前后端共同商定好数据接口的交互形式和数据格式。然后实现前后端的并行开发,其中前端工程师再开发完成之后可以独自进行mock测试,而后端也可以使用接口测试平台进行接口自测,然后前后端一起进行功能联调并校验格式,最终进行自动化测试。
前后端分离常用框架
对于前端工程师来说常用的就是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中的注释格式为<%— —%>,不同的注释方式之间时有差异的,接下来我们就对比一下这些差异
JSP使用建议 JSP的使用建议
JSP和Servlet本质上是相同的,JSP页面功能和Servlet后台功能是完全能够互换的,但是JSP的编码风格是在HTML中嵌入少量JAVA代码,它用于显示数据比较方便,如果Servlet上嵌入HTML字符串处理就比较麻烦
Servlet更适合专门编写JAVA代码,JSP更擅长展示数据,Servlet更适合做后台程序,所以在分层上,我们往往将Servlet作为控制层Controller使用,JSP作为视图层view使用,可以让Servlet将数据发送给JSP,然后在JSP上展示数据
JSP指令标签 page指令标签
jsp标签语法
1 <%@ directive attribute="value" %>
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" />
taglib标签 taglib指令标签
JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。
Taglib指令的语法:
1 <%@ taglib uri="uri" prefix="prefixOfTag" %>
JSP内置对象 什么是内置对象
因为JSP的本质是Servlet,在JSP文件经过转译之后,生成JAVA代码,在运行时,JSP给我们准备好了九个可以直接使用而不用我们自己去new的对象,这九个对象我们称之为内置对象.内置对象完全有JSP自行去维护,我们直接使用即可
九大内置对象
四大域对象
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.*;@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" ); 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" ); 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" ); 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;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); req.getRequestDispatcher("showEmp.jsp" ).forward(req,resp); } }
EL表达式 Expression Language
EL表达式中定义了一些可以帮助我们快捷从域对象中取出数据的写法,基本语法为
四个域标志关键字分别为
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/> <%-- 移除域中的数据 --%> <% %> 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模式的这三个部分的职责非常明确,而且相互分离,因此每个部分都可以独立的改变而不影响其他部分,从而大大提高了应用的灵活性和重用性。
过滤器和监听器 过滤器
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;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 > <servlet-name > myServlet1</servlet-name > <servlet-name > myServlet2</servlet-name > </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链。
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;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;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 () ; String getInitParameter (String name) ; Enumeration getInitParameterNames () ; public ServletContext getServletContext () ;
一开始可以再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;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追加数据" ); } }
用注解来写过滤器
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欢迎页,如果没有登录,提示用户进入登录页进行登录操作
项目结构
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;@WebServlet("/login.do") public class LoginController extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("user" ); String pwd = req.getParameter("pwd" ); System.out.println(name); System.out.println(pwd); 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;@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; String requestURI = req.getRequestURI(); System.out.println(requestURI); if (requestURI.contains("login.jsp" )|| requestURI.contains("login.do" )){ filterChain.doFilter(req,resp); return ; } 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;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 ServletRequestAttributeListener
Session
1 2 3 4 HttpSessionListener HttpSessionAttributeListener HttpSessionBindingListener HttpSessionActivationListener
Application
1 2 ServletContextListener ServletContextAttributeListener
监听器怎么使用
两步使用
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;@WebListener public class MyRequestListener implements ServletRequestListener , ServletRequestAttributeListener { @Override public void requestDestroyed (ServletRequestEvent sre) { ServletRequest servletRequest = sre.getServletRequest(); System.out.println("request销毁了" +servletRequest.hashCode()); } @Override public void requestInitialized (ServletRequestEvent sre) { ServletRequest servletRequest = sre.getServletRequest(); System.out.println("request创建了" +servletRequest.hashCode()); } @Override public void attributeAdded (ServletRequestAttributeEvent srae) { 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) { 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) { 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;@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;public class MySessionBindingListener implements HttpSessionBindingListener { @Override public void valueBound (HttpSessionBindingEvent event) { System.out.println("监听器和某个session对象绑定了" ); } @Override public void valueUnbound (HttpSessionBindingEvent event) { } }
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;@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;public class OnLineNumberListener implements HttpSessionListener { @Override public void sessionCreated (HttpSessionEvent se) { 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) { 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、新建文件如图所示:
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 = new User (username,pwd); 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 = new User (username,pwd); 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}
分页实现的思路 目标效果:
浏览器向后台发送的信息应该是什么?
参数1:要查询的是第几页
参数2:页大小—size
其他参数: 查询条件
服务器向浏览器返回的数据应该是什么?
数据1:当前页的所有信息 List
数据2:当前第几页 currentPage
数据3:信息总条数 totalsize
数据4:总页码数 totalpage
数据5:页大小 size
AJAX 同步和异步 首先用户向HTTP服务器提交一个处理请求。接着服务器端接收到请求后,按照预先编写好的程序中的业务逻辑进行处理,比如和数据库服务器进行数据信息交换。最后,服务器对请求进行响应,将结果返回给客户端,返回一个HTML在浏览器中显示,通常会有CSS样式丰富页面的显示效果。
优点
可以保留浏览器后退按钮的正常功能。在动态更新页面的情况下,用户可以回到前一个页面状态,浏览器能记下历史记录中的静态页面,用户通常都希望单击后退按钮时,就能够取消他们的前一次操作,同步交互可以实现这个需求.
缺点
1同步交互的不足之处,会给用户一种不连贯的体验,当服务器处理请求时,用户只能等待状态,页面中的显示内容只能是空白。
2因为已经跳转到新的页面,原本在页面上的信息无法保存,好多信息需要重新填写
什么是异步交互
指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。异步不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好
优点
1前端用户操作和后台服务器运算可以同时进行,可以充分利用用户操作的间隔时间完成运算
2页面没有跳转,响应回来的数据直接就在原页面上,页面原有信息得以保留
缺点
可能破坏浏览器后退按钮的正常行为。在动态更新页面的情况下,用户无法回到前一个页面状态,这是因为浏览器仅能记录的始终是当前一个的静态页面。用户通常都希望单击后退按钮,就能够取消他们的前一次操作,但是在AJAX这样异步的程序,却无法这样做。
AJAX的最大的特点: 异步访问,局部刷新
AJAX关键技术 使用CSS构建用户界面样式,负责页面排版和美工
使用DOM进行动态显示和交互,对页面进行局部修改
使用XMLHttpRequest异步获取数据
使用JavaScript将所有的元素绑定在一起
AJAX之验证用户名是否被占用 JS表单验证只能校验格式是否正确,但是无法验证用户名是否已经存在,这个就需要后台程序接受到数据后通过查询才能够完成的,那么这里就非常适用于使用异步方式校验,保证用于数据提交后,业务完成的成功率.提升用于体验感
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;@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="" ; xhr = new XMLHttpRequest (); xhr.open("GET" ,"unameCheckServlet.do?uname=" +unameText,true ); xhr.onreadystatechange = showReturnInfo; 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 > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.22</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.3</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.30</version > <scope > provided</scope > </dependency > <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& useUnicode=true& characterEncoding=UTF-8& 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;@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;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;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(); 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 >
核心配置文件中可以指定日志打印方式
关于事务配置
映射文件加载方式
类别名
外部属性配置文件存储数据库连接信息 在
里面新建然后找到
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 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 > <package name ="com.bitzh.pojo" /> </typeAliases > <environments default ="mysql" > <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普通模式分开发
传统开发模式很少用了但是为了后面更加理解先学一下,后期也是同样的操作,就是在实现类里面调用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" > <select id ="findOne" resultType ="emp" > select * from emp where empno = 7499 </select > <select id ="findAll" resultType ="emp" > select * from emp </select > <select id ="findEmpMap" resultType ="map" > select * from emp </select > </mapper >
sqlMapConfig中导入EmpMapper映射文件
1 2 3 4 5 6 <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;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 () { System.out.println("sqlSession查询对象List集合" ); List<Emp> emps = sqlSession.selectList("EmpMapper.findAll" ); } @Test public void testSelectMap () { 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.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" > <select id ="findByEmpno" resultType ="emp" parameterType ="int" > select * from emp where empno = #{empno} </select > <select id ="findEmpByDeptnoAndSal" resultType ="emp" parameterType ="map" > select * from emp where deptno = #{deptno} and sal > = #{sal} </select > <select id ="findEmpByDeptnoAndSal2" resultType ="emp" parameterType ="emp" > select * from emp where deptno = #{deptno} and sal > = #{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<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 () { 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.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" > <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;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.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;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.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" > <insert id ="addEmp" parameterType ="emp" > insert into emp values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno}) </insert > <update id ="updateEmp" parameterType ="emp" > update emp set ename = #{ename} where empno = #{empno} </update > <delete id ="deleteEmp" parameterType ="String" > delete from emp where ename = #{ename} </delete > </mapper >
Mybatis代理模式开发 前面已经使用MyBatis完成了对Emp表的CRUD操作,都是由SqlSession调用自身方法发送SQL命令并得到结果的,实现了MyBatis的入门。
但是却存在如下缺点:
不管是selectList()、selectOne()、selectMap(),都是通过SQLSession对象的API完成增删改查,都只能提供一个查询参数。如果要多个参数,需要封装到JavaBean或者Map中,并不一定永远是一个好办法。
返回值类型较固定。
只提供了映射文件,没有提供数据库操作的接口,不利于后期的维护扩展。
在MyBatis中提供了另外一种成为Mapper代理(或称为接口绑定)的操作方式。在实际开发中也使用该方式。下面我们就是要Mapper代理的方式来实现对Emp表的CRUD操作吧,还有完成多个参数传递、模糊查询、自增主键回填等更多的技能实现。搭建好的项目框架如图所示,相比而言,增加了接口EmployeeMapper。但是却会引起映射文件和测试类的变化。
优点:
1有接口 模块之间有规范了
2参数的处理多样了,接口中的方法参数列表由我们自己决定
3通过代理模式由mybatis提供接口的实现类对象 我们不用写实现类了
代理模式浅析
mybatis是如何通过代理模式实现查询的
这条语句的底层使用了动态代理模式,动态创建一个EmployeeMapper的一个代理对象并赋给接口引用。所以在MyBatis中不需要显式提供Mapper接口的实现类,这也是简单的地方。
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;public interface EmpMapper { 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" > <select id ="findAll" resultType ="emp" > select * from emp </select > </mapper >
加载映射代码
1 2 3 4 5 <mappers > <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;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.close(); } }
代理模式简析
自动生成的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 { List<Emp> findAll () ; Emp findByEmpno (int empno) ; 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" > <select id ="findAll" resultType ="emp" > select * from emp </select > <select id ="findByEmpno" resultType ="emp" > select * from emp where empno =#{empno} </select > <select id ="findByDeptnoAndSal" resultType ="emp" > </select > <select id ="findByDeptnoAndSal2" resultType ="emp" parameterType ="map" > 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 > <select id ="findByDeptnoAndSal4" resultType ="emp" > select * from emp where deptno =#{param1.deptno} and sal >= #{param2.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); } Emp emp = mapper.getByEmpno(7902 ); System.out.println(emp); List<Emp> emps2 = mapper.getByDeptnoAndSal(10 , 1500 ); for (Emp em:emps2) { System.out.println(em); } 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); } 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 List<Emp> findByEname ( String name) ;
mapper映射文件
1 2 3 4 5 <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 <insert id ="addEmp" > insert into emp values(DEFAULT ,#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno}) </insert > <update id ="updateEnameByEmpno" > update emp set ename =#{ename} where empno =#{empno} </update > <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.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" > <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 <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 <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标签 一般用于处理模糊查询的模板
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 > <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标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <select id ="findByEmpnos1" resultType ="emp" > select * from emp where empno in <foreach collection ="array" separator ="," open ="(" close =")" item ="deptno" > #{deptno} </foreach > </select > <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" > <resultMap id ="empMap" type ="emp" > <result property ="name" column ="ename" > </result > </resultMap > <select id ="findByEmpno" resultMap ="empMap" > select * from emp 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 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; private Dept dept; }
接口
1 2 3 4 5 6 7 8 public interface EmpMapper { 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" > <resultMap id ="empJoinDept" type ="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 ="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" > <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" > <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" > <resultMap id ="projectJoinEmps" type ="project" > <id column ="pid" property ="pid" > </id > <result column ="pname" property ="pname" > </result > <result column ="money" property ="money" > </result > <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; 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" > <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" > <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比较麻烦
是一种临时存储少量数据至内存或者是磁盘的一种技术.减少数据的加载次数,可以降低工作量,提高程序响应速度
缓存的重要性是不言而喻的。mybatis的缓存将相同查询条件的SQL语句执行一遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询SQL时候不在执行SQL与数据库交互,而是直接从缓存中获取结果,减少服务器的压力;尤其是在查询越多、缓存命中率越高的情况下,使用缓存对性能的提高更明显。
MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能。MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。
一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。
一级缓存 一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。
缓存中的数据使用键值对形式存储数据
namespace+sqlid+args+offset>>> hash值作为键,查询出的结果作为值
二级缓存 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 >
逆向工程(了解) 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 > <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 > <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 > <property name ="suppressAllComments" value ="true" /> </commentGenerator > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false& useUnicode=true& characterEncoding=UTF-8& serverTimezone=Asia/Shanghai& allowPublicKeyRetrieval=true" userId ="root" password ="root" > </jdbcConnection > <javaTypeResolver > <property name ="forceBigDecimals" value ="false" /> </javaTypeResolver > <javaModelGenerator targetPackage ="com.msb.pojo" targetProject =".\src" > <property name ="enableSubPackages" value ="false" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.msb.mapper" targetProject =".\src" > <property name ="enableSubPackages" value ="false" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.msb.mapper" targetProject =".\src" > <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 > <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文件目录里面了