JavaWeb
基本概念
web(网页)开发
-
静态web
-
html,css
-
提供给所有人看的数据始终不会变化
-
-
动态web
-
-
常用的技术栈:Servlet
-
,ASP,PHP
-
在java中,动态web资源开发的技术统称为JavaWeb
web应用程序
web应用程序:可以提供浏览器访问的程序
-
a.html,b.html....多个web资源,这些web资源可以被外界访问,对外界提供服务(ip+port定位到具体某个电脑的某个进程)
-
你们能访问到任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上(URL)
-
这个统一的web资源会被放在同一个文件夹下。
web应用程序的运行依赖于一个Tomcat(服务器)
-
一个web应用由多部分组成(静态web/动态web)
-
html,css,js
-
jsp,seevlet
-
Java程序
-
jar包
-
配置文件(Properties)
-
-
web应用程序编写完毕后,若想提供给外界访问:需要一个服务器来统一管理
静态web
*.htm, *.html网页的后缀(如果服务器上一直存在这些东西,我们可以直接进行读取)
静态web存在的缺点
-
web页面无法动态更新,所有用户看到的都是同一个页面
-
伪动态(为了静态web动起来的技术)
1、JavaScript(实际开发中,用的最多)
2、VBScript
-
-
它无法和数据库交互(数据无法持久化,用户无法交互)
动态web
页面会动态展示:“web的页面展示的效果因人而异”
动态web缺点
-
假如服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序(停机维护),重新发布
动态web优点
-
web页面可以动态更新,所有用户看到的都不是同一个页面
-
它可以和数据库交互(数据持久化:注册/用户信息/商品信息)
技术讲解
ASP
-
微软做的,国内最早流行的
-
在HTML中嵌入了VB的脚本,ASP+COM
-
在ASP开发中,基本一个页面都有几千行的业务代码,页面及其混乱——> 维护成本高
-
基于C#语言
-
IIS
<h1>
<h1><h1>
<h1>
<h1>
<h1>
<h1>
<%
System.out.println("hello")
%>
<h1>
<h1>
<h1><h1>
<h1>
php
-
php开发速度很快,功能很强大,跨平台,代码很简单(70%网站都用)
-
局限性:无法承载大访问量的情况
JSP/Servlet(JSP的本质是Servlet)
B/S架构:浏览和服务器
C/S架构:客服端和服务器
-
sun公司(被Oracle公司收购了)主推的B/S架构
-
基于java语言的(所有的大公司,或者一些开源的组件,都是用java写的)
-
可以承载三高问题(高并发,高可用,高性能)带来的影响
-
语法非常像ASP(加强市场竞争度,容易学ASP的人转JSP)
.....
web服务器
服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息
IIS(微软的)
-
跑ASP本土的一些程序——Windows中自带的
Tomcat
基本概念(面对百度编程)
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用
Tomcat 实际上运行JSP 页面和Servlet。
目前Tomcat最新版本为9.0.41
.........
下载Tomcat
-
安装 or 解压
-
了解配置文件及目录结构
-
东西的作用
Tomcat
安装
官网下载,解压
文件夹作用
启动/关闭Tomcat
-
命令行直接启动
-
bin目录下的
-
startup.bat
-
shutdown.bat
-
访问测试
配置设置
-
可以配置启动的端口号
Tomcat的默认端口号:8080
mysql:3306
http:80
https:443
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" /> -
可以配置主机的名称
默认的主机名为:localhost——>127.0.0.1
默认的网站应用存放的位置为:webapps
不能动
<Engine name="Catalina" defaultHost="localhost">
改动位置
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
高级面试题
请你谈一谈网站是如何进行访问的?
-
输入一个域名,回车
-
检查本机的 C:\Windows\System32\drivers\etc\hosts 配置文件下有没有这个域名的映射:
-
有:直接返回对应的ip地址,这个地址中,有我们需要访问的web程序,可以直接访问
-
没有:去DNS服务器(全世界的域名都在这里管理,14.215.177.39:433)找,找到的话就返回,找不到的就返回找不到
-
-
解析域名
发布一个web网站
不会就先模仿
-
将自己写的网站,放到服务器(Tomcat
)中指定的web应用的文件夹(webapps)下,就可以访问了
网站应有的结构
--webapps : Tomcat服务器的web目录
-ROOT
-study : 网站的目录名
- WEB-INF
-class : java程序
-lib : web应用所依赖的jar包
-web.xml : 网站默认配置文件
- index.html : 默认的首页
- static
-css
-style.css
-js
-img
-.......
Http协议
概念
HTTP——超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在
超文本:图片,音乐,视频,定位,地图等等.......
http默认端口:80
https默认端口:443(安全的)
两个时代
-
http1.0
-
HTTP/1.0:客服端可以与web服务器连接后,只能获得一个web资源,断开连接(之后就请求不到)
-
-
http2.0
-
HTTP/1.1:HTTP/1.0:客服端可以与web服务器连接后,只能获得多个web资源
-
Http请求
客服端--发请求(Request)--服务器
——以百度为例
Request URL: https://www.baidu.com/tn=44004473_24_oem_dg 请求地址
Request Method: GET get方法/post方法
Status Code: 200 OK 状态码:200
Remote(远程) Address: 110.242.68.3:443
Accept: text/html 类型
Accept-Encoding: gzip, deflate, br 类型的编码
Accept-Language: zh-CN,zh;q=0.9 语言
Cache-Control: max-age=0 缓存控制
Connection: keep-alive 连接(保持)
-
请求行
请求行中的请求方式:Get,Post
-
Get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但是高效
-
Post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效
-
-
请求头(消息头)
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持那种编码格式 GBK UTF-8 GB2312 ISO8859-1(Java默认编码)
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机
Http响应
服务器--响应--客服端
——以百度为例
Cache-Control: private 缓存控制
Connection: keep-alive 连接(保持)
Content-Encoding: gzip 编码
Content-Type: text/html; 类型
-
响应体
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持那种编码格式 GBK UTF-8 GB2312 ISO8859-1(Java默认编码)
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机
Refresh:告诉客服端,多久刷新一次
Location:让网页重新定位 -
响应状态码
2xx:请求响应成功 200
3xx:请求重定向(你重新到我给你的新位置去,俗称“甩锅”)
4xx:找不到资源 404(资源不存在)
5xx:服务器代码错误 500
网关错误:502
常见面试题:
当你在浏览器地址栏中输入地址并回车的一瞬间到页面能够展示回来,经历了什么?
相关链接:
-
我们在浏览器中输入网址。
-
浏览器查找域名对应的IP地址
DNS查找过程为:
浏览器缓存->系统缓存->路由器缓存->ISP DNS缓存->递归搜索
递归搜索过程为:从根域名服务器到顶级域名服务器到你查询的域名服务器。
-
浏览器打开TCP连接(默认端口为80),向该IP的服务器发送一条HTTP请求,如果浏览器存储了该域名下的cookie,那么cookie也会放入http请求中
-
服务器给浏览器进行一个301永久重定向响应。该IP对应的服务器很可能是代理服务器,比如你输入“http://baidu.com”,而不是“http://www.baidu.com”,按道理这两个网址对应的是同一个网页,因此通过代理服务器的方式进行重定向相应,让这两个网址访问的是同一个网页。
-
浏览器根据重定向地址再次进行HTTP请求。
-
服务器分析HTTP请求,生成HTTP响应,将响应发给客户端。
-
浏览器收到响应内容之后,生成主页框架,同时向服务端继续发送请求,请求的内容是主页里的一些资源,比如说图片、视频等。
-
对于静态的页面内容,浏览器通常进行缓存,对于动态的内容通常不缓存,缓存的时间也是有期限的。
-
浏览器向服务器发送异步请求,因为有些页面显示完成之后客户端仍需要与服务端保持联系。
-
整个过程结束之后,浏览器关闭TCP连接。
Maven(项目架构管理工具)
学习原因
在Javaweb开发中,需要使用大量的jar包,手动导入很麻烦。Maven能自动导入并配置jar包。
核心思想
约定大于配置
-
有约束,不要去违反
-
Maven会规定好你如何去编写我们的Java代码,必须按照这个代码来
下载安装Maven
配置环境变量
系统配置
-
M2_HOME——>maven目录下的bin目录
-
MAVE_HOME——>maven的目录
系统中的path配置
-
%MAVEN_HOME%\bin
测试Maven是否配置安装成功
阿里云镜像
镜像(mirrors)作用:加速我们的下载
国内建议使用阿里云的镜像
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
本地仓库/远程仓库
建立一个本地仓库:localRepository
<localRepository>D:\Maven\apache-maven-3.6.2\maven-repo</localRepository>
在IDEA中使用Maven
-
创建一个MavenWeb项目
-
等待项目初始化完毕
-
观察maven仓库中多了什么
-
IDEA中的Maven设置
IDEA项目创建成功后,看一眼Maven的配置
创建一个普通的Maven项目
只有在Web应用下才会有
IDEA中标记文件夹功能
项目结构配置
在IDEA中配置Tomcat
解决警告问题
问题原因:我们访问一个网站,需要指定一个文件夹的名字,这是必要的配置
使用
pom文件
pom.xml 是Maven的核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--Maven版本和头文件-->
<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>
<!--这里就是我们刚才配置的GAV-->
<groupId>com.xujun</groupId>
<artifactId>javaweb-01-maven02</artifactId>
<version>1.0-SNAPSHOT</version>
<!--Package:项目的打包方式
jar:java应用
war:JavaWeb应用
-->
<packaging>war</packaging>
<!--名称(可删除)-->
<name>javaweb-01-maven02 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<!--配置-->
<properties>
<!--项目的默认构建编码-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!--编码的版本-->
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<!--项目依赖-->
<dependencies>
<!--具体依赖的jar包配置文件-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<!--项目构建用的东西-->
<build>
<finalName>javaweb-01-maven02</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Maven的高级之处
Maven出现的问题
Maven由于它的约定大于配置,我们之后可能会遇到我们写的配置文件,无法被导出或者是生效的问题
解决方案
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
</excludes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
IDEA操作——目录树
问题
Maven默认web项目中的web.xml的版本问题
解决方法:
替换为webapp4.0版本和Tomact一致,为了让实验环境达到最优
<?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"
metadata-complete="true">
</web-app>
IDEA中每次都要重复配置Maven
解决方法:
在IDEA中全局默认设置中去配置
Maven仓库的使用
需要添加Maven依赖时,就去Maven仓库搜索,如果无法确定是哪个包可以在Tomcat中的lib目录下去寻找
在Maven中搜索jar时,尽量选择使用人数多的,不易出错
使用方法:
Servlet
Servlet的生命周期
servlet的生命周期就是从servlet出现到销毁的全过程。主要分为以下几个阶段: 加载类—>实例化(为对象分配空间)—>初始化(为对象的属性赋值)—>请求处理(服务阶段)—>销毁
服务器启动时(web.xml中配置load-on-startup=1,默认为0)或者第一次请求该servlet时,就会初始化一个Servlet对象,也就是会执行初始化方法init(ServletConfig conf),该servlet对象去处理所有客户端请求,service(ServletRequest req,ServletResponse res)方法中执行,最后服务器关闭时,才会销毁这个servlet对象,执行destroy()方法。其中加载阶段无法观察,但是初始化、服务、销毁阶段是可以观察到的。
public class EmpServlet extends HttpServlet{
//初始化servlet,调用init方法
@Override
public void init() throws ServletException {
System.out.println("初始化时调用");
}
//开启服务
@Override
protected void service(HttpServletRequest arg0, HttpServletResponse arg1) throws ServletException, IOException {
System.out.println("开启服务时调用");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
//销毁时调用destory
@Override
public void destroy() {
System.out.println("销毁时调用");
}
}
解决中文乱码问题
Servlet简介
Servlet就是Sun公司开发动态web的一门技术。Sun公司在这些API中提供了一个接口——Servlet
把实现了Servlet接口的Java程序叫做Servlet
开发一个Servlet程序的两个步骤
-
编写一个类,实现Servlet接口或继承HttpServlet
-
把开发好的Java类部署到web服务器中
HelloServlet
Servlet接口Sun公司有两个默认的实现类:HttpServlet / GenericServlet
-
构建一个Maven项目,然后删掉里面的src目录(以后的学习就在这个项目里面建立Moudel)-->此时空的工程就是Maven的主工程
解决导包后,IDEAMaven库然爆红的问题
在项目结构配置中根据另一个已经导入的依赖设置所缺的东西;或者直接在Libraries中添加依赖
-
关于Maven父子工程的理解
在父项目中会有:
<modules>
<module>servlet-01</module>
</modules>在子项目中会有:
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>com.xujun</groupId>
<version>1.0-SNAPSHOT</version>
</parent>父项目中的java子项目可以直接使用
son extends father
-
Maven环境优化
-
修改web.xml为最新
<?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"
metadata-complete="true">
</web-app> -
将Maven的结构搭建完整
-
-
编写一个Servlet程序
-
编写一个普通类
-
实现Servlet接口,直接继承HttpServlet
public class HelloServlet extends HttpServlet {
//由于get或者post只是请求不同的方式,可以相互调用,业务逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter();//响应流
writer.print("Hello,Servlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
} -
-
编写Servlet的映射
为什么需要映射:
我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要再web服务中注册我们写的Servlet,还需要给它一个浏览器能够访问的路径
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.xujun.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的映射路径(请求路径)-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern><!--这个路径之前要加反斜杆-->
</servlet-mapping> -
配置Tomcat
注意:配置项目发布的路径
(记得Apply再OK)
注意:
-
启动测试
当启动成功后,会在左侧生成一个target文件夹
Servlet原理
Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:
Mapping问题
一个Servlet可以指定一个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern><!--这个路径之前要加反斜杆-->
</servlet-mapping>
一个Servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern><!--这个路径之前要加反斜杆-->
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern><!--这个路径之前要加反斜杆-->
</servlet-mapping>
一个Servlet可以指定通用映射路径
<!-- * 是通配符,表示之后为任何东西均可-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
默认请求路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<!--优先级较高,会干掉首页,直接进入页面(此时没有固有的映射路径)-->
指定一些后缀或者后缀等等
<!--可以自定义后缀实现请求映射
注意点:* 前面不能加项目映射路径(/代表当前web项目路径)-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.xujun</url-pattern>
</servlet-mapping>
优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的请求处理
ServletContext对象(基本不用/或被更优的替换/笔试题)
web容器在启动的时候,它会为每个Web程序都创建一个ServletContext对象,它代表了当前的web应用:
-
共享数据
我在这个Servlet中保存的数据,可以在另外一个servlet中拿到
用于放置的类
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getInitParameter(); 获取初始化参数
//this.getServletConfig(); Servlet配置
//this.getServletContext(); Servlet上下文
ServletContext context = this.getServletContext();
String username = "许君";//数据
context.setAttribute("username", username);
/*将一个数据以键值对的形式保存在ServletContex中
名字为:username
值为:username
*/
}
}用于拿取的类
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//先存后读!!!
//数据放上面
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");//强制转换
//响应放下面
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}配置一个web.xml
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.xujun.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>com.xujun.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>测试访问结果:先存后取!
如果没访问 local:8080/s2/hello 直接访问 local:8080/s2/getc 页面,则需要输出在web页面上的数据会报错或者显示null
如果先访问 local:8080/s2/hello 之后,再访问 local:8080/s2/getc 页面,此时需要输出在web页面上的数据则会正确显示
-
获取初始化参数
<!--在web.xml中配置web请求参数-->
<!--配置一些web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>//在java类中使用ServeltContext
获取参数
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
} -
请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("进入了ServletDemo04");
// "/"表示当前项目
//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的路径
// forward()——>代表转化的意思
//requestDispatcher.forward(req,resp);//调用forward方法实现请求转发
context.getRequestDispatcher("/gp").forward(req,resp);
} -
读取资源文件
Properties
-
在java目录下新建properties
-
在resources目录新建properties
发现:均被打包到了同一个路径下:classes(俗称:类路径)
思路:(需要一个文件流)
public class PropertiesServlet extends HttpServlet {
public void test() {
Properties properties = new Properties();
//properties.load();
//配置文件一般读取的是一个文件流(找文件的绝对地址路径即可),但是web应用无法保证绝对路径
}
}//Properties
username=root
password=123456//获取文件流
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user+":"+pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}测试结果:
-
HttpServletResponse
web服务器接收到客服端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse对象;
如果要获取客服端请求过来的参数:找HttpServletRequest
如果要给客服端响应一些信息:找HttpServletResponse
对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 setDateHeader(String name, long date);
public void addDateHeader(String name, long date);
public void setHeader(String name, String value);
public void addHeader(String name, String value);
public void setIntHeader(String name, int value);
public void addIntHeader(String name, String value); -
响应的状态码
public static final int SC_CONTINUE = 100;
public static final int SC_SWITCHING_PROTOCOLS = 101;
public static final int SC_OK = 200;
public static final int SC_CREATED = 201;
public static final int SC_ACCEPTED = 202;
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
public static final int SC_NO_CONTENT = 204;
public static final int SC_RESET_CONTENT = 205;
public static final int SC_PARTIAL_CONTENT = 206;
public static final int SC_MULTIPLE_CHOICES = 300;
public static final int SC_MOVED_PERMANENTLY = 301;
public static final int SC_MOVED_TEMPORARILY = 302;
public static final int SC_FOUND = 302;
public static final int SC_SEE_OTHER = 303;
public static final int SC_NOT_MODIFIED = 304;
public static final int SC_USE_PROXY = 305;
public static final int SC_TEMPORARY_REDIRECT = 307;
public static final int SC_BAD_REQUEST = 400;
public static final int SC_UNAUTHORIZED = 401;
public static final int SC_PAYMENT_REQUIRED = 402;
public static final int SC_FORBIDDEN = 403;
public static final int SC_NOT_FOUND = 404;
public static final int SC_METHOD_NOT_ALLOWED = 405;
public static final int SC_NOT_ACCEPTABLE = 406;
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
public static final int SC_REQUEST_TIMEOUT = 408;
public static final int SC_CONFLICT = 409;
public static final int SC_GONE = 410;
public static final int SC_LENGTH_REQUIRED = 411;
public static final int SC_PRECONDITION_FAILED = 412;
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
public static final int SC_REQUEST_URI_TOO_LONG = 414;
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
public static final int SC_EXPECTATION_FAILED = 417;
public static final int SC_INTERNAL_SERVER_ERROR = 500;
public static final int SC_NOT_IMPLEMENTED = 501;
public static final int SC_BAD_GATEWAY = 502;
public static final int SC_SERVICE_UNAVAILABLE = 503;
public static final int SC_GATEWAY_TIMEOUT = 504;
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;响应状态码:
2xx:请求响应成功 200
3xx:请求重定向(你重新到我给你的新位置去,俗称“甩锅”)
4xx:找不到资源 404(资源不存在)
5xx:服务器代码错误 500
网关错误:502
用Response下载文件
-
向浏览器输出消息
-
下载文件
-
获取下载文件的路径
错误路径
-
下载的文件名
-
设置想办法让浏览器能够支持下载我们需要的东西
-
获取下载文件的输入流
-
创建缓冲区
-
获取OutputStream对象
-
将FileOutputStream流写入buffer缓冲区
-
使用OutputStream将缓冲区中的数据输出到客服端
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//- 获取下载文件的路径
String realPath = "D:\\IDEA\\javaweb-02-servlet\\response\\target\\classes\\1.png";
System.out.println("下载文件的路径"+realPath);
//- 下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("//") + 1);
// lastIndexOf("字符串")——>返回一个指定字符串值最后出现的位置,+1之后则表示 / 后的文件名
//- 设置想办法让浏览器能够支持下载我们需要的东西
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
//设置响应的头部 Content-Disposition:指明响应的配置信息 attachment:指明包含附件
//转码操作:URLEncoder.encode(fileName,"UTF-8")——>解决文件名为中文出现乱码的情况
//- 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
//- 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//- 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//- 将FileOutputStream流写入buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客服端
while ((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
out.close();
in.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
} -
验证码功能
验证来源
-
前端实现
-
后端实现,需要用到 Java的图片类,生成一个图片
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//- 获取下载文件的路径
String realPath = "D:\\IDEA\\javaweb-02-servlet\\response\\target\\classes\\1.png";
System.out.println("下载文件的路径"+realPath);
//- 下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("//") + 1);
// lastIndexOf("字符串")——>返回一个指定字符串值最后出现的位置,+1之后则表示 / 后的文件名
//- 设置想办法让浏览器能够支持下载我们需要的东西
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
//设置响应的头部 Content-Disposition:指明响应的配置信息 attachment:指明包含附件
//转码操作:URLEncoder.encode(fileName,"UTF-8")——>解决文件名为中文出现乱码的情况
//- 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
//- 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//- 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//- 将FileOutputStream流写入buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客服端
while ((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
out.close();
in.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
实现重定向(重点)
B一个web资源收到客户端A请求后,会通知客户端A去访问另一个web资源C的过程,叫重定向
//重定向的方法
public void sendRedirect(String location) throws IOException;
测试:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*原理:
resp.setHeader("Location","/r/img");
resp.setStatus(302);
*/
//重定向的时候一定注意路径问题,否则404(代码正确,路径错误)
resp.sendRedirect("/r/img");
}
常见场景:
-
用户登录,登录成功请求另一个页面
面试题
请你聊聊重定向(302)和转发(307)的区别?
相同点:
-
页面都会实现跳转
不同点:
-
请求转发的时候,url地址栏不会发生变化,只有一个服务器,只需要写相对路径即可(服务器内部跳转)
req.getRequestDispatcher("/success.jsp").forward(req,resp);
-
请求重定向的时候,url地址栏会发生变化,多个服务器,需要写绝对路径
req.getRequestDispatcher("/r/success.jsp").forward(req,resp);
HttpServletRequest
HttpServletRequest代表客服端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,可以获取客服端的所有信息
获取(前端传递的)参数 并 请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//后台接收请求出现中文乱码问题
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//doPost最终调用的还是doGet,所以操作代码编写位置没有区别
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");//处理多选框的情况
System.out.println("==============================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
System.out.println("==============================");
//通过请求转发
//resp.setCharacterEncoding("utf-8");——>防止页面的中文乱码情况
//req.getRequestDispatcher("/r/success.jsp").forward(req,resp);——>路径被写死了
//req.getRequestDispatcher(req.getContextPath()+"/success.jsp").forward(req,resp);//较为灵活
req.getRequestDispatcher("/success.jsp").forward(req,resp);
//后端路径反斜杠 / 代表当前项目路径(当前web应用)
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
Cookie 和 Session
Http无协议状态需要通过cookie和session来弥补
会话
会话概念:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器的过程
有状态会话:
一个网站,怎么证明你来过?
客服端 服务端
-
服务端给客服端一个 信件,客服端下次访问服务端带上信件就可以了:cookie
-
服务器等级你来过了,下次你来的时候我匹配你:session
保存会话的两种技术(区别)
cookie
-
客户端技术(响应,请求)
session
-
服务器技术,利用这个技术,可以保存用户的会话信息(把信息和数据放在Session中)
-
Session对象由服务创建
两者的区别
-
存储位置不同: cookie是保存在客户端(浏览器,可以保存多个), session是保存服务器端 (保存重要的信息,减少服务器资源的浪费)
-
存储数据量大小不同: cookie存储是有限的, 不超过4KB, seesion是无限制的;
-
存储的数据类型不同:cookie只能存储键值对的字符串类型,而session可以存储任意类型
-
默认有效期不同:session默认有效期是30分钟
Cookie
-
从请求中拿到cookie信息
-
服务器响应给客户端cookie
Cookie[] cookies = req.getCookies();//获得cookie
//cookie以键值对的形式存储数据/信息
cookie.getName()//获得cookie中的key
cookie.getValue()//获得cook中的value
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");//新建一个cookie
cookie.setMaxAge(24*60*60);//设置cookie的有效期
resp.addCookie(cookie);//响应(添加)给客户端端一个cookie
例子:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器告诉你,你来的时间,把这个时间封装成一个 信件 ,当你下次访问试,就知道你来了
//解决中文乱码问题
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//解决输出到网页乱码的问题
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
//Cookie 服务器端从客户端获取
Cookie[] cookies = req.getCookies();//返回数组,说明Cookie可能存在多个
//判读Cookie是否存在
if(cookies!=null){
out.write("您上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
/* 获取cookie的名字,cookie是以键值对的形式存储
cookie本身就是一个类,因此cookie的getName()方法就是存放在cookie中数据的key*/
//cookie.getName().equals("name") ——> 这样写的话容易出现空指针异常的问题
if("lastLoginTime".equals(cookie.getName())){
//获取cookie的值
//将获得String字符串解析成成某类型(长整型),使用包装类
long lastLoginTime = Long.parseLong(cookie.getValue());//返回长整型的数字(变成毫秒,毫秒再转具体时间)
Date date = new Date(lastLoginTime);
out.write(date.toLocaleString());
}
}
}else{
out.write("这是您第一次访问本站");
}
//服务器给客户端响应一个cookie
//即:每次访问都会给cookie重新赋值(每次访问都会给你一个小饼干,有了也会给你新的)
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");
//expiry——有效期;设置cookie的有效期为一天
cookie.setMaxAge(24*60*60);
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
cookie : 一般会保存在本地的 用户目录下 的 appdate
一个网站cook是否存在上限
-
一个Cookie只能保存一个信息
-
一个web
-
站点可以给浏览器发送多个cookie,最多存放20个cookie
-
Cookie大小有限制4kb
-
一般浏览器的上限是:300个cookie
删除Cookie
-
不设置有效期,关闭浏览器,自动失效
-
设置有效期时间为0秒
中文转码问题(编码/解码)
当:req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8")
仍然无法解决中文乱码问题,可以使用 编码/解码 来解决
PrintWriter out = resp.getWriter();
//解码
out.write(URLDecoder.decode(cookie.getValue(),"utf-8"));
//编码
Cookie cookie = new Cookie("name", URLEncoder.encode("许君","utf-8"));
Session(重点)
Session概念
-
服务器会给每一个用户(浏览器)创建一个Session对象
-
一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
-
用户登录之后,整个网站它都可以去访问 ——> Session的应用:保存用户的信息(譬如:保存购物车的信息)
Session中常用的方法
测试(注销/自动过期)
注:session之所以存在了,是因为Tomcat启动的时候自动访问了该站点
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
resp.setCharacterEncoding("utf-8");
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
//给Session中存东西
session.setAttribute("name",new Person("许君",3));
//获取Session的ID
String id = session.getId();
//判断Session是不是新创建的
if(session.isNew()){
resp.getWriter().write("session创建成功,ID"+id);
}else{
resp.getWriter().write("session已经在服务器中存在了,ID"+id);
}
/*Session创建时做了什么事情
Cookie cookie = new Cookie("JSESSIONID",id);
resp.addCookie(cookie);*/
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
//得到Session
HttpSession session = req.getSession();
Person person = (Person) session.getAttribute("name");
System.out.println(person.toString());
//手动注销
session.invalidate();
//会话自动过期,在web.xml中配置
<!--设置Session默认的失效时间-->
<session-config>
<!--15分钟后Session自动失效,以分钟为单位-->
<session-timeout>15</session-timeout>
</session-config>
JSP
概念和特点
概念
Java Servlet Pages : java服务端页面,也和Servlet一样,用于动态web技术!
特点
写JSP就像在写HTML,但有稍有区别:
-
HTML只给用户提供静态的数据
-
JSP页面中可以嵌入JAVA代码,为用户提供动态数据
JSP原理
代码层面
没有问题
服务器内部工作
Toncat中有一个work目录:
-
IDEA中使用Tomcat会在IDEA的Tomat中产生一个work目录(IDEA Tomcat 的工作空间)
发现页面转变成了java程序
-
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet(JSP本质上就是一个Servlet)
-
JSP最终也会被转换成为一个java类
//初始化
public void _jspInit() {
}
//销毁
protected void _jspDestroy() {
}
//JSPService
public abstract void _jspService(HttpServletRequest var1, HttpServletResponse var2) throws ServletException, IOException; -
JSPService内部做了些什么事情
-
判断请求
-
内置一些对象
final javax.servlet.jsp.PageContext pageContext;//页面上下文
javax.servlet.http.HttpSession session = null;//session
final javax.servlet.ServletContext application;//applicationContext
final javax.servlet.ServletConfig config;//config
javax.servlet.jsp.JspWriter out = null;//config
final java.lang.Object page = this;//page:当前
javax.servlet.jsp.JspWriter _jspx_out = null;//out
HttpServletRequest request;//请求
HttpServletResponse response;//响应 -
输出页面前增加的代码
response.setContentType("text/html; charset=UTF-8");//设置响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,
null, false, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
out = pageContext.getOut();
_jspx_out = out;以上的这些个对象,我们可以在JSP页面中使用(理解:相当于我们自己创建了一个继承了HttpServlet的实现类,然后在里面把所有的方法的类都new了一遍)
-
-
-
在JSP页面中:
-
只要是JAVA代码就会原封不动的输出
-
如果是HTML代码,就会被转换成
out.write("")
-
JSP基础语法
任何语言都有自己的语法,JAVA中有。JSP作为java技术的一种应用,它拥有一些自己的扩充语法(了解,知道),java所有语法都支持
修改jsp,不用重启Tomcat(服务器)
JSP表达式
<%-- JSP 表达式
作用:用来级那个程序的输出,输出到客服端
<%= 变量或者表达式%>
--%>
<%= new java.util.Date()%>
EL表达式
<%
//从pageContext取出,通过寻找的方式
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5");
%>
<%--使用EL表达式 ${} --%>
<h1>取出的值为:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
JSP表达式与EL 表达式(更加常用)的区别:
当值不存在或为空时,JSP表达式会在页面显示NULL;而EL表达式则不显示
EL表达式取的是setAttribute()中的name1(即不需要findAttribute()也可以获取);而JSP表达式取得是String name1,没有这个String语句,会报错
JSP脚本片段
<%--jsp脚本片段--%>
<%--脚本片段中的代码,会被原封不动的生成到.JSP,java中
所以要求:这里面的代码,必须保证Java语法的正确性
--%>
<%
int sum=0;
for(int i=1;i<=100;++i){
sum += i;
}
out.println("<h1>Sum="+sum+"</h1>");
%>
脚本片段的再实现
<%
int x = 10;
out.println(x);
%>
<p>这是一个JSP文档</p>
<%
int y = 2;
out.println(y);
%>
<hr>
<%--在代码中嵌入HTML元素--%>
<%
for(int i=0;i<5;++i){
%>
<h1>Hello,World! <%= i %> </h1>
<%
}
%>
JSP声明
<%!
static {
System.out.println("Loading servlet");
}
private int globalvar = 0;
public void xu(){
System.out.println("进入了方法许!");
}
%>
JSP声明会被编译到JSP生成的Java的类中!
其他的(JSP表达式,脚本片段的再实现,脚本片段的再实现)就会被生成到_jspService方法中!在JSP,嵌入Java代码即可
关于注释
JSP的注释不会在客户端显示,HTML的就会
JSP指令
<%@ page args(参数)....%>
<%@include file=""%>
<%--@include 会将两个页面合二为一--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>
<hr>
<%--jsp标签
jsp:include:拼接页面,本质还是三个(变量互不影响)
--%>
<jsp:include page="/common/header.jsp"/>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"/>
9大内置对象
-
PageContext——>存东西
-
Request——>存东西
-
Response
-
Session——>存东西
-
Application【ServletContext】——>存东西
-
config【ServletConfig】
-
out
-
page(几乎不用)
-
exception
存东西的内置对象的作用域比较
//从pageContext取出,通过寻找的方式
//作用域从底层到高层:page->reques->session->application
//JVM:双亲委派机制——>往上寻找,往下使用
pageContext.setAttribute("name1","xujun01");
//保存的数据只在一个页面中有效
request.setAttribute("name2","xujun02");
//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","xujun");
//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","xujun");
//保存的数据只在服务器有效,从打开服务器到关闭服务器
-
request:客户端向服务器发送请求,产生的数据,用户用完了就没用了。比如:新闻,头条
-
session:客户端向服务器发送请求,产生的数据,用户用完了一会还能用。比如:购物车
-
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用。比如:聊天数据
JSP标签,JSTL标签,EL表达式
JSP标签
<!--JSP 依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<%--jsp:include --%>
<%--在 jsp:forward 中使用jsp:param 转发的时候携带参数
http://localhost:8080/jsptag.jsp?name=xujun&age=3
--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="xujun"/>
<jsp:param name="age" value="3"/>
</jsp:forward>
JSTL标签(JSP标准标签库)
JSTL标签库的使用就是为了弥补HTML标签的不足;JSTL自定义了许多标签,可以供我们使用,标签的功能和Java代码一样!
<!--JSTL 表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard 标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
分类
-
XML标签
-
SQL标签
-
格式化标签
-
核心标签(学部分)
JSTL标签库使用步骤
-
引入对应的 taglib
注:在Tomcat中需要引入jstl的包,否则会报错——JSTL解析错误(即引入依赖无法直接生效)
-
使用其中的方法
c:if
<body>
<h4>if测试</h4>
<form action="coreif.jsp" method="get">
<%--
EL表达式——获取表单中的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员,则登录成功--%>
<%--var="isAdmin" 存入的是指这个if执行的布尔值,如果是真则isAdmin=true--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员欢迎您!"
</c:if>
<%--自闭合标签——> / --%>
<c:out value="${isAdmin}"/>
</body>
c:choose c:when
<c:set var="score" value="85"/>
<c:choose>
<c:when test="${score>=90}">
你的成绩为优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为一般
</c:when>
<c:when test="${score>=70}">
你的成绩为良好
</c:when>
<c:when test="${score<=60}">
你的成绩为优秀
</c:when>
</c:choose>
c:forEach c:out
<%
ArrayList<String> people = new ArrayList<>();
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田七");
request.setAttribute("list",people);
%>
<%--
var ,每一次遍历出来的变量
items ,要遍历的对象
begin , 开始
end , 结束
step , 步长
--%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"/> <br>
</c:forEach>
<c:forEach var="people" items="${list}" begin="1" end="3" step="2" >
</c:forEach>
EL表达式
-
获取数据
-
执行运算
-
获取web开发的常用对象
-
调用Java方法
JavaBean(实体类)
特定写法
-
必须要有一个无参构造
-
属性必须私有化
-
必须有对应的get/set方法
作用(ORM映射)
一般用来和数据库的字段做映射 ORM
ORM:对象关系映射
-
表——>类
-
字段——>属性
-
行记录——>对象
People表
id | name | age | address |
---|---|---|---|
1 | 许君1号 | 3 | 贵州 |
2 | 许君2号 | 4 | 武汉 |
3 | 许君3号 | 5 | 大理 |
class People{
private int id;
private String name;
private int age;
private String address;
}
class A{
new People(1,"许君1号","贵州");
new People(2,"许君2号","武汉");
new People(3,"许君3号","大理");
}
MVC三层架构
写项目,从底层开始写!
MVC的概念
Model-View-Controller 模型、视图、控制器
早些年
用户直接访问控制层,控制层可以直接操作数据库
servlet——CRUD——>数据库
弊端:程序十分臃肿,不利于维护
servlet代码中:处理请求、响应、处理业务代码、处理逻辑代码
架构:没有什么是加一层解决不了的问题
例子:
程序员调用
|
JDBC
|
mysql,Orcle,等等
MVC三层架构图示和分析
Model
-
业务处理:业务逻辑(Service)
-
数据持久层:CRUD(Dao)
View
-
展示数据
-
提供链接发起Service请求(a链接,form表单,img图片)
Controller(Servlet)
-
接受用户的请求:req(请求参数,Session信息)
-
交给业务层处理对应的代码
-
控制视图的跳转
登录——>接受用户的登录请求——>处理用户的请求(获取用户登录的参数,username,password)——>交给业务层处理登录业务(判断用户名密码是否正确:事务)——>Dao层查询用户名和密码是否正确——>数据库
Filter(过滤器)
作用
过滤器,用来过滤网站的数据
-
处理中文乱码
-
登录验证......
Filter开发步骤
-
导包(不要导错)
-
编写过滤器(实现Filter接口,重写对应的方法即可)
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
//初始化:web服务器启动,就已经初始化了,随时等待过滤对象出现!
public void init(FilterConfig filterConfig) throws ServletException {
}
//FilterChain : 链
/*
1、过滤中所有的代码,在过滤特定请求的时候都会执行
2、必须要让过滤器继续同行
filterChain.doFilter(servletRequest,servletResponse);
*/
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("CharacterEncodingFilter执行前...");
//让我们的请求继续走,如果不写,程序到这里就会被拦截
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("CharacterEncodingFilter执行后...");
}
//销毁:web服务器关闭的时候,过滤器会销毁
public void destroy() {
}
} -
在web.xml中配置Filter
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.xujun.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--只要是 /servlet 的任何请求,会经过这个过滤器-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
Filter实现权限拦截
功能
用户登录之后才能进入主页!用户注销后就不能进入主页了
操作
-
用户登录之后,向Session中放入用户的数据
-
进入主页的时候要判断用户是否已经登录了;
(在过滤器中实现)
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException{
//转化 servlet中的HttpServletRequest —— filter中的ServletRequest(强转)
HttpServletRequest servletRequest1 = (HttpServletRequest) servletRequest;
HttpServletResponse servletResponse1 = (HttpServletResponse) servletResponse;
if(servletRequest1.getSession().getAttribute(Constant.USER_SESSION)==null){
servletResponse1.sendRedirect("/error.jsp");
}
//先写这一句(一定不能漏)——让程序继续往下走,不卡死在儿
filterChain.doFilter(servletRequest,servletResponse);
}
监听器
Listener开发步骤
-
编写一个监听器
实现一个监听器的接口(N种)
//如果统计application——>统计访问数量
//统计网站在线人数(在线人数监听)——>统计session
public class OnlineCountListener implements HttpSessionListener {
//创建session监听:一旦创建Session就会触发这个
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
ServletContext ctx = httpSessionEvent.getSession().getServletContext();
Integer onlineCount = (Integer)ctx.getAttribute("onlineCount");
if(onlineCount==null){
onlineCount = new Integer(1);
}else{
int count = onlineCount.intValue();
onlineCount = new Integer(count+1);
}
ctx.setAttribute("onlineCount",onlineCount);
}
//销毁session监听: 一旦销毁Session就会触发这个
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
ServletContext ctx = httpSessionEvent.getSession().getServletContext();
Integer onlineCount = (Integer)ctx.getAttribute("OnlineCount");
if(onlineCount==null){
onlineCount = new Integer(0);
}else{
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}
ctx.setAttribute("onlineCount",onlineCount);
}
}
/*
Session销毁方式
1、手动销毁:httpSessionEvent.getSession().invalidate();
2、自动销毁(在web.xml中设置自动销毁时间):
<session-config>
<session-timeout>1</session-timeout>
<!--以分钟为单位-->
</session-config>
*/ -
在web.xml中配置监听器
<!--注册监听器-->
<listener>
<listener-class>com.xujun.listener.OnlineCountListener</listener-class>
</listener> -
看情况是否使用(几乎不用)
Listener在GUI中的使用
GUI(图形界面)编程中经常使用
public static void main(String[] args) {
//新建一个窗体,其中“Happy Every Day”是窗体中的标题(变量)
Frame frame = new Frame("Happy Every Day");
//面板
Panel panel = new Panel(null);
//设置窗体的布局
frame.setLayout(null);
frame.setBounds(300,500,500,500);
frame.setBackground(new Color(0,0,255));//设计背景颜色
panel.setBounds(50,50,300,300);
panel.setBackground(new Color(0,255,0));
frame.add(panel);
frame.setVisible(true);//设置可见性
//监听事件,监听关闭事件
//重写该类中全部方法(包括我们不需要的部分)
frame.addWindowListener(new WindowListener() {
public void windowOpened(WindowEvent e) {
System.out.println("打开");
}
public void windowClosing(WindowEvent e) {
System.out.println("关闭ing");
System.exit(0);
}
public void windowClosed(WindowEvent e) {
System.out.println("关闭ed");
}
public void windowIconified(WindowEvent e) {
}
public void windowDeiconified(WindowEvent e) {
}
public void windowActivated(WindowEvent e) {
System.out.println("激活");
}
public void windowDeactivated(WindowEvent e) {
System.out.println("未激活");
}
});
}
//在原来的基础上加一层(把它变成一个抽象类),就可以仅重写需要的部分
文件传输原理(文件上传和下载)
准备工作
采用Apache的开源工具common—fileupload这个文件上传组件(可下载jar包/直接添加maven依赖)
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
文件上传
文件上传的过程中是将文件以流的形式提交到服务器端
文件上传调优
-
为保证服务器安全,上传文件应该放在外界无法直接访问的目录下(如:WEB_INF)
-
为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
1、时间戳
2、uuid——生成一段随机的不会重复的数字
3、md5——加密
4、位运算算法
-
限制上传文件的最大值
-
限制上传文件的类型(在收到上传文件名时,判断后缀名是否合法)
文件上传需要用到的类详解
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象(用来遍历/迭代),在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象(磁盘文件遍历迭代系统)。所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
FileItem类
在HTML页面中必须有 <input type="file" name="filename">
表单如果包含一个文件上传输入项的话,表单属性需要设置enctype="multipart/form-data"
,浏览器表单的类型如果为multipart/form-data,在服务器端想获得数据就要通过流
常用方法
ServletFileUpload类
负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中,使用其parseRequest(HttpServletRequest)方法可以通过表单中的每一个HTML标签提交的数据封装成一个FileItem对象,然后以List列表的形式返回
代码
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//判断上传的文件是普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(req)) {
return;//终止方法运行,说明这是一个普通的表单,直接返回。
}
//创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {
uploadFile.mkdir();//创建这个目录
}
//缓存(临时文件)
//临时路径,假如文件超过了预期大小,我们就把他放到一个临时文件中,过几天 自动删除 或者 提醒用户转存为永久
String tmpPath = this.getServletContext().getRealPath("WEB-INF/tmp");
File file = new File(uploadPath);
if (!file.exists()) {
file.mkdir();//创建这个目录
}
//处理上传的文件,一般都需要通过流来获取,可以使用request.getInputStream(),原生态的文件上传流获取,但是十分麻烦
//建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于 commons-io组件
try {
//1、创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的
DiskFileItemFactory factory = getDiskFileItemFactory(file);
//2、获取ServletFileUpload
ServletFileUpload upload = getServletFileUpolad(factory);
//3、处理上传文件
String msg = uploadParseRequest(upload, req, uploadPath);
//servlet请求转发消息
req.setAttribute("msg", msg);
req.getRequestDispatcher("info.jsp").forward(req, resp);
} catch (FileUploadException e) {
e.printStackTrace();
}
}
public static DiskFileItemFactory getDiskFileItemFactory(File file) {
//1、创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的
DiskFileItemFactory factory = new DiskFileItemFactory(1024*1024,file);
/*可以不写,DiskFileItemFactory()中有默认值
//通过这个公管处设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放入临时文件中;
factory.setSizeThreshold(1024 * 1024);//缓存区大小问1M
factory.setRepository(file);//临时文件的保存目录,需要一个file
*/
return factory;
}
private ServletFileUpload getServletFileUpolad(DiskFileItemFactory factory) {
//2、获取ServletFileUpload
ServletFileUpload upload = new ServletFileUpload(factory);
//可以不写,ServletFileUpload()中有默认值
//监听文件上传进度:
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已经读取到的文件大小
//pContentlengt:文件大小
public void update(long pBytesRead, long pContentlength, int pItems) {
System.out.println("总大小:" + pContentlength + "已上传:" + pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值(此处为10M)
upload.setFileSizeMax(1024 * 1024 * 10);
//设置总共能够上传文件的大小
upload.setSizeMax(1024 * 1024 * 10);
return upload;
}
private String uploadParseRequest(ServletFileUpload upload, HttpServletRequest req, String uploadPath) throws FileUploadException, IOException {
String msg = "";
//3、处理上传文件
//把前端请求解析,封装成一个FileItem对象,需要从ServletFileUpload对象中获取
List<FileItem> fileItems = upload.parseRequest(req);
//fileItem 是每一个表单对象
for (FileItem fileItem : fileItems) {
//判断上传的文件是普通的表单还是文件的表单
if (fileItem.isFormField()) {
//getFiledName 指的是前端表单控件的name
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");//处理乱码
System.out.println(name + ":" + value);
}
else {//文件
//==================处理文件======================
String uploadFileName = fileItem.getName();//拿到文件名
//可能存在文件名不合法的情况
if (uploadFileName.trim().equals("") || uploadFileName == null) {//如果文件名为空
continue;//跳过本次循环继续下一个List元素的遍历
}
else {
//获得上传的文件名
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);//找最后一个 斜杠 后一串字符
System.out.println("上传的文件名:" + uploadFileName);
//获得上传文件的后缀名
String fileExtname = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);//找最后一个 点 后一串字符
/*
如果后缀名不对,直接返回 return,不处理,告诉用户名文件类型不对
*/
System.out.println("文件信息[件名:" + fileName + "---文件类型" + fileExtname + "]");
//可以使用UUID(唯一识别的通用码),保证文件名唯一
//UUID.randomUUID(),随机生成一个唯一识别的通用码
/*
网络传输中的东西,都需要序列化
POJO,实体类,如果想要在多个电脑上运行(只要是传输,都需要把对象序列化)
标记接口:implements Serializable
*/
String uuidPath = UUID.randomUUID().toString();
//==================文件处理完毕======================
//存放地址:uploadPath
//文件真实存放的路径:realPath
String realPath = uploadPath + "/" + uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if (!realPathFile.exists()) {
realPathFile.mkdir();
}
//==================存放地址完毕======================
//获得文件上传的流
InputStream in =null;
in = fileItem.getInputStream();
//创建一个文件输出流
//realPath:真实的文件夹
//差一个文件:加上输出文件的名字+"/"+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath + "/" + uuidPath);
//创建一个缓冲区
byte[] buffter = new byte[1024 * 1024];
//判断是否读取完毕
int len = 0;
//如果大于0说明还存在数据
while ((len = in.read(buffter)) > 0) {
fos.write(buffter, 0, len);
}
//关闭流
fos.close();
in.close();
msg = "文件上传成功!";
fileItem.delete();
;//上传成功,清除临时文件
//==================文件传输完毕======================
}
}
}
return msg;
}
运行结果
存放路径