目录
一、Java Web基础
基础概念
web开发:
- web,网页的意思
- 静态web
- HTML,CSS
- 提供给所有人看的数据(不会变化)
- 动态web(JavaWeb)
- 提供给所有人的数据会因人而异,数据会不同(时间,用户…)
- 技术栈:PHP,ASP,Servlet/JSP
Web应用程序
可以提供浏览器访问的程序
- 比如:a.html 文件可以被外界访问,对外界提供服务
- 利用URL用户可以去访问一个页面或资源一定在某台计算机上
- 统一的Web资源会被放在同一文件夹下(Web应用程序—>Tomcat(服务器))
- Web应用程序编写完后,需要一个服务器来统一管理来实现提供给外界访问
Web应用程序组成部分:
- HTML,CSS,JS
- JSP,Servlet,ASP,PHP
- Java程序
- Jar包
- 配置文件(Properties)
静态Web
静态Web页面的缺点:
- Web页面无法动态更新,所有用户看到的都是一个页面
- 伪动态(JS,VBScript)
- 它无法与数据库交互
动态Web
可以实现Web页面展示效果因人而异
可以与数据库交互(JDBC),数据持久化
动态Web页面的缺点:
- 加入服务器的动态web资源出现错误,需要重新编写后台程序(停机维护)
Web服务器
服务器是一种被动的操作,用来处理用户一些请求和给用户一些响应信息
IIS:微软的
Tomacat:
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。Tomcat最新版本为10.0.23
ky下载Tomcat,解压后用bin中startup.sh 启动,开启后可以打开网址https://localhost:8080
可以配置启动的端口号
- tomcat的默认端口号:8080
- MySQL:3306
- http:80
- https:443
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
高难度面试题:
谈谈网站是如何进行访问的?
-
输入一个域名,回车
-
检查本机的C:\WINDOWS\System32\drivers\etc\hosts配置文件下有没有这个域名映射
-
有:直接返回对应的IP地址,有我们需要的web程序则可以直接访问
127.0.0.1 localhost
-
没有:去DNS服务器上去找
-
发布一个Web网站
-
将自己写的网站放到服务器中指定的web应用的文件夹(webapps)下,就可以访问了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zu2NrqVE-1671708205271)(C:\Users\海潇\Desktop\屏幕截图 2022-10-22 202355.png)]
可以复制一个ROOT文件(删去可以删掉的),开启服务器后可以打开网址https://localhost:8080/MyWeb/
网站应有的结构:
--webapps : Tomcat服务器的web目录
-ROOT
-MyWeb : 网站的目录名
- WEB-INF
-classes : java程序
-lib : web应用所依赖的jar包
-web.xml : 网站配置文件
- index.html 默认首页
- static
-css
-style.css
-js
-img
....
Http协议
什么是Http
Http是一个简单的请求–响应协议(超文本传输协议),它通常运行在TCP之上
- 80端口
- Https:443端口(”s“是指安全的意思)
我在Microsoft Edge浏览器上面抓包不是很懂,不能抓到具体有用的数据,所有我看了其他大佬的博客,链接放在这:HTTP协议超级详解 - 爱文飞翔 - 博客园 (cnblogs.com)
请求行
- 请求行中请求方式:GET
- 请求方式:get,post,head,delete,put…
- get:请求能够携带的参数较少,大小有限,会在浏览器的URL地址栏显示数据内容,不安全,但高效
- post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效
消息头
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持那种编码格式
Accept-Language:它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成时断开还是保持连接
…
响应状态码
200:请求响应成功
3xx:请求重定向
- 重定向:重新到我给你的新位置去
404:找不到资源
5xx:服务器代码错误 500 502(网关错误)
二、Maven项目
Maven的意义
能够自动帮助导入和配置jar包,就是个导jar包的管理工具
Maven的核心思想:约定大于配置
修改镜像(了解)
- 镜像:mirrors
- 加速国内反应速度
在D:\apache-maven-3.8.6\conf\settings.xml 下修改,以下就是这样
阿里云镜像
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
</mirror>
本地仓库
建立一个本地仓库,自己创建一个文件夹
<localRepository>D:\apache-maven-3.8.6\maven-repo</localRepository>
eclipse配置maven本地仓库:
eclipse配置Maven本地仓库_罗汉爷的博客-CSDN博客
将tomcat加入到eclipse(高版本需要操作步骤1【添加包】):
- 增加server选项:eclipse中没有server选项的解决办法(详细)_果冻奶酪的博客-CSDN博客_eclipse下面没有server
- 将romcat加入:超详细Eclipse配置Tomcat配置教程_极小白的博客-CSDN博客_eclipse配置tomcat
Maven项目配置(重点)
单击maven文件选中properties配置Java Build Path 和Project Facets
注意:
- 一定要jdk的版本在两个配置地方一致
- Dynamic Web Module版本高的话一定要使用jdk1.8以上
- 记得在Project Facets和自己的tomcat连接上
创建maven父子工程(了解)
-
先创建一个Maven Project项目
-
创建一个maven Module项目,它可以充当问子工程(在其下面配置pom.xml文件子项目中就可以不用再配置了,子工程可以用父工程的)
-
创建maven父子工程:使用eclipse创建maven的父子工程_NancyWu_LuckyGirl的博客-CSDN博客_eclipse创建父子工程
-
关于The parent project must have a packaging type of POM问题:新建maven module时报错 The parent project must have a packaging type of POM_frankliu01的博客-CSDN博客
maven环境优化(了解)
现在的eclipse创建出来的web.xml不需要改动,servlet映射等都会自动生成,以下是老版本可能需要的web.xml文件的优化
- 修改web.xml为最新的:maven里面web.xml的配置文件_weather_d的博客-CSDN博客_mavenwebxml文件
三、Servlet文件
Servlet简介
Servlet就是sun公司开发动态web的一门技术
开发Servlet程序两个步骤:
- 编写一个类
- 把开发好的Java类部署到web服务器中
HelloServlet的创建
1.创建servlet文件
在src/main/Java目录下创建servlet文件,命名为HelloServlet并继承HttpServlet(建议使用jakarta.servlet.http.HttpServlet,现在版本高了就搞出了Jakarta)
以下导入的是javax(图找不到了)
package servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("holleservlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}
}
2.编写servlet映射(eclipse下不用写,会自动生成)
在web.xml下编写
<!--注册servlet -->
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet的请求路径 -->
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<!--url名字 -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
3.添加tomcat服务器(之前就要配置好,在servers下引入你想加载的maven文件就行了)
4.测试
servlet生命周期
实例化----->初始化------->服务------->销毁
servlet特点
- servlet是单例多线程
- 一个servlet实例只会执行一次无参构造器与init()方法,并在第一次访问时进行
- 用户没提交一次对当前servlet的请求,就会执行一次service()方法
- 只会执行一次destroy()方法,在服务器停止时执行
- 默认情况下,servlet在web容器启动时不会被实例化的
关于mapping问题
-
一个servlet可以对应一个映射路径
-
一个servlet可以对应多个映射路径
<servlet-mapping> <servlet-name>Helloservlet</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Helloservlet</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping>
-
一个servlet可以对应通用映射路径
<servlet-mapping> <servlet-name>Helloservlet</servlet-name> <url-pattern>/hello/*</url-pattern> </servlet-mapping>
-
默认请求路径
<servlet-mapping> <servlet-name>Helloservlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
-
自定义后缀实现请求
<servlet-mapping> <servlet-name>Helloservlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> //*前面什么都不加,可以在浏览器上输入如何前缀
Servlet Context
web容器在启动的时候,它会为每个web程序都创建一个对应的servletcontext对象
servletcontext作用:共享数据,我在一个servlet保存的数据,可以在另一个servlet中拿到
- 先创建一个放置数据的类
package com.hai;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Servlet implementation class HelloServlet
*/
public class HelloServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public HelloServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//System.out.println("heelo");
//response.getWriter().append("Served at:heelo ").append(request.getContextPath());
ServletContext s = this.getServletContext();
String username = "hai";
s.setAttribute("username", username);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
- 再创一个servlet类
package com.hai;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Servlet implementation class NewServlet
*/
public class NewServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NewServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
ServletContext s = this.getServletContext();
String username = (String) s.getAttribute("username");
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");;
response.getWriter().print("名字:"+username);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
-
测试结果
-
先打开NewServlet类,则
-
先打开HelloServlet类,后打开NewServlet类,则
-
Servlet Context的应用
- 获取初始化参数
<!-- 配置一些web应用初始化参数 -->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
<description></description>
<display-name>ServletDome</display-name>
<servlet-name>ServletDome</servlet-name>
<servlet-class>com.hai.ServletDome</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDome</servlet-name>
<url-pattern>/jp</url-pattern>
</servlet-mapping>
<servlet>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext s = this.getServletContext();
String url = s.getInitParameter("url");
response.getWriter().print(url);
}
- 请求转发
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext s = this.getServletContext();
RequestDispatcher requestDispatcher = s.getRequestDispatcher("/jp"); //转发的请求路径
requestDispatcher.forward(request, response); //调用forward方法实现请求
}
- 读取资源文件
Properties文件
-
在src/main/java目录下新建Properties
-
在src/main/resource目录下新建Properties
发现:都被打包在class下生成,统称为class path
思路:都需要一个流(Stream类)
Properties文件下写的内容:
username = root
password = 123456
servlet文件下写的内容:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/class/db.properties");
Properties properties = new Properties();
properties.load(is);
String user = properties.getProperty("username");
String pwd = properties.getProperty("password");
response.getWriter().print(user+":"+pwd);
}
HttpServletResponse
web服务器接收到客户端的HTTP请求,针对这个请求,分别创建一个代表的HttpServletRequest对象,代表响应的一个HttpServletResponse对象
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端响应一些信息:找HttpServletResponse
简单分类
负责向浏览器发送数据的方法
public ServletOutputStream getOutputStream() throws IOException;
public PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
public void setCharacterEncoding(String charset);
public void setContentLength(int len);
public void setContentLengthLong(long len);
public void setContentType(String type);
public void setBufferSize(int size);
响应状态码(也是servlet会向浏览器发送头的方法)
default public Supplier<Map<String, String>> getTrailerFields() {
return null;
}
/*
* Server status codes; see RFC 2068.
*/
/**
* Status code (100) indicating the client can continue.
*/
public static final int SC_CONTINUE = 100;
/**
* Status code (101) indicating the server is switching protocols according to Upgrade header.
*/
public static final int SC_SWITCHING_PROTOCOLS = 101;
/**
* Status code (200) indicating the request succeeded normally.
*/
public static final int SC_OK = 200;
/**
* Status code (201) indicating the request succeeded and created a new resource on the server.
*/
public static final int SC_CREATED = 201;
/**
* Status code (202) indicating that a request was accepted for processing, but was not completed.
*/
public static final int SC_ACCEPTED = 202;
/**
* Status code (203) indicating that the meta information presented by the client did not originate from the server.
*/
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
/**
* Status code (204) indicating that the request succeeded but that there was no new information to return.
*/
public static final int SC_NO_CONTENT = 204;
/**
* Status code (205) indicating that the agent <em>SHOULD</em> reset the document view which caused the request to be
* sent.
*/
public static final int SC_RESET_CONTENT = 205;
/**
* Status code (206) indicating that the server has fulfilled the partial GET request for the resource.
*/
public static final int SC_PARTIAL_CONTENT = 206;
/**
* Status code (300) indicating that the requested resource corresponds to any one of a set of representations, each
* with its own specific location.
*/
public static final int SC_MULTIPLE_CHOICES = 300;
/**
* Status code (301) indicating that the resource has permanently moved to a new location, and that future references
* should use a new URI with their requests.
*/
public static final int SC_MOVED_PERMANENTLY = 301;
/**
* Status code (302) indicating that the resource has temporarily moved to another location, but that future references
* should still use the original URI to access the resource.
*
* This definition is being retained for backwards compatibility. SC_FOUND is now the preferred definition.
*/
public static final int SC_MOVED_TEMPORARILY = 302;
/**
* Status code (302) indicating that the resource reside temporarily under a different URI. Since the redirection might
* be altered on occasion, the client should continue to use the Request-URI for future requests.(HTTP/1.1) To represent
* the status code (302), it is recommended to use this variable.
*/
public static final int SC_FOUND = 302;
/**
* Status code (303) indicating that the response to the request can be found under a different URI.
*/
public static final int SC_SEE_OTHER = 303;
/**
* Status code (304) indicating that a conditional GET operation found that the resource was available and not modified.
*/
public static final int SC_NOT_MODIFIED = 304;
/**
* Status code (305) indicating that the requested resource <em>MUST</em> be accessed through the proxy given by the
* <code><em>Location</em></code> field.
*/
public static final int SC_USE_PROXY = 305;
/**
* Status code (307) indicating that the requested resource resides temporarily under a different URI. The temporary URI
* <em>SHOULD</em> be given by the <code><em>Location</em></code> field in the response.
*/
public static final int SC_TEMPORARY_REDIRECT = 307;
/**
* Status code (400) indicating the request sent by the client was syntactically incorrect.
*/
public static final int SC_BAD_REQUEST = 400;
/**
* Status code (401) indicating that the request requires HTTP authentication.
*/
public static final int SC_UNAUTHORIZED = 401;
/**
* Status code (402) reserved for future use.
*/
public static final int SC_PAYMENT_REQUIRED = 402;
/**
* Status code (403) indicating the server understood the request but refused to fulfill it.
*/
public static final int SC_FORBIDDEN = 403;
/**
* Status code (404) indicating that the requested resource is not available.
*/
public static final int SC_NOT_FOUND = 404;
/**
* Status code (405) indicating that the method specified in the <code><em>Request-Line</em></code> is not allowed for
* the resource identified by the <code><em>Request-URI</em></code>.
*/
public static final int SC_METHOD_NOT_ALLOWED = 405;
/**
* Status code (406) indicating that the resource identified by the request is only capable of generating response
* entities which have content characteristics not acceptable according to the accept headers sent in the request.
*/
public static final int SC_NOT_ACCEPTABLE = 406;
/**
* Status code (407) indicating that the client <em>MUST</em> first authenticate itself with the proxy.
*/
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
/**
* Status code (408) indicating that the client did not produce a request within the time that the server was prepared
* to wait.
*/
public static final int SC_REQUEST_TIMEOUT = 408;
/**
* Status code (409) indicating that the request could not be completed due to a conflict with the current state of the
* resource.
*/
public static final int SC_CONFLICT = 409;
/**
* Status code (410) indicating that the resource is no longer available at the server and no forwarding address is
* known. This condition <em>SHOULD</em> be considered permanent.
*/
public static final int SC_GONE = 410;
/**
* Status code (411) indicating that the request cannot be handled without a defined
* <code><em>Content-Length</em></code>.
*/
public static final int SC_LENGTH_REQUIRED = 411;
/**
* Status code (412) indicating that the precondition given in one or more of the request-header fields evaluated to
* false when it was tested on the server.
*/
public static final int SC_PRECONDITION_FAILED = 412;
/**
* Status code (413) indicating that the server is refusing to process the request because the request entity is larger
* than the server is willing or able to process.
*/
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
/**
* Status code (414) indicating that the server is refusing to service the request because the
* <code><em>Request-URI</em></code> is longer than the server is willing to interpret.
*/
public static final int SC_REQUEST_URI_TOO_LONG = 414;
/**
* Status code (415) indicating that the server is refusing to service the request because the entity of the request is
* in a format not supported by the requested resource for the requested method.
*/
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
/**
* Status code (416) indicating that the server cannot serve the requested byte range.
*/
public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
/**
* Status code (417) indicating that the server could not meet the expectation given in the Expect request header.
*/
public static final int SC_EXPECTATION_FAILED = 417;
/**
* Status code (500) indicating an error inside the HTTP server which prevented it from fulfilling the request.
*/
public static final int SC_INTERNAL_SERVER_ERROR = 500;
/**
* Status code (501) indicating the HTTP server does not support the functionality needed to fulfill the request.
*/
public static final int SC_NOT_IMPLEMENTED = 501;
/**
* Status code (502) indicating that the HTTP server received an invalid response from a server it consulted when acting
* as a proxy or gateway.
*/
public static final int SC_BAD_GATEWAY = 502;
/**
* Status code (503) indicating that the HTTP server is temporarily overloaded, and unable to handle the request.
*/
public static final int SC_SERVICE_UNAVAILABLE = 503;
/**
* Status code (504) indicating that the server did not receive a timely response from the upstream server while acting
* as a gateway or proxy.
*/
public static final int SC_GATEWAY_TIMEOUT = 504;
/**
* Status code (505) indicating that the server does not support or refuses to support the HTTP protocol version that
* was used in the request message.
*/
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
}
常见应用
- 向浏览器输出信息
- 下载文件
- 验证码功能
- 实现重定向
1.下载文件
- 要获取下载文件的路径
- 下载的文件名
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 要获取下载文件的路径
// String rp = this.getServletContext().getRealPath("/15196900621605176.jpg");
String rp = "C:\\Users\\hai\\Desktop/15196900621605176.jpg";
// 下载的文件名
String fileName = rp.substring(rp.lastIndexOf("\\")+1);
// 设置想办法让浏览器能够支持下载我们需要的东西,中文文件用URLEncoder.encode(fileName,"UTF-8")编码,否则可能会乱码
response.setHeader("Content-disposition","attachment;filename="+fileName);
// 获取下载文件的输入流
FileInputStream in = new FileInputStream(rp);
// 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 获取OutputStream对象
ServletOutputStream outputStream = response.getOutputStream();
// 将FileOutputStream流写入到buffer缓冲区
while((len=in.read(buffer))>0) {
outputStream.write(buffer, 0, len);
}
in.close();
outputStream.close();
// 使用OutputStream将缓冲区中的数据输出到客户端
}
测试结果:
2. 验证码功能
验证怎么来的?
- 前端实现
- 后端实现,需要用到Java的图片类,生成一个图片
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如何让浏览器3秒自动刷新一次
response.setHeader("refresh","5");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D g = (Graphics2D) image.getGraphics();
//设置图片的背景颜色
g.setColor(Color.white);
g.fillRect(0, 0, 80, 20);
//给图片写数据
g.setColor(Color.blue);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
//告诉浏览器这个请求用图片的方式打开
response.setContentType("image/jpeg");
//网站存在缓存,不让它缓存
response.setContentType("expires");
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
//把图片给浏览器
ImageIO.write(image, "jpg",response.getOutputStream());
}
private String makeNum() { //生成随机数
Random random = new Random();
String num = random.nextInt(9999999)+ "";
StringBuffer sb = new StringBuffer();
//保证7位数随机数
for (int i=0;i<7-num.length();i++) {
sb.append("0");
}
num = sb.toString()+ num;
return num;
}
测试结果:
3.实现重定向
一个web资源收到客户端请求后会告诉客户端去访问另一个web资源,通过另一个资源访问
response.sendRedirect("/jjj/yanzheng");
//结果会跳转到yanzheng这个界面
//原理:
//response.setHeader("Location", "/jjj/yanzheng");
//response.setStatus(302);
面试题:请你聊聊重定向和转发的区别?
-
相同点:
页面都会实现跳转
-
不同点:
- 请求转发的时候 URL不会发生变化
- 重定向时,URL地址栏会发生变化
HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过HTTP协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过HttpServletRequest方法来获得客户端的信息
- 获取前端的信息,转发
<!DOCTYPE HTML><html lang="en"><head>
<meta charset="UTF-8">
<title>Apache Tomcat Examples</title>
</head>
<body>
<form action="http://localhost:8080/jjj/dl" method="post">
<p>
用户名:<input type="text" name="username">
密码: <input type="password" name="password">
</p>
<input type="submit">
</form>
</body>
</html>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决浏览器中文乱码问题
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取前端的信息
String username = request.getParameter("username");
String password = request.getParameter("password");
String[] hoobys = request.getParameterValues("hoobys");
//重定向
// response.sendRedirect("/jjj/new.jsp");
//转发
request.getRequestDispatcher("/new.jsp").forward(request, response);
}
四、Cookie\Session
会话:用户打开一个浏览器,点击了很多超链接,访问了多个web资源,然后关闭浏览器,这个过程叫做会话
有状态会话:客户端来过,下次再来的时候,浏览器知道来过
一个网站怎么证明你来过?
客户端-------------->服务端
- 服务端给客户端一个信件,客户端下次访问时带上信件就可以了:Cookie
- 服务端登记你来过了,下次来的时候就会匹配你:Session
保存会话的两种技术
Cookie
- 客户端技术(响应,请求)
Session
- 服务器技术,可以把信息和数据放在Session中
常见问题:网站登录后,下次就不用再登陆了,第二次直接访问就行了
Cookie[] cookies = request.getCookies(); //获得Cookie
cookie.getName() //获得cookie中的key
cookie.getValue()//获得cookie中的value
Cookie time = new Cookie("time",System.currentTimeMillis()+""); //新建一个cookie
time.setMaxAge(24*60*60); //设置cookie有效期
response.addCookie(time); //响应一个cookie给客户端
cookie:一般会保存在本地的用户目录下appdata
一个网站cookie是否有上限?
- 一个cookie只能保存一个信息
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
- cookie大小有限制4kb
- 300个cookie浏览器上限
cookie删除
- 不设置有效期,关闭浏览器,自动失效
- 设置有效期为0
//再创建一个cookie,名字要和要删除的cookie名一致
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = new Cookie("time",System.currentTimeMillis()+"");
cookie.setMaxAge(0);
response.addCookie(cookie);
}
Session(重点)
什么是Session:
- 服务器会给每一个用户(浏览器)创建一个Session对象
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
- 用户登录之后,整个网站它都可以访问------>保存用户信息,保存购物车信息
Cookie\Session的区别:
- Cookie是把用户的数据写给用户浏览器,浏览器保存
- Session把用户的数据写到用户独占Session中,服务器端保存
使用场景:
- 保存登录用户信息
- 购物车信息
- 再整个网站中经常使用的数据会放在Session
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//得到session
HttpSession session = request.getSession();
//给session中存东西
session.setAttribute("name", "hai");
//获得session的id
String id = session.getId();
//判断session是不是新创建
if(session.isNew()) {
response.getWriter().write("创建成功,id="+id);
}else {
response.getWriter().write("创建已经存在了,id="+id);
}
//注销后会立马生成一个新的
HttpSession session = request.getSession();
session.removeAttribute("name");
//手动注销session
session.invalidate();
//获得session中数据
HttpSession session = request.getSession();
Object attribute = session.getAttribute("name");
response.getWriter().write(attribute.toString());
Session失效时间,在web.xml文件中配置
<!-- session默认失效时间 -->
<session-config>
<!-- session默认失效时间为1分钟 -->
<session-timeout>1</session-timeout>
</session-config>
五、JSP文件
JSP和HTML的区别
- jsp页面中可以嵌入Java代码,为用户提供动态数据
- html页面只会给用户提供静态的数据
本质:
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet,jsp最终也会被转换为一个Java类,它也是个servlet
在jsp页面中:只要是Java代码就会原封不动的输出;如果是HTML代码则会被转换为:
out.write("<html>\r\n"); //以这样的格式输出到前端
JSP基本使用:JSP的基本使用总结_Nice2cu_Code的博客-CSDN博客_jsp的使用场景
JSP基础语法
四个依赖:
<dependencies>
<!-- Servlet 依赖 -->
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<!-- JSP 依赖 -->
<!-- https://mvnrepository.com/artifact/jakarta.servlet.jsp/jakarta.servlet.jsp-api -->
<dependency>
<groupId>jakarta.servlet.jsp</groupId>
<artifactId>jakarta.servlet.jsp-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- JSTL 表达式的依赖 -->
<!-- https://mvnrepository.com/artifact/org.glassfish.web/jakarta.servlet.jsp.jstl -->
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<version>2.0.0</version>
</dependency>
<!-- standard标签库 -->
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
JSP常用<>
- <% %>叫做脚本片段,在jsp页面可以有多个脚本片段,但是多
个脚本片段之间要保证结构完整。- <%! %>称作声明,其中写的内容将来会直接翻译在Servlet类中,因为我们可以在类中定义方法和属性以及全局变量,所以我们可以在<%! %>中声明方法、属性、全局变量。
- <%= %>称作jsp表达式,用于将已经声明的变量或者表达式输出到网页上面。
- 直接写在jsp页面中的代码称作模板元素,将来会Servlet的Service方法中的out.write(“___”)中,作为输出内容。
JSP中的<>应用:JSP页面中<%!%>与<%%>与<%=%>
<% %>
<%= %>
<%! %>
<%-- 注释 --%>
<%-- jsp的注释不会出现在web页面上,HTML的注释会 --%>
JSP表达式
<%--jsp表达式
作用:用来将程序的输出到客户端
<%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>
JSP脚本片段
<%-- jsp脚本片段 --%>
<%
int sum=0;
for(int i=0;i<=10;i++){
sum+=i;
} %>
out.print(sum);
脚本混实现
<%-- 脚本在实现 --%>
<%
for(int i=0;i<5;i++){
%>
<h3>hello <%=i%></h3>
<%
}
%>
JSP声明(实现全局变量)
上面的代码都是写在方法下的,jsp也支持写在全局下
<%!
public static void hai(){
System.out.print("你猜猜我是谁?");
}
%>
JSP声明:
会被编译到jsp生成的Java类中,其他的会被生成到_jspService方法中
在jsp中嵌入Java代码即可
自定义一个错误页面
web.xml下的配置:
<error-page>
<error-code>404</error-code>
<location>/Error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/Error/500.jsp</location>
</error-page>
创建一个jsp文件:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<title>500</title>
</head>
<body>
<img alt="500" src="../image/500.png">
</body>
</html>
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<title>404</title>
</head>
<body>
<img alt="" src="../image/404.png">
</body>
</html>
测试结果:
JSP指令
JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。
推荐菜鸟:JSP 指令
<%-- 显示声明这是一个错误页面 --%>
<%@ page isErrorPage="true" %>
<%@ page ... %>
<%-- 把文件包含进去 --%>
<%@ include file="" %>
...
九大内置对象
- Page Context (存东西)
- Request (存东西)
- Response
- Session (存东西,例如:购物车)
- Application [Servlet Context] (存东西,例如:聊天框)
- config [Servlet Config]
- out
- page
- exception
<%-- 内置对象 --%>
<%
request.setAttribute("name0", "hai0");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name1", "hai1");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name2", "hai2");//保存的数据只在服务器中有效,从打开服务器到关闭服务器
pageContext.setAttribute("name3", "hai3");//保存的数据只在一个页面中存在
%>
<%--脚本片段中的代码,会被原封不动的生成到。JSP.java --%>
<%
//从pageContext取出,通过寻找的方式来
//从底层到高层(作用域):page-->request-->session--application
String name3=(String)pageContext.findAttribute("name3");
String name2=(String)pageContext.findAttribute("name2");
String name1=(String)pageContext.findAttribute("name1");
String name0=(String)pageContext.findAttribute("name0");
%>
<h1>显示:</h1>
<hr>
<p>${name3}<br>
${name2}<br>
${name1}<br>
${name0}<br>
${name4}<br> <%--不存在--%>
</p>
JSP标签、JSTL标签、EL表达式(重点)
EL表达式:${}
- 获取数据
- 执行运算
- 获取web开发的常用对象
JSP标签
<%--http://localhost:8080/rr/New2.jsp?name=hai&age=20 --%>
<jsp:forward page="/New2.jsp">
<jsp:param value="name" name="hai"/>
<jsp:param value="age" name="20"/>
</jsp:forward>
===============================================
<jsp:include page="/New2.jsp"></jsp:include>
<h1>hello</h1>
<jsp:include page="/New2.jsp"></jsp:include>
上述代码会在浏览器上从上到下显示:
New.jsp的内容
hello
New.jsp的内容
JSTL标签
JSTL标签库的使用就是为了弥补HTML标签的不足,它自定义许多标签,可以供我们使用,标签的功能和Java代码一样
JSTL标签库:JSP 标准标签库(JSTL) | 菜鸟教程 (runoob.com)
- 核心标签(掌握部分即可)
<%--引入JSTL核心标签库 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
if测试:
<hr>
<form action="" mathod="get">
<input type="text" name="username" value="${param.username}">
<input type="submit">
</form>
<c:if test="${param.username=='hai'}" var="ture">
<c:out value="hai欢迎您!"></c:out>
</c:if>
<c:out value="${ture}"></c:out>
测试结果:
set/chhose测试:
<%--set 定义一个变量 choose相当于switch --%>
<c:set var="s" value="85"></c:set>
<c:choose>
<c:when test="${s>=90}">
优秀
</c:when>
<c:when test="${s>=80}">
一般优秀
</c:when>
<c:when test="${s>=70}">
优良
</c:when>
<c:when test="${s>=60}">
及格
</c:when>
</c:choose>
c:forEach测试:
<%--c:forEach 遍历 --%>
<%
ArrayList<Object> p = new ArrayList<>();
p.add(0, "hai0");
p.add(1, "hai1");
p.add(2, "hai2");
p.add(3, "hai3");
p.add(4, "hai4");
request.setAttribute("list",p);
%>
<%--
items 要遍历的对象
var 每次遍历的变量
begin 从哪开始
end 到那结束
step 步长
--%>
<c:forEach var="p" items="${list}">
<c:out value="${p}"></c:out>
</c:forEach>
<hr>
<c:forEach var="p" items="${list}" begin="1" end="3" step="2">
<c:out value="${p}"></c:out>
</c:forEach>
测试结果:
- 格式化标签
- SQL 标签
- XML 标签
- JSTL 函数
六、MVC三层架构
什么是MVC: Model View Controller 模型、视图、控制器
- 早些年:用户直接访问控制层,控制层就可以直接操作数据库;
servlet ----> CRUD ----> 数据库
servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码
程序员调用 <------JDBC<-------MySQL Oracle SQLserver …
Model(模型)
-
业务处理:业务逻辑(Service)
-
数据持久层
View(视图)
- 展示数据
- 提供链接发起servlet请求(a,from,img…)
Controller(控制器)
- 接收用户请求:(req:请求数据、Session信息)
- 交给业务层处理对应的代码
- 控制视图的跳转
七、Filter(过滤器)
Filter(过滤器):用来过滤网站的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sNkzLRVe-1671708205280)(D:\image\v2-r.png)]
Filter作用:
- 解决文件乱码问题
- 登陆验证
创建Filter步骤:
- 在pom.xml下连接到数据库
<!-- 连接mysql数据库 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
- 编写Filter(解决文件乱码问题)
public class NewFilter extends HttpFilter implements Filter {
public NewFilter() {
super();
// TODO Auto-generated constructor stub
}
//初始化:web服务器启动它就初始化了
public void destroy() {
}
//Chain:链
/*
1.过滤中的所有代码,在过滤特定请求的时候都会执行
2.必须要让过滤器继续执行
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
System.out.println("doFilter执行:");
chain.doFilter(request, response); //放行doFilter,让我们的请求继续走,如果不写就停下了
}
//销毁
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("销毁NewFilter");
}
}
- 在web.xml中配置Filter
<filter>
<display-name>NewFilter</display-name>
<filter-name>NewFilter</filter-name>
<filter-class>com.hai.NewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>NewFilter</filter-name>
<url-pattern>/NewFilter</url-pattern> <!-- /*可以指代文件下所有页面 -->
</filter-mapping>
八、Listener(监听器)
实现一个监听器的接口:(有n种)
//统计网站在线人数 : 统计session
public class NewListener extends HttpServlet implements HttpSessionListener {
public NewListener() {
super();
// TODO Auto-generated constructor stub
}
//创建session监听 : 看你的一举一动
//一旦创建一个session就会触发一次这个监听
public void sessionCreated(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer attribute = (Integer)servletContext.getAttribute("OnlineCount");
if(attribute==null) {
attribute = new Integer(1);
}else {
int intValue = attribute.intValue();
attribute = new Integer(1+intValue);
}
servletContext.setAttribute("OnlineCount", attribute);
}
//销毁session监听
//一旦销毁一个session就会触发一次这个监听
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer attribute = (Integer)servletContext.getAttribute("OnlineCount");
if(attribute==null) {
attribute = new Integer(0);
}else {
int intValue = attribute.intValue();
attribute = new Integer(intValue-1);
}
servletContext.setAttribute("OnlineCount", attribute);
}
}
/*
session销毁
1. 手动销毁 se.getSession().invalidate();
2. 自动销毁 在web.xml中配置,如下:
补充:Integer类 :Integer详解_白啊白啊的博客-CSDN博客_integer运算
在web.xml配置
<listener>
<listener-class>com.hai.NewListener</listener-class>
</listener>
<session-config>
<session-timeout>1</session-timeout>
</session-config>
测试
<div>
<h3>当前有</h3>
<span style="color:red"><%=this.getServletConfig().getServletContext().getAttribute("OnlineCount")%></span>
<h3>个人在线</h3>
</div>
测试结果:
九、JDBC回顾
什么是JDBC:等价于中间商,将application和数据库连接起来
连接MySQL (简单的实现JDBC)
// TODO Auto-generated method stub
String url = "jdbc:mysql://127.0.0.1:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//连接数据库
Connection connection = DriverManager.getConnection(url,user,password);
//向数据库发送SQL的对象statement
Statement statement = connection.createStatement();
//编写SQL
String sql = "select * from users;";
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()) {
System.out.print("id="+resultSet.getObject("id")+"\t");
System.out.print("name="+resultSet.getObject("name")+"\t");
System.out.print("password="+resultSet.getObject("password")+"\t");
System.out.print("email="+resultSet.getObject("email")+"\t");
System.out.print("birthday="+resultSet.getObject("birthday")+"\t");
System.out.println();
}
//关闭,先开后关
resultSet.close();
statement.close();
connection.close();
}
用预编译(PreparedStatement)编写
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String url = "jdbc:mysql://127.0.0.1:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//连接数据库
Connection connection = DriverManager.getConnection(url,user,password);
//向数据库发送SQL的对象statement
//编写SQL
String sql = "insert into users(id,name,password,email,birthday)value(?,?,?,?,?);";
//预编译
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1, 4);
statement.setString(2, "五六七");
statement.setString(3, "123456");
statement.setString(4, "null");
statement.setDate(5,new Date(new java.util.Date().getDate()));
int update = statement.executeUpdate();
if(update>0) {
System.out.println("成功注册");
}
//关闭,先开后关
statement.close();
connection.close();
}
}
用Servlet编写JDBC
- Maven文件驱动MySQL需要的包
<!-- 连接mysql数据库 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 以及java.sql包和jakarta.sql包 -->
- 编写一个servlet文件
public void init() {
String url = "jdbc:mysql://127.0.0.1:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
//加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//连接数据库
try {
connection = DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String birthday = request.getParameter("birthday");
String sql = "insert into users(id,name,password,email,birthday)value(?,?,?,?,?);";
//预编译
PreparedStatement statement;
try {
statement = connection.prepareStatement(sql);
statement.setString(1, id);
statement.setString(2, username);
statement.setString(3, password);
statement.setString(4, email);
statement.setString(5,birthday);
int update = statement.executeUpdate();
if(update>0) {
System.out.println("成功注册");
}
//关闭,先开后关
statement.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
response.sendRedirect("/bbb/login.jsp");
}
- 编写一个jsp文件(实现在浏览器上看见)
<form action="http://localhost:8080/bbb/Longin" mathod="post">
<h3>注册:</h3>
<p>
ID:<input type="number" name="id">
名字:<input type="text" name="username"><br>
密码:<input type="password" name="password">
Email:<input type="text" name="email"><br>
生日:<input type="date" name="date"><br>
<input type="submit">
- 编辑web.xml
<servlet>
<description></description>
<display-name>LonginServlet</display-name>
<servlet-name>LonginServlet</servlet-name>
<servlet-class>com.hai.LonginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LonginServlet</servlet-name>
<url-pattern>/Longin</url-pattern>
</servlet-mapping>
测试:
“提交”后跳转到的新页面【response.sendRedirect(“/bbb/login.jsp”); 】:
后台显示的内容: