JavaWeb动态Web资源开发
- 静态Web: 用户看到的数据始终不变;动态Web:各人看到的信息不同
- 动态Web:1.页面动态展示,淘宝"千人千面";2.和数据库交互
Web应用程序:给浏览器访问的程序 = 静态Web+动态Web
浏览器访问网络资源流程图
-
客户端通过网络协议(如Http),请求进入服务器,
-
WebSeervice收到请求,其中的**WebServerPlugin(服务插件)**判断要访问的文件是静态还是动态的.
-
静态直接找到资源文件(FIle System),动态的经过jsp,servlet(web技术)增强后(比如用jdbc连接数据库),
-
再通过WebServer(Web服务)找到对应的资源文件,然后以一种编码格式,响应回给客户端.
Http:基于Tcp的超文本传输协议
-
http使用80端口;https使用443端口,s指的是SSL/TLS协议,在HTTP和TCP/IP协议中间
-
http2.0=http/1.1版本,允许客户端与服务器连接后,获得多个资源.1.0是只能获取一个资源
请求行
- post:参数,大小没有限制,浏览器url栏不显示数据,安全,但get比它更高效
消息头(请求头):告诉服务器怎样响应你这个请求
Accept: */* 你这个请求的数据类型
Accept-Encoding: gzip, deflate, br 编码格式
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 语言环境
cache-control: max-age=0 缓存控制,最大缓存存活时间
Connection: keep-alive 连接成功后的持久策略,keep-alive始终保持连接
Host: www.dealctr.com 主机信息
响应行(响应头):告诉客户端(浏览器)应该怎样接收数据
reFresh :告诉客户端,多久刷新一次
location :网页是否重新定位
响应状态码
200:请求响应成功
300:请求重定向
404:找不到资源
500:服务器代码错误.502:网关错误
思考:浏览器从输入网址回车,到页面展示回来,经历了什么
Web服务器
服务器是一种被动的操作,处理请求与给用户回复响应
-
微软windows自带IIS服务器.
-
Tomcat是一款先进,稳定,免费的轻量级应用服务器
Tomcat
重新部署项目是重新加载当前一个项目.
-
java的根加载器在r(runtiem)t.jar包里.
-
启动startup.bat后,Tomcat的默认网址是http://localhost:8000/
-
conf下的server.xml文件是tomcat的配置文件,可以在里面修改初始参数,如端口号
<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />
一个webapps文件夹,就是一个Web应用
- webapps(Web项目)\ROOT下的index.jsp是默认访问的主页面:localhost:8080/index.jsp
浏览器访问网站流程
- 输入网址后,先去C:\Windows\System32\drivers\etc\hosts文件中找,有没有网址对应的域名映射(如下面的activate.navicat.com).
- 找到后返回对应的ip地址(activate.navicat.com对应的是127.0.0.1)
- 这个地址中有我们需要的web程序,找到后直接访问.
- 如果没有找到,就去**DNS(运营商根服务器)**上找,找的到就返回,找不到就提示"404(not found)找不到资源"
改变本机ip地址访问前缀:修改C:\Windows\System32\drivers\etc\hosts中的127.0.0.1(本地ip地址)的映射网址.
# 设置本机的ip地址中的,127.0.0.1和LiChangGe127.0.0.1的,映射域名为activate.navicat.com
127.0.0.1 LiChangGe127.0.0.1 activate.navicat.com
127.0.0.1 LiChangGe
# 都可以用
http://activate.navicat.com:8080/
http://lichangge:8080/
如果不可以,就再修改server.xml的host属性
<Host name="LiChangGe" appBase="webapps" unpackWARs="true" autoDeploy="true"></Host>
Web项目结构
-
一个常见的TomcatWeb项目基本结构如下:webapps(项目顶级目录)\ROOT(自己的web项目)+index.jsp\WEB-INFO(web程序配置信息目录)\web.xml(web程序配置文件)
-
Tomcat的examples是官方API包
Maven项目架构管理工具
- 核心思想:约定大于配置
idea可以不配置环境变量,自带maven
# 查看maven版本
C:\Users\林木>mvn -version
Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739)# 环境变量配置,你自己的maven安装目录
MAVEN_HOME
D:\Maven\apache-maven-3.8.3# maven的运行程序bin目录,用idea可以不配置
M2_HOME
D:\Maven\apache-maven-3.8.3\bin# Path引用maven,必须有
Path
%MAVEN_HOME%\bin
-
conf\settings.xml配置maven本地仓库目录和镜像
# 本地仓库,配置后去看看C:\用户\李长歌(这里是你自己的电脑账户)\.m2(默认隐藏的),这是maven默认的本地仓库,可以删掉. <localRepository>D:\Maven\apache-maven-3.8.3\mvn_properties</localRepository> # 镜像 <mirrors><!-- 阿里镜像配置 --><mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>Nexus aliyun</name><url>https://maven.aliyun.com/repository/public</url></mirror> </mirrors>
idea创建MavenWeb项目
一键直达
用模板创建
maven常用的全局设置
创建新模块时选择Maven构建,实现联动父项目
添加webapp目录
添加框架支持时选择web服务,来添加web目录
一个标准的web子模块
- 必须配置工件:因为访问一个网站,必须指定一个文件夹
http://localhost:8080/xiaomi
pom.xml(Maven核心配置文件)代码解读
父类和子类模块的maven代码会联动
<groupId>com.changGe.li</groupId>
<artifactId>JavaWeb</artifactId>
<version>1.0-SNAPSHOT</version><modules><module>XiaoMi</module>
</modules><name>JavaWeb</name>
<packaging>pom</packaging># 子类
<groupId>com.changGe.li</groupId>
<artifactId>XiaoMi</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging><name>XiaoMi Maven Webapp</name>
配置运行时的java版本信息
<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target>
</properties>
配置依赖
<!-- 配置依赖:maven会自动导入这个jar包,所需要的其他jar包 -->
<dependencies></dependencies>
- maven遇到配置文件,无法导出或使用时 (1条消息) Maven解决配置文件无法导出或者无法生效_ReolPurion的博客-CSDN博客
- idea的日志文件,记载了我们的错误
- bat是批处理文件,可以在里面写cmd代码
HttpServlet
需要导包
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><!--注意这里是配置作用域为测试和编译时有效不配置的话,maven打包时会把这个包也打进项目中,就会和tomcat本身的servlet-api包冲突,报500错误等--><scope>provided</scope>
</dependency>
当存储数据时,其下所有类本质是一个Map集合
继承HttpServlet(javax.servlet.http包下),需要Maven导入依赖
只要是实现了Servlet接口,就叫做Servlet小程序
<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version>
</dependency>
重写方法
package com.changGe.li.servlet;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("utf-8");resp.getWriter().print("Hello World");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req,resp);}}
配置域名映射
<servlet><!--设置名字和要加载的Servlet--><servlet-name>he</servlet-name><servlet-class>com.changGe.li.servlet.TestServlet</servlet-class>
</servlet><servlet-mapping><!--对应he下面的Servlet的域名映射是/he,注意,一定要/(代表当前web项目,也就是localhost:8080)--><servlet-name>he</servlet-name><url-pattern>/he</url-pattern>
</servlet-mapping>
tomcat日志文件配置
Servlet原理
- 如果需要携带数据去到新页面,用request转发.
- 不携带数据,只是单纯的页面跳转或刷新,就用respons重定向(respons也可以携带参数,后面再说).
浏览器向服务器发送请求,访问的是web容器(可以理解为服务器)
-
浏览器访问的是映射,然后返回IP地址
-
客户端发送请求,到达web容器.
-
如果是第一次的话,web容器直接访问servlet,第二次和以后,就是带着请求,找servlet的对应的service方法(如doGet()和doPost()等).
-
同时web容器的respons对象,也会监听接收,来自serivice的响应.监听到数据,web容器响应给客户端
作用到代码上的执行流程
表单提交时携带请求和请求参数
- 按下submit提交表单,浏览器向action的值的地方发送请求
- 提取表单中所有有name属性的元素,把他们的值作为参数传递:如localhost:8080/Super/jsp/user.do?method=modify&uid=26
<form id="userForm" name="userForm" method="post" action="${pageContext.request.contextPath }/jsp/user.do"><input type="hidden" name="method" value="modify"><input type="text" name="userName" id="userName" value="${user.userName}"> <input class="button_box" type="submit" value="保存"></form>
- 服务器找到web.xml,看有没有对应的映射
- 找到这个映射对应的servlet
<servlet><servlet-name>modifyPassword</servlet-name><servlet-class>com.changGe.li.servlets.ModifyPasswordServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>modifyPassword</servlet-name><!--/ 代表当前路径,也就是localhost:8080其后的路径语法根据表单的请求来写也可以随便写,如/* 代表项目下所有资源都可以访问,或--><url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping><!--不能这样写,会报错java.lang.IllegalArgumentException: servlet映射中的<url pattern>[/*.do]无效因为/*,是匹配localhost:8080/下所有servlet上,/*.do服务器就认为你要访问所有以.do为后缀的Servlet,找不到
-->
<url-pattern>/*.do</url-pattern>
- 优先访问最接近的映射
<a href="${pageContext.request.contextPath}/hello">
<!--优先访问最接近的-->
<url-pattern>/hello</url-pattern>
<!--不访问-->
<url-pattern>/hello.do</url-pattern>
-
调用对应的service(),如上面的表单提交方式是get,就找到doGet();
-
执行其中的实现代码
public class ModifyPasswordServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//重定向发送一个请求且携带参数resp.sendRedirect(req.getContextPath()+"/jsp/user.do?method=query");}
ServletContxet:管理所有Servlet,实现Servlet间共享数据
每个程序都有一个对应的上下文对象(ServletContext)
实现Servlet间共享数据
<servlet><servlet-name>he</servlet-name><servlet-class>com.changGe.li.servlet.TestServlet</servlet-class>
</servlet><servlet-mapping><servlet-name>he</servlet-name><url-pattern>/hello</url-pattern>
</servlet-mapping><servlet><servlet-name>hello</servlet-name><servlet-class>com.changGe.li.servlet.TestServletOne</servlet-class>
</servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello.do</url-pattern>
</servlet-mapping>
public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("utf-8");ServletContext servletContext = this.getServletContext();servletContext.setAttribute("name","武则天");}
} public class TestServletOne extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("utf-8");ServletContext servletContext = this.getServletContext();Object name = servletContext.getAttribute("name");System.out.println(name);}}
servletContext转发
// 一定要写上/代表当前web项目目录下
servletContext.getRequestDispatcher("/info.jsp").forward(req,resp);
配置初始化参数后,其下所有servlet都可以获取
<context-param><param-name>name</param-name><param-value>李长歌</param-value>
</context-param>
ServletContext servletContext = this.getServletContext();String name = servletContext.getInitParameter("name");
System.out.println(name);//李长歌
项目构建后会被打包,在target/项目/WEB-INF/classes下
classes俗称类路径
如果java目录下的资源没有导入成功,在pom.xml中添加下列代码
然后删除target目录,重新部署或重启tomcat服务器
filtering的作用 (4条消息) maven Filtering true 作用_滕青山YYDS的博客-CSDN博客
<!--构建项目时,控制某些资源可以被导入或导出-->
<build><resources><resource><!-- 导入src/main/java下,所有目录下,以.properties和以.xml为后缀名的文件 --><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><!--允许我这个文件用一些字符,替换掉导入文件中对应的字符--><filtering>true</filtering></resource></resources>
</build>
读取资源文件
public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("utf-8");ServletContext servletContext = this.getServletContext();InputStream resourceAsStream = servletContext.getResourceAsStream("/WEB-INF/classes/com/changGe/li/lib/test1.properties");Properties properties = new Properties();properties.load(resourceAsStream);String name = properties.getProperty("name");System.out.println(name);}}
Request请求转发:直接请求另一个资源的路径,直接写相对路径就可以
服务器接收到浏览器的请求后,针对这个请求自动创建Request(请求)和Respons(响应)对象
HttpServlet源码中有初始的状态码
public interface HttpServletRequest extends ServletRequest {/*** String identifier for Basic authentication. Value "BASIC"*/public static final String BASIC_AUTH = "BASIC";/*** String identifier for Form authentication. Value "FORM"*/public static final String FORM_AUTH = "FORM";/*** String identifier for Client Certificate authentication. Value "CLIENT_CERT"*/public static final String CLIENT_CERT_AUTH = "CLIENT_CERT";
请求转发时,可以携带参数
req.setAttribute("name","李长歌");
req.getRequestDispatcher("info.jsp").forward(req,resp);
获取复选框中数据
<form action="${pageContext.request.contextPath}/hello"><input type="checkbox" name="check" value="篮球"><input type="checkbox" name="check" value="足球"><input type="checkbox" name="check" value="排球"><input type="submit" value="点我一下看看"/>
</form>
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取所有的值,返回数组String[] names = req.getParameterValues("check");for (String name : names) {System.out.println(name);}}
Respons响应重定向:从你现在的路径去到一个新的路径,url栏会发生变化,要写绝对路径
//req.getContextPath() = 当前项目目录路径:localhost:8080
resp.sendRedirect(req.getContextPath()+"/info.jsp");
重写向时不会传递参数
响应数据
//把集合响应回去,ajax用做函数的data
resp.setContentType("application/json");
resp.setCharacterEncoding("utf-8");
try {PrintWriter writer = resp.getWriter();//将集合变成json对象输出writer.write(JSON.toJSONString(result));writer.flush();writer.close();} catch (IOException e) {e.printStackTrace();
}
下载文件:respons负责把读取到的数据,响应回给浏览器
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//以utf-8解析请求resp.setCharacterEncoding("utf-8");//让浏览器以utf-8模式解析响应resp.setHeader("Content-type", "text/html;charset=UTF-8");String imgUrl = "E:\\JAVA\\JavaWeb\\XiaoMi\\src\\main\\resources\\test.properties";//从最后一个/开始截取,获得文件名String fileName = imgUrl.substring(imgUrl.lastIndexOf("\\") + 1);//输入流FileInputStream fileInputStream = new FileInputStream(imgUrl); //设置响应头:内容类型,附件;文件名字=resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));//获取响应的文件输出流ServletOutputStream outputStream = resp.getOutputStream();byte[] bytes = new byte[10];int length = 0;while ((length = fileInputStream.read(bytes)) != -1){outputStream.write(bytes,0,length);}}
resp.setCharacterEncoding("utf-8");resp.setHeader("Content-type", "text/html;charset=UTF-8");String imgUrl = "E:\\JAVA\\JavaWeb\\XiaoMi\\src\\main\\resources\\test.properties";//从最后一个/开始截取,获得文件名String fileName = imgUrl.substring(imgUrl.lastIndexOf("\\") + 1);//输入流FileInputStream fileInputStream = new FileInputStream(imgUrl);BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);FileReader fileReader = new FileReader(imgUrl);//设置响应头:内容类型,附件;文件名字=resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));//获取响应的文件输出流ServletOutputStream outputStream = resp.getOutputStream();BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);// PrintWriter writer = resp.getWriter();byte[] bytes = new byte[10];char[] chars = new char[1024];int length = 0;/*while ((length = fileInputStream.read(bytes)) != -1){outputStream.write(bytes,0,length);}*/while ((length = bufferedInputStream.read(bytes)) != -1){bufferedOutputStream.write(bytes,0,length);bufferedOutputStream.flush();}bufferedInputStream.close();bufferedOutputStream.close();outputStream.close();fileInputStream.close();
验证码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//宽高,以8位RGB色彩模式打包出去BufferedImage bufferedImage = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);//得到支笔Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();graphics.setBackground(new Color(0xEBA9E7));graphics.setColor(new Color(0x00F4E0));//实心矩形,x,y,width,heightgraphics.fillRect(400,400,600,600);//字体,罗马基线,大小graphics.setFont(new Font("方正舒体",Font.ROMAN_BASELINE,50));//要写的字符串,x,ygraphics.drawString(verifyCode(),50,200);//让浏览器以图片格式解析resp.setContentType("image/png");//设置浏览器的3秒刷新一次resp.setHeader("refresh","3");//多少秒后过期resp.setDateHeader("expires",-1);//缓存控制不缓存resp.setHeader("Cache-Control","no-cache");//程序不缓存,作用和上面一样,一般两者一起使用resp.setHeader("Program","no-cache");//把图片,按照格式,写到一个地方去ImageIO.write(bufferedImage,"png",resp.getOutputStream());}public String verifyCode(){Random random = new Random();/*** valueOf()的底层:return (obj == null) ? "null" : obj.toString();* 它不会报空指针异常,但是如果是null,也会直接变成"null"*/String number = String.valueOf(random.nextInt(10000000));StringBuilder stringBuilder = new StringBuilder(number);//保证验证码是7个字符for (int i = 0; i < 7 - number.length(); i++) {stringBuilder.append("x");}System.out.println(stringBuilder);return stringBuilder.toString();}
请求重定向与请求转发的区别
- 请求重定向
使用responce.sendRedirect(“xx.jsp”)来进行重定向。是客户端的行为:即客户端会访问两次,第一次访问后会立即跳转到第二个重定向页面上,从本质上讲等于两次请求,而前一次的请求封装的request对象不会保存,地址栏的URL地址会改变。
2、请求转发
使用request.getRequestDispatcher(“xx.jsp”).forward(request,response)请求转发。forward(request,response)用于保存内置对象request和response。是服务器的行为:服务器会代替客户端去访问转发页面,从本质是一次请求,转发后请求对象会保存,地址栏的URL地址不会改变。
Cookie(客户端技术)
有状态会话:会话(客户端和服务器间的一次交流seesion) + 缓存(cookie)
本质是把用户的数据写给浏览器,由浏览器保存
用cookie读取上一次访问时间:刷新一次数据也刷新
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String format = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒").format(new Date());Cookie lastTime = new Cookie("lastTime", format);boolean flag = true;Cookie[] cookies = req.getCookies();if(cookies != null && cookies.length > 0){for (Cookie cookie : cookies) {String value = cookie.getName();if(value != null && value.equals("lastTime")){flag = false;//特定格式解析字符串,常用于中文解码req.setAttribute("lastTime", URLDecoder.decode(cookie.getValue(),"utf-8"));//更新登录时间cookie.setValue(format);/*** 缓存时间0秒,就是删除cookie,* 只有用户关闭浏览器才会删除cookie*/cookie.setMaxAge(60 * 60);//单位是秒resp.addCookie(lastTime);req.getRequestDispatcher("index.jsp").forward(req,resp);return;}}//for}if(flag){resp.addCookie(lastTime);req.setAttribute("msg", "这是您第一次登录");req.getRequestDispatcher("info.jsp").forward(req,resp);}}
精简版
String format = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒").format(new Date());Cookie[] cookies = req.getCookies();boolean flag = true;if(cookies != null && cookies.length > 0){for (Cookie cookie : cookies) {String value = cookie.getName();if(value != null && value.equals("lastTime")){flag = false;req.setAttribute("msg", "您上一次登录时间:"+URLDecoder.decode(cookie.getValue(),"utf-8"));}}//for}if(flag){req.setAttribute("msg", "这是您第一次登录");
}Cookie lastTime = new Cookie("lastTime", format);
resp.addCookie(lastTime);req.getRequestDispatcher("info.jsp").forward(req,resp);
浏览器的applition里可以看到cookie缓存.,可以删除掉特定的cookie
只有清除了localhost:8080的缓存后,cookie才真正消失,每次浏览器都默认存储了一些cookie
用everything我们可以找到**cookiie的存放位置,**只是我们不能用(没有权限,到AC就看不到了)
浏览器最多发送300个coolie,约是15个网站的,最大共1200kb数据.
Seesion(服务器技术)
服务器会为每个浏览器都创建一个seesion,在服务器上保存用户的数据,全局作用域,直到浏览器关闭.
seesion在浏览器默认存储30分钟
<session-config><!--15分钟自动失效--><session-timeout>15</session-timeout>
</session-config>
-
seesion存储在cookie中,去浏览器的applition中找到cookie看,或者请求头的cookie里保存的有
-
底层代码相当于response.addcookie(new Cookie(“JSSSIONID”,value));
//约等于此,不过是服务器内部实现的,我们这样的不行 resp.addCookie(new Cookie("JSSSIONID", "123"));
-
seesion可以存储对象,能用seesion就不要用context
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session = req.getSession();session.setAttribute("name","李长歌");session.setAttribute("age","18");session.setAttribute("sex","女");//如果是新创建的if(session.isNew()){Enumeration<String> attributeNames = session.getAttributeNames();//sessionId是D6E8AD19A02583A4CB3DA5F36F86EB28System.out.println("sessionId是"+session.getId());/*** 结果:* 女* 李长歌* 18* * 页面自动回到浏览器主页了,因为session注销了*/while (attributeNames.hasMoreElements()){String name = attributeNames.nextElement();System.out.println(session.getAttribute(name));//可以用于用户退出时删除用户信息session.removeAttribute(name);}//注销session.invalidate();}}
和全文索引的应用场景一样
JSP(Java服务页面JavaServerPages)
本质就是Servlet(小应用服务程序)
- PHP语言不能承载大访问量,微软ASP是HTML+VB脚本,用C#语言开发
- jsp基于Java的B(browser)S架构,sun公司开发,可以承载三高:高并发,高性能和高可用(数据要高可靠,服务要高可用)
index.jsp源码分析
服务器访问任何资源,本质是访问Servlet. 因为idea的tomcat>work目录下的jsp页面最后变成了.java文件.
JSP源码分析__板蓝根_的博客-CSDN博客_jsp源码
这个博客没有找到,就在这里面找
这是我最终找到的路径:C:\Users\林木\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\61c407dd-c490-4564-89ac-71da59dbac6b\work\Catalina\localhost\XiaoMi\org\apache\jsp
-
继承自HttpJspBase类,HttpJspBase继承自HttpServlet类
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
-
初始化,销毁和服务三大方法
public void _jspInit() { }public void _jspDestroy() { }public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
-
判断请求类型.method()
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {response.setHeader("Allow","GET, HEAD, POST, OPTIONS");response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");return;}
-
初始化对象有context(改名application),page = this…
final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null;
-
有个try里面,在输出页面前为一些默认的对象,如pageContext等赋值,我们可以在jsp页面中直接使用
try {response.setContentType("text/html;charset=utf-8");pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);_jspx_page_context = pageContext;application = pageContext.getServletContext();config = pageContext.getServletConfig();session = pageContext.getSession();out = pageContext.getOut();_jspx_out = out;out.write("\n");out.write("<html>\n");out.write("\n");out.write("<body>\n");
<%--这是jsp表达式,加"="号专门用来输出--%>
<input type="text" value="名字是:<% String name="李长歌"; %><%= name%>"/><!--对应到jsp.java底层->
out.write(" <input type=\"text\" value=\"名字是:");String name="李长歌"; out.print( name);
jsp语法
javax-servlet-jsp-api依赖
<dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.0</version>
</dependency>
html的注释可以在控制台看到,jsp不显示
<%--jsp脚本在_jspService()中--%>
<!--${i}EL表达式-->
<% for(int i = 0 ; i < 5; i++){%><h1>hello world<%= i %></h1>
<% } %><%--jsp声明在jsp类中--%>
<%! public void test(){}
%>
自定义错误页面
<%@ page pageEncoding="utf-8" %><%--jsp标签:写在页面最上面,作为配置.显式声明这是一个错误页面--%>
<%@ page isErrorPage="true" %>
<%@page errorPage="info.jsp" %>
<html><body><% int i = 1 / 0;%>
</body>
</html>
或
<error-page><!--500错误时跳转页面--><error-code>500</error-code><location>/info.jsp</location>
</error-page>
添加公共页面
<%--是将页面中的代码提出来,然后和现有页面代码拼接在一起--%>
<%@ include file="info.jsp" %>
out.write("<head>\r\n");
out.write(" <title>Info</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\r\n");
out.write("<h3>info下的h3</h3>\r\n");
out.write(" <img src=\"WEB-INF/images/error.jpg\" width=\"1240\" height=\"800\">\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\n");
out.write("<html>\n");
out.write("\n");
out.write("<body>\n");<%--第二种方法--%>
<%--这是调用其它页面,不会出现代码冲突的问题--%>
<jsp:include page="info.jsp"/>
out.write(" <h2>22222</h2>\n");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "info.jsp", out, false);
out.write("\n");
jsp标签
<%--作用域是request--%>
<jsp:forward page="info.jsp"><jsp:param name="name" value="李长歌"/>
</jsp:forward>
<h3>name is:${pageContext.request.getParameter("name")}</h3>
static包下一般放前端的资源,如css,img,js和plugins等.
WEB-INF目录对用户不可见
EL表达式
作用域优先级:pageScope > requestScope > sessionScope > pageContext(页面运行时)>applicationScope(服务器)
如果没有给定取值作用域,则默认从pageScope开始查找,找到则返回,没找到则按照上述顺序继续查找,以此类推,知道找到为止。
如果最终没有找到,则返回null。
九大对象及作用域
- page对象的数据出了页面就消失了;
- seesion间可以互相访问,但是当要统计服务器中所有数据时,就要去到更高级的application对象了:
- page -> request -> seesion -> pageContext>application
- 类似于双亲委派机制,先从应用类加载器找,找不到用扩展类加载器(ext中的jar包),还找不到就最后去根加载器(rt目录下的jar包).
<body><%pageContext.setAttribute("name","李长歌");session.setAttribute("name1","session");request.setAttribute("name2","request");application.setAttribute("name3","application");//获取值String name5 = (String) pageContext.findAttribute("name5");%><%--找不到也不报错,el表达式自动过滤null值,不显示在页面中--%><!--name:李长歌 name1:session name2:request name3:application name5:-->name:${name}name1:${name1}name2:${name2}name3:${name3}name5:${name5}<!--不显示-->
</body>
设置作用域
<%//等同于session.setAttribute("key","value");pageContext.setAttribute("key","value",pageContext.SESSION_SCOPE);
%>
作用域保存数据的重要性从低到高:request(客户端看完即删,新闻图片等.) < seestion(需要二次使用,但不事关关键业务,如购物车) < application(需要全局作用,事关关键业务,如聊天数据)
<%pagecontext.setAttribute("name","李长歌");request = pagecontex.getRequest();requext.forward("forward.jsp");
%><%--页面转发时,会自动将数据也给转发走--%>
<%--info页面下--%>
<%String name = (String)pagecontext.getAttribute("name");out.print(name);
%>
jstl标签
jstl-api依赖和standrard标签库依赖
如果报错说找不到这个包,可能是因为tomcat中没有这个jar包,直接粘到tomcat的work目录中去
<dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version>
</dependency>
<dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version>
</dependency>
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%--写在最顶上,导入core标签库,字首(也就是前缀)为c,字首可以随便写--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%><%--如果报错:根据标记文件中的TLD或attribute指令,attribute[items]不接受任何表达式是因为和tomcat中的jstl-api.jar包不兼容就换成下面这个--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
switch循环和if判断
<body><%--定义变量sorce,赋值为100--%><c:set var="score" value="100"/><%-- 把test判断的值,赋值给number --%><c:if test="${score > 0 and score <= 100}" var="number"><c:out value="${number}"/><%--循环判断--%><c:choose><%--会自动break--%><c:when test="${score >=90 or score <= 100}"><c:out value="优秀的成绩"/></c:when></c:choose></c:if>
</body>
增加for
<%ArrayList<Object> arrayList = new ArrayList<>();arrayList.add(0,0);arrayList.add(1,1);arrayList.add(2,2);arrayList.add(3,3);request.setAttribute("list",arrayList);
%><%--从1遍历到3,一次i+2,最后只输出1,3--%>
<c:forEach var="item" items="${list}" begin="1" end="3" step="2"><%--从list集合中取值,值为item,打印item到页面上--%><c:out value="${item}"/>
</c:forEach>
JavaBean(咖啡豆)
jsp:useBean的用法_远方©的博客-CSDN博客_jsp:usebean
- 有无参构造+属性私有化,有对应的set,get方法
<form action="index.jsp" method="get"><input type="text" name="name" value="李长歌"><input type="text" name="age" value="18"><input type="submit" value="请提交">
</form><%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/><%--获取表单中的所有值,并一一赋值给User类,对应的set方法--%>
<jsp:setProperty name="user" property="*"/><%--从user对象中取值--%>
<jsp:getProperty name="user" property="name"/><%--李长歌--%>
<jsp:getProperty name="user" property="age"/><%--18--%>
<%=user.getAge()%><%--18--%>
<form action="index.jsp" method="get"><input type="text" name="name" value="李长歌"><input type="text" name="age" value="18"><input type="submit" value="请提交">
</form><%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/><%--只给setName()赋值--%>
<jsp:setProperty name="user" property="name"/><jsp:getProperty name="user" property="name"/><%--李长歌--%><%--尽管age的input输入了值,还是为0--%>
<jsp:getProperty name="user" property="age"/><%--0--%>
<%=user.getAge()%><%--0--%>
<form action="index.jsp" method="get"><input type="text" name="name" value="李长歌"><input type="text" name="age" value="18"><input type="submit" value="请提交">
</form><%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/><%--给setName()赋值"武则天"--%>
<jsp:setProperty name="user" property="name" value="武则天"/><%--最后得到的还是"武则天"--%>
<jsp:getProperty name="user" property="name"/>
<form action="index.jsp?name=李世民" method="get"><input type="text" name="name" value="李长歌"><input type="text" name="age" value="18"><input type="submit" value="请提交"></form><%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/><%--从请求的参数取值name,赋值给user对象的setName--%>
<jsp:setProperty name="user" property="name" param="name"/><%--最后得到的还是"李长歌"--%>
<jsp:getProperty name="user" property="name"/>
<body><%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/><%--赋值给user对象的setName--%>
<jsp:setProperty name="user" property="name" value="武则天"/><%--"武则天"--%>
<jsp:getProperty name="user" property="name"/></body>
MVC(Model view controller模型视图控制器)三层架构
三层架构就是在Model层进行dao(数据持久),service(业务处理)的中间过渡
Filter过滤器:在服务器与资源文件之间过滤数据
mysql-connector-java依赖,一定要用和自己数据库版本一样的jar包(依赖),不同版本的结构不同
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version>
</dependency>
实现Filter下的方法init初始化,doFIlter业务,destroy销毁,可以只实现doFilter()
@Override
public void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);
}@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {}
@Override
public void destroy() {Filter.super.destroy();
}
用filter实现登录验证
- 用户提交表单后,找到对应的servlet
<h1 class="title">欢迎登录</h1>
<form action="${pageContext.request.contextPath}/login" method="get"><input class="input_box" type="text" name="userCode" value="hanlubiao" placeholder="用户名"><input class="input_box" type="password" name="password" value="0000000" placeholder="密码"><input class="button_box" type="submit" value="登录"><span>${error}${param.get("error")}</span>
</form>
<servlet><servlet-name>login</servlet-name><servlet-class>com.changGe.li.servlets.LoginServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>login</servlet-name><url-pattern>/login</url-pattern>
</servlet-mapping>
- 这里面取值用户的信息,判断是否成功.
成功了就跳转主页,失败是失败页面.
public class LoginServlet extends HttpServlet {//数据库工具类private UserService userService = new UserServiceImpl();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String userCode = req.getParameter("userCode");String password = req.getParameter("password");//根据用户名和密码找到User对象User user = userService.getUser(userCode,password);//不为空就保存到session中if(user != null){req.getSession().setAttribute("user",user);resp.sendRedirect(req.getContextPath()+"/jsp/frame.jsp");}else {//失败就重定向req.setAttribute("error","用户名或密码错误");req.getRequestDispatcher("login.jsp").forward(req,resp);}}
}
- 注销就是让用户对应的seesion删除掉:req.getSession().removeAttribute(“user”);
注销后回到登录页面.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {User user = (User)req.getSession().getAttribute("user");if(user != null){req.getSession().removeAttribute("user");resp.sendRedirect(req.getContextPath()+"/login.jsp");}else {//为空,代表已经被删除了req.getRequestDispatcher("error.jsp").forward(req,resp);}}
- 有个过滤器每次有新请求时,都判断用户的seesion是否为空
<!-- 初始过滤:强制登录,管理系统不需要让游客看到首页 -->
<filter><filter-name>forcoLogin</filter-name><filter-class>com.changGe.li.filters.ForcoLoginFilter</filter-class>
</filter>
<filter-mapping><filter-name>forcoLogin</filter-name><url-pattern>/jsp/*</url-pattern>
</filter-mapping>
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resq = (HttpServletResponse) response;//从session中获取user对象User user = (User)req.getSession().getAttribute("user");if(user == null){request.setAttribute("error","请先登录,谢谢");resq.sendRedirect(req.getContextPath()+"/login.jsp?error="+ URLEncoder.encode("请先登录,谢谢","utf-8"));}else{//找到之后直接进入主页.不用登录//必须让请求和响应 链接 起来后,程序才会继续进行,不然就卡住chain.doFilter(req, resq);}}
- user的session经常用到,可以提取成静态常量
public static final String USER_SEESION = "user";
用这个思想,实现一个判断用户vip等级的demo.
主要是过滤器根据username获取到的seesion,进而获取对象,然后判断等级
<form action="hello" method="get">等级:<input type="text" name="grade" value="2"><input type="submit" value="提交"></form>
info.jsp
<body><h2>您的等级是:${grade}</h2>
</body>
web.xml
<filter><filter-name>verify</filter-name><filter-class>com.changGe.li.FilterTest</filter-class>
</filter>
<filter-mapping><filter-name>verify</filter-name><!--监听名为he的servlet的动作--><servlet-name>he</servlet-name><!--两者可以同时存在,不存在优先级这一说--><url-pattern>/*</url-pattern>
</filter-mapping><servlet><servlet-name>he</servlet-name><servlet-class>com.changGe.li.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>he</servlet-name><url-pattern>/hello</url-pattern>
</servlet-mapping>
TestServlet
public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String grade = req.getParameter("grade");req.getSession().setAttribute("grade",grade);req.getRequestDispatcher("info.jsp").forward(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req,resp);}}
FilterTest
public class FilterTest implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resq = (HttpServletResponse) response;String grade = req.getParameter("grade");if(grade == null || grade.equals("")){//从session中获取user对象grade = String.valueOf(req.getSession().getAttribute("grade"));}if(grade != null && !grade.equals("")){System.out.println("if中的"+grade);switch (grade){case "1":req.setAttribute("grade","1");break;case "2":req.setAttribute("grade","2");break;default:req.setAttribute("grade","3");}chain.doFilter(req, resq);}else{resq.getWriter().write("等级为空");}}}
Listener监听器
各种类型的监听器都可以实现,如HttpSestionListener,始终监听session域
index.jsp
<form action="hello" method="get"><input type="submit" value="提交"></form>
<!--web.xml配置监听器-->
<listener><listener-class>com.changGe.li.ListenerTest</listener-class>
</listener>
监听到有session被创建时,就创建一个sess的session,赋值
有session被销毁时,得到资源
public class ListenerTest implements HttpSessionListener {//有session被创建时@Overridepublic void sessionCreated(HttpSessionEvent se) {//创建sessionse.getSession().setAttribute("sess", URLEncoder.encode("监听到session被创建了"));}//有session被销毁时@Overridepublic void sessionDestroyed(HttpSessionEvent se) {Object source = se.getSource();//输出结果org.apache.catalina.session.StandardSessionFacade@8539a6fSystem.out.println("监听到的资源是:"+source.toString());}}
servlet中先创建一个session,触发sessionCreated()
然后得到sessionCreated()创建的session打印出去
最后删除自己创建的session
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//创建sessionreq.getSession().setAttribute("name","李长歌");//写去sessionresp.getWriter().write(String.valueOf(req.getSession().getAttribute("sess")));//删除sessionreq.getSession().invalidate();}
实现统计在线人数
只要有个session被创建后,在线人数就+1;销毁时在线人数-1
servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//一秒刷新一次resp.setHeader("refresh","1");//一定要从ServletContext中取值,ServletContext监控所有sessionString online = String.valueOf(req.getServletContext().getAttribute("online"));req.setAttribute("online",online);req.getRequestDispatcher("info.jsp").forward(req,resp);}
public class ListenerTest implements HttpSessionListener {//有session被创建时@Overridepublic void sessionCreated(HttpSessionEvent se) {//在最高的作用域servletContext操作sessionServletContext servletContext = se.getSession().getServletContext();Integer online = (Integer) servletContext.getAttribute("online");if(online == null || online <= 0){online = 1;}else {online ++;}servletContext.setAttribute("online",online);}//有session被销毁时@Overridepublic void sessionDestroyed(HttpSessionEvent se) {ServletContext servletContext = se.getSession().getServletContext();Integer online = (Integer)servletContext.getAttribute("online");if(online == null || online < 0){online = 0;}else {online --;}servletContext.setAttribute("online",online);}}
info.jsp
<body><h2>在线人数是:${online}</h2>
</body>
web.xml
<session-config><!-- 超时(过期)时间为1分钟 写成0或负数就代表永远不超时--><session-timeout>1</session-timeout>
</session-config>
实现GUI窗口关闭,实现它的子类,用的是适配器模式
public static void main(String[] args) {JFrame jFrame = new JFrame();//适配器模式,windowAdapter实现了WindowListenerjFrame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosed(WindowEvent e) {super.windowClosed(e);}});}
WindowAdapter实现了WindowListener
public abstract class WindowAdapterimplements WindowListener, WindowStateListener, WindowFocusListener
{
juit单元测试依赖
<!--可以直接用@test写在方法上,不用main方法,就能直接运行了-->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>
q.setAttribute(“online”,online);
req.getRequestDispatcher(“info.jsp”).forward(req,resp);
}
```java
public class ListenerTest implements HttpSessionListener {//有session被创建时@Overridepublic void sessionCreated(HttpSessionEvent se) {//在最高的作用域servletContext操作sessionServletContext servletContext = se.getSession().getServletContext();Integer online = (Integer) servletContext.getAttribute("online");if(online == null || online <= 0){online = 1;}else {online ++;}servletContext.setAttribute("online",online);}//有session被销毁时@Overridepublic void sessionDestroyed(HttpSessionEvent se) {ServletContext servletContext = se.getSession().getServletContext();Integer online = (Integer)servletContext.getAttribute("online");if(online == null || online < 0){online = 0;}else {online --;}servletContext.setAttribute("online",online);}}
info.jsp
<body><h2>在线人数是:${online}</h2>
</body>
web.xml
<session-config><!-- 超时(过期)时间为1分钟 写成0或负数就代表永远不超时--><session-timeout>1</session-timeout>
</session-config>
实现GUI窗口关闭,实现它的子类,用的是适配器模式
public static void main(String[] args) {JFrame jFrame = new JFrame();//适配器模式,windowAdapter实现了WindowListenerjFrame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosed(WindowEvent e) {super.windowClosed(e);}});}
WindowAdapter实现了WindowListener
public abstract class WindowAdapterimplements WindowListener, WindowStateListener, WindowFocusListener
{
juit单元测试依赖
<!--可以直接用@test写在方法上,不用main方法,就能直接运行了-->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>
ORM对象关系映射 :就是实体类和数据库字段互相对应