两种方式:可以通过浏览器(browser)进行上网,也可以通过客户端(client)进行上网
两种结构
BS结构 browser server 浏览器服务器
CS结构 client server 客户端服务器
BS结构 browser server 浏览器服务器
- 不需要安装客户端,只要能连上网,就能随时随地使用
- 开发人员只需要对服务器端程序进行开发、维护,降低开发维护难度和开发维护成本
- 浏览器主要负责用户界面的动态展示,只处理一些简单的逻辑功能
- 所有具体业务逻辑的处理都由服务器端程序完成,所以程序负载几乎都转移给服务器
端。
- 但是随着服务器负载的增加,可以平滑地增加服务器的个数并建立集群服务器系统,然
后在各个服务器之间做负载均衡。
CS结构 client server 客户端服务器
- 将应用程序分为客户端和服务器端两层,客户端程序用于展示功能,为用户提供操作界
面,同时也可以进行业务逻辑的处理;而服务器端程序负责操作数据库完成数据处理等
核心业务
- 由此可见通过C/S开发模型开发的应用程序,客户端程序可以承担一部分业务逻辑处
理,特别是数据的预处理工作,减轻了服务器端程序的压力
BS优缺点:
优点:实时地更新数据(新功能的增加只需要在服务端完成, 浏览器刷新就好了),
缺点:将负载给了服务器
CS优缺点:
优点:客户端也分担了一部分负载,
缺点:如果有新的功能要增加必须要重新下载客户端
------------------------------
作用:能够让本地电脑中的资源可以被其他的电脑访问
常见的javaweb服务器
weblogic
它是oracle公司的,已经被oracle收购,它是全面支持javaee规范,收费的大型的web
服务器,它是企业中主流的服务器,在网络上学习资料比较多。
websphere
它是ibm公司的一个大型的收费的全面支持javaee规范的javaee容器。
tomcat
它是开源的免费的servlet规范的服务器。 它是apache一个web服务器。
jboss
hibernate公司产品,不是开源免费的,是javaee规范的容器。
ngix
Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理服务器
------------------------------
什么是web?
Web指的就是网页,我们所说的web指的是internet主机(服务器)上的供外界访问的资源
web资源可以分为两种:静态web资源、动态web资源
静态web资源
指web页面上供人们浏览的数据,它们始终不变。例如html
优点:
静态网站开发简易,只需要掌握HTML、CSS和JS就可以开发
静态访问速度快,因为静态网页不需要和任何程序进行交互,更不需要对数据进行处理
缺点:
静态网站内容无法实时更新,因为网站由一个个的静态HTML网页构成,新增内容只能
通过开发人员修改代码
网站内容过多时,每个页面都需要单独制作,需要不断编写和维护HTML页面,增加了
网站开发人员的工作量,提高了运营费用。
动态web资源
指web页面中内容是由程序产生的,供人们浏览,并且在不同的时间点,数据不一样,并且
还可以实现人与人之间的交互。用到Servlet和JS等技术.
优点
维护方便、可以根据用户需求实现各种功能
查询信息方便,能存储大量数据,需要时能立即查询
网站内容可以实时动态更新
与用户交互性强,提高用户粘性
缺点
技术要求高
测试是否安装成功:
在tomcat的安装目录下有一个bin目录 ,在目录 中有一个startup.bat文件执行它。打开浏览器,在浏览器的地址栏上输入 http://localhost:8080
配置
- Web静态项目
包含都是静态资源:html、js、css、图片、音频等等
- Web动态项目: 不是必须包含动态资源
包含都是静态资源:html、js、css、图片、音频等等
那么和静态项目的区别在哪里?可以有动态资源及WEB-INF文件夹
通过http://localhost:8080/ 访问的资源是来自于tomcat服务器的动态web项目(内置),
而在tomcat的一个安装目录中,是由一个webapps的文件夹专门用来部署web项目
------------------------------
方式一:直接将web应用程序放置到webapps目录
直接将一个web应用程序放置在tomcat/webapps目录下。这时web应用程序目录名称就是我们访问tomcat下的这个应用程序的名称
方式二:虚拟目录初级版
将一个不在tomcat下的web应用程序部署加载。可以在tomcat/conf/server.xml文件中配置,在server.xml文件中的标签中添加一段配置
<Context docBase="磁盘路径" path="/hello" />
经过以上配置后,在浏览器上可以输入http://localhost/hello来访问
但是,在tomcat6以后已经不建议使用了。
<Context docBase="磁盘路径" />
这种方案配置,xml文件的名称就是访问路径,在浏览器中访问http://localhost/good
------------------------------
------------------------------
只有资源来到了day50_war_exploded文件夹中,才意味着部署成功
src文件夹:
可以部署上去!部署到了项目中的\WEB-INF\classes文件夹中
web文件夹:
可以部署上去!部署到了项目目录中!
day50项目下:
不可以部署上去
协议:两个设备进行数据交换的约定!
Http协议:超文本传输协议(hypertext transfer protocl)
超文本:字符、音频、视频、图片等等
基于tcp协议。tomcat服务器底层实现本质上就是TCP(Socket)
经过演示发现,浏览器和服务器,它们之间进行交互,是一个请求-响应模型!!!
请求:
请求行
请求头
请求正文
响应:
响应行
响应头
响应正文
通过抓包可以看到如下信息
General:请求行,响应行
Request Headers:请求头
Response Headers:响应头
响应正文:将显示内容携带到浏览器
请求正文:用来接收请求的参数.
------------------------------
------------------------------
请求组成:请求行、请求头、请求正文
Request URL : 请求路径,告诉服务器要请求的资源路径
Request Method : 请求方式 , GET/POST
protocol : http协议版本
---------------
get请求只能携带小数据、get请求下的请求参数会直接拼接到Request URL(请求网址)后面,QueryStringParameters
post请求可以携带大数据、post请求下的请求参数会存放到请求正文
请求参数:比如,表单中的输入框中的值.
如果我们要做文件上传,需要用到post请求,文件比较大
---------------
Content-Type:浏览器告诉服务器,请求正文的数据类型
User-Agent:浏览器告诉服务器,我是个什么样的浏览器
---------------
请求正文,只有当请求方式为post,且有请求参数时才会有请求正文
------------------------------
Http响应组成:响应行、响应头、响应正文
Status Code : 响应状态码
常见的有:
200:服务器响应成功
302: 告诉浏览器,进行重定向
304: 页面上的内容没有发生改变,不需要重新请求服务器
404: 没有对应的服务器资源
500:服务器内部错误!
---------------
Location:告诉浏览器重定向的资源路径,需要结合响应状态码302使用
Content-Type:服务器告诉浏览器,响应正文的数据类型
Content-Type:text/html;charset=utf-8; 服务器告诉浏览器,响应正文是文本和html标签;告诉浏览器,应该以utf-8的形式进行解码!浏览器就会以html标签及utf-8的形式对响应正文进行渲染显示!!!
refresh:定时跳转
Content-Disposition:文件下载
---------------
浏览器显示的内容
Servlet是运算在服务器上的一个java程序,简单说,它就是一个java类。我们要使用servlet,需要导入servlet的api.
------------------------------
------------------------------
package servlet;
//1,自定义类继承HttpServlet
public class Demo01Servlet extends HttpServlet {
//2,重写doGet和doPost方法
//doGet:处理get请求
//doPost:处理post请求
//在doGet方法中调用doPost方法,不管是 get请求 or post请求,都交给doGet方法处理
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理get请求
System.out.println("Demo01Servlet doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理post请求
doGet(req, resp);
}
}
web/WEB-INF/web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Demo01Servletservlet-name>
<servlet-class>com.qfedu.servlet.Demo01Servletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Demo01Servletservlet-name>
<url-pattern>/demo01url-pattern>
servlet-mapping>
web-app>
浏览器发起请求: http://localhost:8080/day50/demo01
就会在服务器中找访问名称为demo01的Servlet -> Demo01Servlet
请求的处理就交给了Demo01Servlet的实例,根据请求方式get/post,决定是给doGet还是doPost方法处理
不管是get请求还是post请求,对于服务器来说,没差别
get请求将请求参数放到请求网址
post请求将请求参数放到请求正文
服务器最终无非就要获取请求参数。getParameter()方法
------------------------------
HttpServlet继承于GenericServlet、GenericServlet实现于Servlet,也就是说Servlet是顶层接口
在servlet接口中,没有doGet和doPost方法,处理请求是service方法(抽象的)
在GenericServlet类中,没有doGet和doPost方法,处理请求是service方法(抽象的)
HttpServlet类中重写service方法。
根据源码,发现重写service方法中,
有将ServletRequest强转为HttpServletRequest,
将ServletResponse强转为HttpServletResponse
以上强转是因为,ServletRequest和ServletResponse并没有针对Http协议做优化!!!无法专门针对http协议调用方法!!
HttpServletRequest和HttpServletResponse有针对http协议做优化!!!
在开发中,一般应用比较多的是使用extends HttpServlet,优点是它是与http协议相关
的,封装了http协议相关的操作。
------------------------------
在javax.servlet.Servlet接口中定义了三个方法init service destroy它们就是servlet的生命周期
方法
public interface Servlet {
//监听Servlet的初始化
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
//处理请求
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
//监听Servlet的销毁
void destroy();
}
------------------------------
根据Servlet生命周期,可知,servlet默认不会随着服务器的启动而初始化
load on startup可以让servlet随着服务器的启动而初始化
对于load-on-startup它的可以配置的值有10个,1代表优先级最高,数值越大,优先级越低
------------------------------
对于servlet,我们需要在web.xml文件中对其进行配置
<servlet>
<servlet-name>Demo01Servletservlet-name>
<servlet-class>servlet.Demo01Servletservlet-class>
servlet>
---------------
<servlet-mapping>
<servlet-name>Demo01Servletservlet-name>
<url-pattern>/demo01url-pattern>
servlet-mapping>
一个可有多个与其对应
要求网址上的访问名称完全和一致
必须以"/"开头,否则会报错:IllegalArgumentException : Invalid
---------------
要求网址上的访问名称中的目录和一致
必须以"/"开头,以"*"结尾
比如:/a/b/* , 目录必须是/a/b,后面随便写
---------------
要求网址上的访问名称中的后缀和一致
不能以"/"开头,以"*"开头,后缀名根据业务写
比如:*.xyz。后缀名必须是xyz,其他随意写!!!
------------------------------
创建一个servlet时,如果它的url-pattern的配置值为”/”这时这个servlet就是一个缺省的
servlet,tomcat服务器中默认就有缺省Servlet
凡是在web.xml文件总找不到匹配的元素的URL,它们的请求都将交给
缺省Servlet处理。也就是说,缺省的servlet用于处理其他Servlet处理不了的请求
当访问tomcat服务中的静态资源(html、图片等等)时,实际上是在访问这个缺省的servlet
在当前工程中自定义Servlet,将url-parttern设置为"/",就覆盖了tomcat容器中的缺省Servlet
SpringMVC框架中,用于放行静态资源
------------------------------
http://localhost:8080/day51/img/girl.jpg
/day51/img/girl.jpg
当前目录:./ ,可以省略
上一级目录:../
------------------------------
ServletConfig是javax.servlet.包下的一个接口。ServletConfig它是Servlet的一个配置对象
ServletConfig对象是由服务器创建的,它是通过Servlet的init方法传递到Servlet中
获取Servlet名称 getServletName
获取Servlet初始化参数 getInitParameter getInitParameterNames
获取ServletContext对象
getInitParameter(String parameterName):根据参数名称获取指定的参数值
getInitParameterNames():获取所有的参数名称
获取域对象:ServletContext
public class Demo11Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
String servletName = servletConfig.getServletName();
System.out.println(servletName+"正在运行...");
System.out.println("--------------------");
String username = servletConfig.getInitParameter("username");
System.out.println(username);
String password = servletConfig.getInitParameter("password");
System.out.println("--------------------");
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = servletConfig.getInitParameter(parameterName);
System.out.println("name"+parameterName+"password"+parameterValue);
}
ServletContext servletContext = getServletContext();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
<servlet>
<servlet-name>Demo11Servletservlet-name>
<servlet-class>servlet.Demo11Servletservlet-class>
<init-param>
<param-name>usernameparam-name>
<param-value>rootparam-value>
init-param>
<init-param>
<param-name>passwordparam-name>
<param-value>root123param-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>Demo11Servletservlet-name>
<url-pattern>/demo11url-pattern>
servlet-mapping>
------------------------------
相当于是整个应用程序对象
ServletContext是一个域对象,可以用来存储数据.可以将它想像成一个Map
,可以通过它实现Servlet资源共享
在应用程序中的任何位置都能够访问
* getAttribute(String parameterName) : 获取ServletContext域中指定名称的参数值
* setAttribute(String paramterName,Object parameterValue):存储参数到ServletContext域中
* removeAttribute(String parameterNam):将ServletContext域中指定名称的参数移除
---------------
在web.xml中配置的全局初始化参数,可以通过ServletContext对象获取
public class Demo15Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
Enumeration<String> parameterNames = servletContext.getInitParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = servletContext.getInitParameter(parameterName);
System.out.println("name:"+parameterName+"value:"+parameterValue);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<context-param>
<param-name>usernameparam-name>
<param-value>rootparam-value>
context-param>
<servlet>
<servlet-name>Demo15Servletservlet-name>
<servlet-class>servlet.Demo15Servletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>Demo15Servletservlet-name>
<url-pattern>/demo15url-pattern>
servlet-mapping>
---------------
getRealPath:依据当前项目去生成真实磁盘路径
servletContext.getRealPath("upload"):当前项目的服务器磁盘路径/upload
servletContext.getRealPath("upload/img"):当前项目的服务器磁盘路径/upload/img
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
String upload = servletContext.getRealPath("upload");
System.out.println(upload);
}
------------------------------
public class Demo17Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 获取ServletContext对象
ServletContext servletContext = getServletContext();
//2 判断是否第一次
Integer count = (Integer) servletContext.getAttribute("count");
if (count == null) {
//2.1 第一次访问
count = 1;
System.out.println(count);
servletContext.setAttribute("count", count);
} else {
//2.2 非第一次访问
count++;
System.out.println(count);
servletContext.setAttribute("count", count);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
------------------------------
支持注解开发,由注解配置来代替web.xml配置
@WebServlet(name = "Demo01Servlet", urlPatterns = {
"/demo01", "/mydemo01"},
initParams = {
@WebInitParam(name = "username", value = "root"),
@WebInitParam(name = "password", value = "123456")})
---------------
name:String:设置Servlet名称
urlPatterns:String[]:设置Servlet的访问路径
loadOnStartup:int:设置Servlet的load-on-startup属性
initParams:WebInitParam[]:设置Servlet的初始化参数
当浏览器发起请求后,服务器会创建一个请求对象、一个响应对象,通过service方法传入给Serlvet
request与response的作用
* request对象就可以操作http请求信息
* response对象就可以操作http响应信息
ServletRequest与HttpServletRequest及ServletResponse与HttpServletResponse关系
* ServletRequest与ServletResponse它们是HttpServletRequest与HttpServletResponse的
父接口
------------------------------
操作响应行
* setStatus(int status):操作正常响应状态码,比如:200、302
* ssendError(int status):操作错误响应状态码,比如: 404
操作响应头
* public void addHeader(String name, String value):直接覆盖响应头原有值
* public void setHeader(String name, String value):在响应头原有值的后面追加
------------------------------
通过各种方法将各种网络请求重新定个方向转到其它位置
通过操作状态码302及响应头location来实现
重定向的流程
* 当浏览器访问一个资源Demo03Servlet,访问名称为“/demo03”
* Demo03Servlet进行重定向
* 操作响应状态码302
* 操作响应头Location,服务器告诉浏览器,重定向的资源的访问路径
* 浏览器进行一个新的请求,完成重定向
response.setStatus(302);
response.setHeader("Location","demo04");
---------------
response.sendRedirect("demo04");
------------------------------
一个资源定时一段时间之后,跳转到另外一个资源
操作响应头refresh
resp.setHeader("refresh","5;url=demo07");
------------------------------
响应正文:就是浏览器显示的主体内容
response.getWriter().write():操作响应正文
响应正文中文乱码
* 本质原因,服务器的编码和浏览器的解码,格式不同!
* setCharacterEncoding("utf-8"):
* 告诉服务器,应该以utf-8格式编码响应正文
* setHeader("Content-Type","text/html;charset=utf-8"):
* 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
* setContentType("text/html;charset=utf-8")
* 告诉服务器,应该以utf-8格式编码响应正文
* 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//告诉服务器,编码格式为utf-8
// response.setCharacterEncoding("utf-8");
//单单只告诉服务器的编码格式,不合适的!浏览器的编码格式不确定!
//告诉浏览器,编码格式也应该是utf-8
// response.setHeader("Content-Type","text/html;charset=utf-8");
response.setContentType("text/html;charset=utf-8");
String msg1 = "hello world 老邱";
PrintWriter writer = response.getWriter();
//操作响应正文
writer.write(msg1);
//操作响应正文中文乱码
//不仅要告诉服务器编码格式,还要告诉浏览器编码格式! 它们应该是统一的!
//有没有一个简单,就是既告诉服务器的编码,同时也告诉浏览器的编码
}
------------------------------
getRequestURI 获取请求路径
getMethod 获取请求方式
getRemoteAddr 获取请求ip
getLocalPort 获取请求端口
getQueryString 请求网址"?"后面的路径
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo02Servlet");
//获取请求路径
String requestURI = request.getRequestURI();
System.out.println(requestURI);
//获取请求方式
String method = request.getMethod();
System.out.println(method);
//获取请求ip
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
//获取请求端口
int port = request.getLocalPort();
System.out.println(port);
//获取请求网址后的请求参数
String queryString = request.getQueryString();
//在网址后的请求参数,如果要使用,需要做字符串的拆分!!
System.out.println(queryString);
//SpringMVC:前后端交互使用的是json字符串,json解析 --> java对象!!
}
------------------------------
getHeader() 获取指定请求头的值
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo03Servlet");
//获取User-Agent请求头:判定请求是由哪种浏览器发起
String userAgent = request.getHeader("User-Agent");
System.out.println(userAgent);
}
------------------------------
请求正文:post+请求参数
请求参数:不管是get请求 还是 post请求
getParameter 获取指定请求参数值
getParameterNames 获取所有请求参数名称
getParameterValues(String parameterName) 获取指定请求参数所有值
getParameterMap 键,获取所有请求参数名称 , 相当于getParameterNames方法
值,获取指定请求参数所有值,相当于getParameterValues方法
getParameter 能拿到表单中的参数、URL中的参数
getAttribute 拿的是通过setAttribute存入request的属性
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取指定参数值
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username : " + username + " , password : " + password);
System.out.println("---------------");
//获取所有请求参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String parameterValue = request.getParameter(parameterName);
System.out.println("name : " + parameterName + " , value : " + parameterValue);
}
System.out.println("---------------");
//获取指定请求参数所有值
String[] usernames = request.getParameterValues("username");
System.out.println(usernames[0]);
String[] hobbys = request.getParameterValues("hobbys");
for (String hobby : hobbys) {
System.out.println(hobby);
}
System.out.println("---------------");
//获取请求参数对应的map :
//getParameterMap() -> Map(String,String[])
//键:请求参数名称 相当于 getParameterNames
//值:一组请求参数值 相当于 getParameterValues
Map<String, String[]> parameterMap = request.getParameterMap();
//双列集合:获取到所有的实体对象(键值对象)
Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
//键 - 请求参数名称
String parameterName = entry.getKey();
//值 - 一组请求参数值
String[] values = entry.getValue();
StringBuffer valueStr = new StringBuffer();
for (String value : values) {
valueStr.append(value + " ");
}
System.out.println("参数名称 : "+ parameterName + " , 参数值 : " + valueStr);
}
}
------------------------------
本质:请求正文中的中文参数乱码
解决方案
* request.setCharacterEncoding("utf-8")
* 告诉服务器应该以utf-8解码请求正文
* 逆向,先编码在解码
* 先将乱码字符串以iso8859-1编码成字节
* 将字节以utf-8解码成字符串
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//不管是get请求 还是 post请求,都存在请求参数中文乱码问题
//post请求 告诉服务器,应该以utf-8来解码请求正文
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
username = new String(username.getBytes("iso8859-1"),"utf-8");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
将tomcat容器的URIEncoding=“utf-8”,对Query String中的请求参数有效,对请求正文中无效,对post请求下的中文乱码无效
---------------
本质:Query String中的中文参数乱码
解决方案
* 逆向,先编码在解码
* 先将乱码字符串以iso8859-1编码成字节
* 将字节以utf-8解码成字符串
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//不管是get请求 还是 post请求,都存在请求参数中文乱码问题
//告诉服务器,应该以utf-8对请求正文进行解码
//get请求中,有请求正文么? 没有!
// request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
//将乱码的字符串编码(iso8859-1)成字节
// byte[] bytes = username.getBytes("iso8859-1");
//将字节解码(utf-8)成字符串
// String newUsername = new String(bytes,"utf-8");
username = new String(username.getBytes("iso8859-1"),"utf-8");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
}
修改tomcat容器的URIEncoding=“utf-8”
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="utf-8"
redirectPort="8443" />
request.setCharacterEncoding(“utf-8”)对get请求无效,告诉服务器应该以utf-8来解码请求正文,跟Query String 没有关系
---------------
方案
* tomcat8.5
* 相当于是tomcat7.0修改了URIEncoding="utf-8"
* 就解决了get请求参数中文乱码问题
* request.setCharacterEncoding("utf-8")
* 就解决了post请求参数中文乱码问题
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
System.out.println(username);
String password = request.getParameter("password");
System.out.println(password);
}
------------------------------
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Demo08Servlet");
//转发到Demo09Servlet , 重定向 : 操作响应状态码302,操作响应头location 都是通过response
//获取一个转发器对象,传入一个转发地址
RequestDispatcher dispatcher = request.getRequestDispatcher("demo09");
//开始转发
dispatcher.forward(request,response);
}
转发只有一次请求
------------------------------
转发forward:服务器内部进行,客户端不知道。
转发可以共享request中的数据。
转发只能在当前工程内部进行。
重定向:redirect:由服务器返回给客户端地址,再由客户端发起第二次请求。
重定向不能共享request中的数据。
重定向可以访问其他工程。
请求次数
* 重定向有2次请求
* 转发有1次请求
跳转区别
* 重定向既可以站内资源进行跳转,站外资源也可以进行跳转
* 转发只能站内资源进行跳转
路径区别
* 要转发的资源的相对路径无区别
* 要转发的资源的绝对路径有区别
* 重定向,是从先从项目开始找,再找资源
* 转发,是直接从项目中找资源
不能重定向两次
重定向不能拿到上一个请求中的参数,但是可以在重定向的url中拼接参数
/**
* 重定向和转发的区别之站内资源、站外资源
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//重定向到站外资源
response.sendRedirect("http://www.baidu.com/");
//转发到站外资源
request.getRequestDispatcher("http://www.baidu.com/").forward(request,response);
}
/**
* 重定向和转发的区别之路径
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//重定向和转发,使用相对路径是没有区别的!
//使用重定向跳转到index.html(相对路径)
response.sendRedirect("index.html");
//使用转发跳转到index.html(相对路径)
request.getRequestDispatcher("index.html").forward(request,response);
//使用重定向跳转到index.html(绝对路径)
response.sendRedirect("/day55/index.html");
//使用转发跳转到index.html(绝对路径)
request.getRequestDispatcher("/index.html").forward(request,response);
}
------------------------------
域对象:可以存储数据的对象
ServletContext域对象的作用范围:不同设备、当前项目中的任意一个资源都可以访问ServletContext域中的数据
request域对象的作用范围
* request对象的生命周期
* 发起请求时,request初始化
* 响应时,request销毁
* request域对象的作用范围在一次请求中!
request在重定向和转发中使用!
* 重定向中不能使用request域对象
* 转发中可以使用request域对象
/**
* request域对象在重定向和转发中的使用
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request域操作数据的方法和ServletContext域是一样!
//setAttribute、getAttribute、removeAttribute
//往request域中存储一个变量
String msg = "hello servlet";
request.setAttribute("msg",msg);
//重定向到Demo13Servlet request.getContextPath() 获取项目名称
response.sendRedirect(request.getContextPath()+"/demo13");
request.getRequestDispatcher("/demo13").forward(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.removeAttribute("msg");
System.out.println("Demo13Servlet");
String msg = (String) request.getAttribute("msg");
System.out.println(msg);
}
打开浏览器,访问服务器中资源,关闭浏览器;这个过程就是会话
* Cookie会话技术;浏览器会话技术
* Session会话技术;服务器会话技术
解决ServletContext域对象、Request域对象存储数据所存在的问题
网景公司发明。是浏览器的会话技术
* 浏览器请求服务器,请求Demo01Servlet,创建一个Cookie对象,名称为cookie1
* 可以通过响应头Set-Cookie,携带cookie给浏览器进行保存
* 浏览器再次请求服务器,请求Demo02Servlet,获取cookie1对象
------------------------------
//方式一(不推荐)
response.addHeader("set-cookie","msg=hellocoolie");
//方式二
Cookie cookie = new Cookie("msg","hellocookie");
response.addCookie(cookie);
开发步骤
* 通过request对象获取所有的Cookie对象,存储到一个数组中
* 遍历该数组,匹配Cookie名称
* 如果匹配上,就知道了指定的Cookie对象
* 如果匹配不上,就没有指定的Cookie对象
Cookie[] cookies = request.getCookies();
Cookie cookie = null;
for(Cookie sonCookie : cookies){
if("msg".equals(sonCookie.getName())){
cookie = sonCookie;
}
}
if(null != cookie){
System.out.println("name : "+msgCookie.getName() + " , value : "+ msgCookie.getValue());
}
------------------------------
cookie的生命周期
* 默认是随着浏览器的关闭而销毁
setMaxAge
* 设置cookie的存活时长,cookie就可以不随着会话的关闭而销毁
* 取值有三种:>0有效期,单位秒;=0浏览器关闭;<0内存存储,默认-1
默认情况下,Cookie对象会随着任何一个请求携带到服务器
setPath 设置Cookie的访问路径
Cookie cookie = new Cookie("msg","helloworld");
cookie.setPath("/day56/demo04");
response.addCookie(cookie);
cookie对象只有访问路径包含"/day56/demo04",才会跟随请求携带到服务器
------------------------------
需求:
第一次访问,就直接打印当前时间
不是第一次访问,就打印上一次的访问时间
开发步骤:
获取对应的Cookie对象
判断是否是第一次访问
如果是第一次访问
打印当前时间
将当前时间存储到Cookie中
如果不是第一次访问
打印上一次访问时间
将当前时间存储到Cookie中
//判断是否是一次请求
Cookie[] cookies = request.getCookies();
Cookie cookie = null;//记录上一次的访问时间
if (cookies != null && cookies.length != 0 ) {
for (Cookie sonCookie : cookies) {
if ("lastTime".equals(sonCookie.getName())) {
cookie = sonCookie;
}
}
}
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
if (null == cookie) {
//第一次访问 ,打印当前时间,并将创建Cookie对象,存储当前时间
Date currentDate = new Date();
System.out.println("第一次访问,时间为" + format.format(currentDate));
cookie = new Cookie("lastTime",currentDate.getTime()+"");
} else {
//不是第一次访问,从cookie取出上一次的访问时间,并打印。获取当前时间,并存储cookie对象中
long lastDateMills = Long.parseLong(cookie.getValue());
Date lastDate = new Date(lastDateMills);
//获取到了上一次的访问时间
String lastTimeStr = format.format(lastDate);
System.out.println("上一次访问,时间为" + lastTimeStr);
//获取当前时间,并存储cookie对象中
Date currentDate = new Date();
// cookie.setValue(currentDate.getTime()+"");
cookie = new Cookie("lastTime",currentDate.getTime()+"");
}
response.addCookie(cookie);
需求:
浏览商品,将商品的浏览的记录起来,并显示!
<a href="/day56/history?id=0">西游记a><br>
<a href="/day56/history?id=1">红楼梦a><br>
<a href="/day56/history?id=2">水浒传a><br>
<a href="/day56/history?id=3">三国志a><br>
/* * 获取history的Cookie对象
* * 判断商品浏览记录是否为空
* * 如果浏览记录没有
* * * 创建Cookie,并将当前的商品记录到Cookie中
* * 如果浏览记录有
* * * 有当前的商品,不做任何处理
* * * 没有当前商品,就需要将当前的商品拼接到已有记录中
*/
@WebServlet(name = "HistoryServlet", urlPatterns = "/history")
public class HistoryServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
Cookie cookie = null;
Cookie[] cookies = request.getCookies();
if (null != cookies && 0 != cookies.length){
for (Cookie sonCookie : cookies) {
if ("history".equals(sonCookie.getName())) {
cookie = sonCookie;
}
}
}
if (null == cookie) {
//之前没有任何浏览记录 ,创建Cookie对象 ,并存储浏览记录(id)
cookie = new Cookie("history",id);
} else {
//之前有一些浏览记录
String historyStr = cookie.getValue();
if (!historyStr.contains(id)) {
//有一些记录,但是不包含当前浏览的商品;
//将浏览商品拼接到已有浏览记录中
//120
//1-2-0
historyStr += "-"+id;
cookie.setValue(historyStr);
} else {
//有一些记录,包含当前浏览的商品 ,不做任何处理
}
}
response.addCookie(cookie);
//上述代码,已经完成了商品浏览记录功能,剩下就是要显示商品浏览记录
response.sendRedirect(request.getContextPath()+ File.separator+"showHistory");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
/*
* 显示商品浏览记录
* * 获取history对应的Cookie对象
* * 获取对应的商品浏览记录
* * 判断是否有浏览记录
* * 如果没有,就显示“没有浏览记录”
* * 如果有,就显示处理浏览记录字符串
*/
@WebServlet(name = "ShowHistoryServlet", urlPatterns = "/show")
public class ShowHistoryServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = null;
Cookie[] cookies = request.getCookies();
if (null != cookies && 0 != cookies.length) {
for (Cookie sonCookie : cookies) {
if ("history".equals(sonCookie.getName())) {
cookie = sonCookie;
}
}
}
StringBuffer responseContent = new StringBuffer();//记录响应正文
if (null == cookie) {
//没有浏览记录
responseContent.append("没有浏览记录,");
responseContent.append("浏览商品");
} else {
//有浏览记录
//获取浏览记录 0-1-2-3
String[] bookNames = {
"西游记","红楼梦","水浒传","三国志"};
String historyStr = cookie.getValue();
String[] historys = historyStr.split("-");
responseContent.append("您的浏览记录如下:
");
for (String history : historys) {
//history : 0 / 1 / 2 /3
String bookName = bookNames[Integer.parseInt(history)];
responseContent.append(bookName+"
");
}
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseContent.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
------------------------------
获取指定名称的Cookie对象
public class CookieUtils {
public static Cookie getCookie(Cookie[] cookies,String cookisName){
if(cookies!=null&&cookies.length!=0){
for(Cookie sonCookie:cookies){
if(cookisName.equals(sonCookie.getName())){
return sonCookie;
}
}
}
return null;
}
}
Cookie之所以叫做浏览器会话,原因是Cookie的数据存储到浏览器
Session之所以叫做服务器会话,原因是Session的数据存储到服务器
第一次请求Demo01Servlet时,根据request.getSession方法, 新建一个session对象
当第一次响应时,会将该session对象的id作为cookie头响应给浏览器保存
set-cookie:JSESSIONID=4741C65CC84788A204E87EB870196EB0
第二次请求Demo01Servlet时,根据request.getSession方法,请求中会有cookie头
Cookie:JSESSIONID=4741C65CC84788A204E87EB870196EB0
会根据该JSESSIONID去服务器中找有没有对应的session对象,如果有就直接用,没有就新建
------------------------------
生命周期:session默认是有30分钟的存活时间,参考tomcat中的web.xml
<session-config>
<session-timeout>30session-timeout>
session-config>
session和cookie是相关联的,cookie中存储了jsessionid,request.getSession方法会根据
jsessionid去选择,到底是新建session对象,还是引用原来的session对象;
如果,将浏览器关闭了,就意味着cookie中存储的jsessionid就会销毁,
对应request.getSession就会新建一个session对象,但是原来的session对象还存在
session只有两种情况会销毁
调用了invalidate方法
过了30分钟
------------------------------
setAttribute 往session域对象中存储数据
getAttribute 从session域对象中获取数据
removeAttribute 把session域对象中的数据移除
------------------------------
request是一次请求,session是一次会话,一次会话中包含多次请求
request中的数据(setAttritube)在重定向后失效,不同的request不能共享数据。
session中的数据无论是重定向还是转发,都能拿到。关闭浏览器失效
session默认有效期30分钟(打开浏览器,超过30分钟无操作,session失效)
重启Tomcat清空所有session
------------------------------
session存在于服务端,可以存放任意类型的数据,依赖于cookie中的JSESSIONID
cookie存在于客户端,只能存放字符串
用户可能禁用浏览器的cookie,导致sessionid不能存储在浏览器cookie中
服务端就不能获取用户session信息
encodeRedirect方法,将用户的sessionid拼接到url后面
String url = resp.encodeRedirectURL("/admin/list");
resp.sendRedirect(url);
------------------------------
public class UserDaoImpl implements UserDao {
@Override
public User login(User inputUser) throws SQLException {
// ComboPooledDataSource就是连接池,连接池包含很多连接对象
// 连接池作用就是减少连接的创建次数!
// 第一个用户,登录,创建一个连接池,创建3个连接
// 第二个用户,登录,创建一个连接池,创建3个连接
// 应该只让连接池创建一次!!后面复用就OK了!!
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
User existUser = queryRunner.query(
"select * from tb_user where username = ? and password = ?",
new BeanHandler<User>(User.class),
inputUser.getUsername(),
inputUser.getPassword());`在这里插入代码片`
return existUser;
}
}
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
private UserDao userDao = new UserDaoImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
User inputUser = new User();
inputUser.setUsername(username);
inputUser.setPassword(password);
try {
User existUser = userDao.login(inputUser);
System.out.println(existUser);
//判断登录成功
if (null == existUser) {
//登录失败,请求转发,跳转到登录页面
request.getRequestDispatcher("/login.html").forward(request,response);
} else {
//登录成功,重定向,跳转到显示用户信息
//存储existUser
//request : 跳转到首页,使用了重定向,会有一个新的请求
//servletContext : 如果存储到ServletContext,就意味着所有人都可以拿到你的用户信息!
//cookie : 如果存储到cookie中,就是存储到浏览器 , 不安全! cookie中是无法存储中文及一些特殊符号!!
//session : 数据存储到服务器!!
request.getSession().setAttribute("existUser",existUser);
response.sendRedirect("/day57/showIndex");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
@WebServlet(name = "ShowIndexServlet" ,urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
User existUser = (User) request.getSession().getAttribute("existUser");
if (null != existUser) {
//在登录状态
response.getWriter().write("欢迎回来,"+existUser.getUsername());
} else {
//不在登录状态
//方式一:提示下,未登录
// response.getWriter().write("您还没有登录,请登录");
//方式二:跳转到登录页面
response.sendRedirect("/day57/login.html");
//看需求,选择方式一还是方式二
//登录状态权限管理!!
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
第三方jar包,必须放到WEB-INF文件夹中
登录失败使用请求转发、登录成功使用重定向
---------------
创建图片对象
画背景
画边框
画干扰线
产生四位随机数,存储到session
画四位随机数
将图片响应到浏览器
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 60;//定义图片宽度
int height = 32;//定义图片高度
//创建图片对象
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//创建画笔对象
Graphics g = image.getGraphics();
//设置背景颜色
g.setColor(new Color(0xDCDCDC));
g.fillRect(0, 0, width, height);//实心矩形
//设置边框
g.setColor(Color.black);
g.drawRect(0, 0, width - 1, height - 1);//空心矩形
Random rdm = new Random();
//画干扰椭圆
for (int i = 0; i < 50; i++) {
int x = rdm.nextInt(width);
int y = rdm.nextInt(height);
g.drawOval(x, y, 0, 0);
}
//产生随机字符串
String hash1 = Integer.toHexString(rdm.nextInt());
//生成四位随机验证码
String capstr = hash1.substring(0, 4);
//将产生的验证码存储到session域中,方便以后进行验证码校验!
request.getSession().setAttribute("existCode", capstr);
System.out.println(capstr);
g.setColor(new Color(0, 100, 0));
g.setFont(new Font("Candara", Font.BOLD, 24));
g.drawString(capstr, 8, 24);
g.dispose();
//将图片响应到浏览器
response.setContentType("image/jpeg");
OutputStream strm = response.getOutputStream();
ImageIO.write(image, "jpeg", strm);
strm.close();
}
//获取输入的验证码
String validateCode = request.getParameter("validateCode");
//将输入的验证码和产生的随机验证码进行校验
String existCode = (String) request.getSession().getAttribute("existCode");
if (validateCode.equals(existCode)) {
//校验通过,完成登录功能
} else {
//校验不通过,跳转到登录页面
}
滤器就是一个用于在请求之前处理资源的组件
生命周期
随着服务器启动而初始化
随着请求的发出而过滤
随着服务器关闭而销毁
执行流程
浏览器发起请求
服务器会根据这个请求,创建request对象及response对象
过滤器会持有request对象及response对象
只有当过滤器放行之后,request对象及response对象才会传给Serlvet
过滤器链
根据配置顺序,遵从"先过滤,后放行"的原则
------------------------------
开发步骤
自定义类实现Filter接口
重写init、doFilter、destroy方法
在web.xml中配置过滤器
声明过滤器
过滤器配置过滤路径
public class Demo01Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//Demo01Filter过滤器的初始化
System.out.println("Demo01Filter初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//Demo01Filter过滤器处理请求
System.out.println("Demo01Filter放行之前");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Demo01Filter放行之后");
}
@Override
public void destroy() {
//Demo01Filter过滤器的销毁
System.out.println("Demo01Filter销毁");
}
}
<filter>
<filter-name>Demo01Filterfilter-name>
<filter-class>com.qfedu.filter.Demo01Filterfilter-class>
filter>
<filter-mapping>
<filter-name>Demo01Filterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
------------------------------
<filter>
<filter-name>Demo03Filterfilter-name>
<filter-class>com.qfedu.filter.Demo03Filterfilter-class>
<init-param>
<param-name>usernameparam-name>
<param-value>rootparam-value>
init-param>
<init-param>
<param-name>passwordparam-name>
<param-value>root123param-value>
init-param>
filter>
public class Demo03Filter implements Filter {
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
//获取初始化参数名称
String parameterName = parameterNames.nextElement();
//获取初始化参数值
String parameterValue = config.getInitParameter(parameterName);
System.out.println("name : " + parameterName + " , value : " + parameterValue);
}
}
......
}
<filter-mapping>
<filter-name>Demo03Filterfilter-name>
<servlet-name>Demo01Servletservlet-name>
filter-mapping>
---------------
完全匹配:必须以"/"开头
<filter-mapping>
<filter-name>Demo03Filterfilter-name>
<url-pattern>/aaurl-pattern>
filter-mapping>
过滤器只过滤访问路径完全匹配"/aa"的资源
目录匹配:必须以"/"开头,以"*"结尾
<filter-mapping>
<filter-name>Demo03Filterfilter-name>
<url-pattern>/aa/bb/*url-pattern>
filter-mapping>
过滤器只过滤访问路径目录匹配到“/aa/bb“的资源
后缀名匹配:必须以"*"开头,以后缀名结尾
<filter-mapping>
<filter-name>Demo03Filterfilter-name>
<url-pattern>*.jspurl-pattern>
filter-mapping>
过滤器只过滤后缀名为jsp的资源
------------------------------
@WebFilter注解
WebInitParam[] initParams() default {}; 配置初始化参数
String filterName() default ""; 配置过滤器名称
String[] servletNames() default {}; 配置过滤的Servlet
String[] urlPatterns() default {}; 配置过滤路径
@WebFilter(filterName = "Demo04Filter" ,
urlPatterns = "/demo01",
servletNames = "Demo01Servlet" ,
initParams = {
@WebInitParam(name = "username",value = "root"),
@WebInitParam(name = "password",value = "root123")
})
public class Demo04Filter implements Filter {
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String parameterValue = config.getInitParameter(parameterName);
System.out.println(parameterName + " , " + parameterValue);
}
}
......
}
执行顺序
按照过滤器的类名的字典顺序决定谁先过滤,谁先放行
比如AFilter、BFilter,那么AFilter就会先过滤,BFilter会先放行
------------------------------
public class EncodingFilter implements Filter {
private String encoding = null;
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("encoding");
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//处理响应中文乱码
resp.setContentType("text/html;charset="+encoding);
//处理请求中文乱码
req.setCharacterEncoding(encoding);
chain.doFilter(req, resp);//放行
}
}
<filter>
<filter-name>EncodingFilterfilter-name>
<filter-class>com.qfedu.filter.EncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>EncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
实现步骤
登录账户后,根据是否勾选了自动登录选项框,
判断是否访问和登录相关资源
如果是,直接放行
如果不是,判断是否已经在登录状态
如果是,直接放行
如果不是,需要从cookie中取出存储的用户信息,进行登录操作
如果登录成功,直接放行
如果登录失败,就跳转到登录页面
@WebServlet(name = "LoginServlet" ,urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "root".equals(password)) {
String autoLogin = request.getParameter("autoLogin");
System.out.println(autoLogin);
if ("autoLogin".equals(autoLogin)) {
//进行自动登录,无非就是将用户信息(账户和密码(加密))保存起来!!!
//request、servletContext、cookie、session
Cookie cookie = new Cookie("autoLogin",username+"-"+password);
cookie.setMaxAge(7 * 24 * 60 * 60);
response.addCookie(cookie);
}
//登录成功,转发到一个页面,用来显示用户信息
User existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser",existUser);
request.getRequestDispatcher("/showIndex").forward(request,response);
} else {
//登录失败,转到登录页面
request.getRequestDispatcher("/index.jsp").forward(request,response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
public class AutoLoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
//获取请求路径
String requestURI = request.getRequestURI();
//1,判断访问的资源是否和登录相关
if (requestURI.contains("login")) {
//和登录相关的资源,直接放行
chain.doFilter(request, resp);
} else {
//不是登录相关的资源
//2,判断是否在登录状态
User existUser = (User) request.getSession().getAttribute("existUser");
if (null == existUser) {
//不在登录状态 , 进行自动登录
//获取Cookie
Cookie cookie = CookieUtils.getCookie(request.getCookies(), "autoLogin");
//判断cookie是否为空 , 存在浏览器清理缓存
if (null == cookie) {
//浏览器清理缓存 , 相当于自动登录失败!! 跳转到登录页面,进行手动登录
request.getRequestDispatcher("/login.jsp").forward(request,resp);
} else {
//还有缓存,进行自动登录
//获取用户信息 root-root
String infoStr = cookie.getValue();
String[] infos = infoStr.split("-");
String username = infos[0];
String password = infos[1];
if ("root".equals(username) && "root".equals(password)) {
//自动登录成功 ,修改登录状态, 直接放行 ,意味着,还不在登录状态!!!
existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser",existUser);
chain.doFilter(req,resp);
} else {
//自动登录失败 (修改了密码) ,跳转到登录页面,进行手动登录
request.getRequestDispatcher("/login.jsp").forward(request,resp);
}
}
} else {
//在登录状态 , 直接放行
chain.doFilter(request,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
对request对象进行增强。增强获取参数相关方法
放行。传递增强的请求方法
@WebFilter(
filterName = "SensitiveWordsFilter" ,
urlPatterns = "/*",
initParams = {
@WebInitParam(name = "word1",value = "笨蛋"),
@WebInitParam(name = "word2" ,value = "傻瓜"),
@WebInitParam(name = "word3" ,value = "尼玛"),
@WebInitParam(name = "word4",value = "靠")
})
public class SensitiveWordsFilter implements Filter {
//敏感词
List<String> sensitiveWords = new ArrayList<>();
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String sensitiveWord = config.getInitParameter(parameterNames.nextElement());
sensitiveWords.add(sensitiveWord);
}
}
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("SensitiveWordsFilter doFilter");
HttpServletRequest request = (HttpServletRequest) req;
//增强request下的getParameter方法
HttpServletRequest requestPrxoy = (HttpServletRequest) Proxy.newProxyInstance(
request.getClass().getClassLoader(),
request.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强getParameter方法
Object returnValue = null;
String methodName = method.getName();
if ("getParameter".equals(methodName)) {
//returnValue就是getParameter方法的返回值,可能会存在敏感词
String returnValue1 = (String)method.invoke(request, args);
//开始处理敏感词
for (String sensitiveWord : sensitiveWords) {
if (returnValue1.contains(sensitiveWord)) {
//getParameter方法的返回值包含敏感词
returnValue1 = returnValue1.replace(sensitiveWord,"***");
}
}
return returnValue1;
} else {
returnValue = method.invoke(request, args);
}
return returnValue;
}
});
chain.doFilter(requestPrxoy, resp);
}
}
监听器概念
事件源:事件发生的源头
监听器:监听事件发生
绑定:将监听器绑定到事件源
事件:能够触发监听器的事
Servlet监听器
事件源:request域对象、session域对象、ServletContext域对象
监听器:Servlet三种监听器
绑定:配置web.xml
事件:域对象发生改变
------------------------------
一类监听器 监听域对象的创建、销毁
二类监听器 监听域对象中的属性变更(属性设置、属性替换、属性移除)
三类监听器 监听域对象的java对象的绑定
ServletRequestListener : 监听ServletRequest域对象的创建、销毁
HttpSessionListener :监听HttpSession域对象的创建、销毁
ServletContextListener : 监听ServletContext域对象的创建、销毁
开发步骤
自定义类实现一类监听器
重写监听器中的方法
配置web.xml
public class MyListener01 implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//监听ServletContext域的初始化,随着服务器的启动
System.out.println("ServletContext初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//监听ServletContext域的销毁,随着服务器的关闭
System.out.println("ServletContext销毁");
}
}
<listener>
<listener-class>com.qfedu.listener.MyListener01listener-class>
listener>
事件源: ServletContext域对象
监听器:ServletContextListener
绑定: web.xml配置
事件 : ServletContext域对象发生了创建、发生了销毁
ServletRequestAttributeListener :监听ServletRequest域对象中属性变更
HttpSessionAttributeListener : 监听HttpSession域对象中属性变更
ServletContextAttributeListener : 监听ServletContext域对象中属性变更
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性添加
System.out.println("ServletContext added");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性值被替换
System.out.println("ServletContext replaced");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
//监听ServletContext域对象中属性值移除
System.out.println("ServletContext removed");
}
}
<listener>
<listener-class>com.qfedu.listener.MyServletContextAttributeListenerlistener-class>
listener>
HttpSessionBindingListener
监听session域中的java对象的状态(绑定和解绑)
绑定:将java对象存储到session域对象
解绑:将java对象从session域对象移除
监听器组成
事件源:java对象
监听器:HttpSessionBindingListener
绑定:java对象实现HttpSessionBindingListener接口
事件:java对象在session中状态发生改变
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("User绑定");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("User解绑");
}
......
}
HttpSessionBindingListener监听不需要在web.xml配置
------------------------------
@WebListener 相当于在web.xml绑定了监听器
@WebListener
public class MyServletContextLIstener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext创建");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext销毁");
}
}
------------------------------
登录功能
登录失败,转发到登录页面
登录成功,记录登录状态,重定向到首页(显示用户名、注销登录、在线人数)
注销登录
注销登录成功;正常来说,应该转发到登录页面;转发到首页
在线人数
使用HttpSessionBindingListener监听器
使用ServletContext域对象,存储在线人数count
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录title>
head>
<body>
<form action="/day59/login" method="post">
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
<button type="submit">登录button>
form>
body>
html>
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
//有人登录成功 , 在线人数(count)加1
//判断是否是第一个登录成功的人
//获取ServletContext
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
if (null == count) {
//就是第一个登录成功的人
count = 1;
} else {
//不是第一个登录成功的人
count++;
}
servletContext.setAttribute("count", count);
}
//在同一个浏览器,意味着是同一个session
//第一次登录 ,session.setAttribute("existUser","root") , valueBound +1
//第二次登录 ,session.setAttribute("existUser","root1") , valueBound +1 -> valueUnbound -1
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
//有人注销登录 ,在线人数(count)减1
System.out.println("User解绑");
ServletContext servletContext = event.getSession().getServletContext();
Integer count = (Integer) servletContext.getAttribute("count");
count--;
servletContext.setAttribute("count", count);
}
private Integer id;
private String username;
private String password;
public User() {
}
public User(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
在线人数:在同一个浏览器,操作多次登录,意味着是同一个session
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "root".equals(password)) {
//登录成功 , 修改登录状态 ,跳转到ShowIndexServlet
User existUser = new User();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser", existUser);
// request.getRequestDispatcher("/showIndex").forward(request,response);
response.sendRedirect("/day59/showIndex");
} else {
//登录失败,转发到登录页面,重新登录
request.getRequestDispatcher("/login.html").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//注销登录 , 将existUser变量从session域中移除!
// request.getSession().removeAttribute("existUser");
//注销登录,将session销毁 -> 将existUser变量从session域中移除!
request.getSession().invalidate();
//注销成功
request.getRequestDispatcher("/showIndex").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
User existUser = (User) request.getSession().getAttribute("existUser");
StringBuffer responseBody = new StringBuffer();
if (null == existUser) {
//不在登录状态,提示
responseBody.append("您还没有登录;请登录
");
} else {
//在登录状态,显示信息
responseBody.append("欢迎回来," + existUser.getUsername() + " 注销
");
}
ServletContext servletContext = getServletContext();
//获取在线人数count
Integer count = (Integer) servletContext.getAttribute("count");
System.out.println("在线人数 " + count);
//count变量为null,在没有任何人登录状态下,访问了ShowIndexServlet
if (null == count) {
//没有任何人在登录状态 ,在线人数处理成0人
count = 0;
} else {
//有人在登录状态 ,直接输出在线人数count人
}
responseBody.append("在线人数 : " + count);
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
开发步骤
获取用户列表放到首页完成
登录状态才需要获取用户列表
@Override
public List<User> selectUserList() throws Exception {
return new QueryRunner(JDBCUtil.getDataSource())
.query("select * from tb_user",
new BeanListHandler<User>(User.class));
}
@WebServlet(name = "ShowIndexServlet" ,urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
User existUser = (User) request.getSession().getAttribute("existUser");
StringBuffer responseBody = new StringBuffer();
if (null != existUser) {
//已经在登录状态
responseBody.append("欢迎回来~ " + existUser.getUsername());
responseBody.append(" 注销
");
//获取用户列表
UserDao userDao = new UserDaoImpl();
try {
List<User> userList = userDao.selectUserList();
System.out.println(userList);
responseBody.append("");
responseBody.append("");
responseBody.append("ID ");
responseBody.append("账户 ");
responseBody.append("密码 ");
responseBody.append(" ");
for (User user : userList) {
//遍历一个User对象,对应就应该有一个tr
responseBody.append("");
responseBody.append(""+user.getId()+" ");
responseBody.append(""+user.getUsername()+" ");
responseBody.append(""+user.getPassword()+" ");
responseBody.append(" ");
}
responseBody.append("
");
} catch (Exception e) {
e.printStackTrace();
}
} else {
//不在登录状态
responseBody.append("您还没有登录,请登录
");
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
开发步骤
首页,给每一个记录后面添加一个删除按钮
用户列表不要显示当前登录用户
@Override
public void deleteUserById(Integer id) throws Exception {
new QueryRunner(JDBCUtil.getDataSource())
.update("delete from tb_user where id = ?",id);
}
responseBody.append("");
responseBody.append("");
responseBody.append("ID ");
responseBody.append("账户 ");
responseBody.append("密码 ");
responseBody.append("操作 ");
responseBody.append(" ");
for (User user : userList) {
if ( user.getId() != existUser.getId()) {
//遍历一个User对象,对应就应该有一个tr
responseBody.append("");
responseBody.append(""+user.getId()+" ");
responseBody.append(""+user.getUsername()+" ");
responseBody.append(""+user.getPassword()+" ");
responseBody.append("删除 ");
responseBody.append(" ");
}
}
responseBody.append("
");
@WebServlet(name = "DeleteUserServlet" ,urlPatterns = "/deleteUser")
public class DeleteUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String idStr = request.getParameter("id");
Integer id = Integer.parseInt(idStr);
UserDao userDao = new UserDaoImpl();
try {
userDao.deleteUserById(id);
} catch (Exception e) {
e.printStackTrace();
}
response.sendRedirect("/day60/showIndex");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
开发流程
在首页需要给每一个记录新加多选框
当点击批量删除按钮之后,完成批量删除操作
public void deleteUsersByIds(List<Integer> idList) throws Exception {
for (Integer id : idList) {
deleteUserById(id);
}
}
responseBody.append(");
responseBody.append("");
responseBody.append("");
responseBody.append(" ");
responseBody.append("ID ");
responseBody.append("账户 ");
responseBody.append("密码 ");
responseBody.append("操作 ");
responseBody.append(" ");
for (User user : userList) {
if ( user.getId() != existUser.getId()) {
//遍历一个User对象,对应就应该有一个tr
responseBody.append("");
responseBody.append(" ");
......
responseBody.append(" ");
}
}
responseBody.append("
");
responseBody.append("");
responseBody.append("");
开发步骤
首页加入一个添加用户按钮
点击添加用户按钮进入到登录页面
public void addUser(User inputUser) throws Exception {
new QueryRunner(JDBCUtil.getDataSource())
.update("insert into tb_user(username ,password) values(?,?)",
inputUser.getUsername(),
inputUser.getPassword());
}
//已经在登录状态
responseBody.append("欢迎回来~ " + existUser.getUsername());
responseBody.append(" 注销
");
responseBody.append("添加用户
");
@WebServlet(name = "AddUserServlet" ,urlPatterns = "/addUser")
public class AddUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
User inputUser = new User();
inputUser.setUsername(username);
inputUser.setPassword(password);
//方式一:在添加之前,校验下用户名是否存在
//方式二:让username字段有唯一约束!
UserDao userDao = new UserDaoImpl();
try {
userDao.addUser(inputUser);
//添加成功,跳转到首页
response.sendRedirect("/day60/showIndex");
} catch (Exception e) {
e.printStackTrace();
//添加失败,跳转到添加用户页面
response.sendRedirect("/day60/add.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
开发步骤
首页加入修改按钮
@Override
public void updateUser(User user) throws Exception {
new QueryRunner(JDBCUtil.getDataSource())
.update("update tb_user set username = ? , password = ? where id = ?",
user.getUsername(),
user.getPassword(),
user.getId());
}
for (User user : userList) {
if ( user.getId() != existUser.getId()) {
//遍历一个User对象,对应就应该有一个tr
responseBody.append("");
responseBody.append(" ");
responseBody.append(""+user.getId()+" ");
responseBody.append(""+user.getUsername()+" ");
responseBody.append(""+user.getPassword()+" ");
responseBody.append("" +
"删除 " +
"修改"+
" ");
responseBody.append(" ");
}
}
@WebServlet(name = "ShowUpdateUserServlet" , urlPatterns = "/showUpdateUser")
public class ShowUpdateUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Integer id = Integer.parseInt(request.getParameter("id"));
//根据id查询用户
UserDao userDao = new UserDaoImpl();
StringBuffer responseBody = new StringBuffer();
try {
User user = userDao.selectUserById(id);
System.out.println(user);
responseBody.append(");
responseBody.append("");
responseBody.append("账户:
");
responseBody.append("密码:
");
responseBody.append("");
responseBody.append("");
} catch (Exception e) {
e.printStackTrace();
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
@WebServlet(name = "UpdateUserServlet" ,urlPatterns = "/updateUser")
public class UpdateUserServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Integer id = Integer.parseInt(request.getParameter("id"));
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username : " + username + ", password : " + password);
//要修改的内容:username、password
User user = new User(id,username,password);
UserDao userDao = new UserDaoImpl();
try {
userDao.updateUser(user);
//修改成功,跳转到首页
response.sendRedirect("/day60/showIndex");
} catch (Exception e) {
e.printStackTrace();
//修改失败,跳转到修改页面
response.sendRedirect("/day60/showUpdateUser?id="+id);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
完整版案例
让一个Servlet可以处理多种不同的请求,不同的请求调用Servlet的不同方法。
客户端发送请求时, 必须多给出一个参数, 用来说明要调用的方法。这样,BaseServlet 通过该参数来
调用目标方法。
请求处理方法的签名必须与 service 相同, 即方法的返回值和参数,以及声明的异常都相同。
@WebServlet(name = "UserServlet",urlPatterns = "/user")
public class UserServlet extends BaseServlet {
public void login(HttpServletRequest request,HttpServletResponse response){
String username = request.getParameter("username");
String password = request.getParameter("password");
User inputUser = new User();
inputUser.setUsername(username);
inputUser.setPassword(password);
UserDao userDao = new UserDaoImpl();
try {
User existUser = userDao.login(inputUser);
if (null == existUser) {
//登录失败
request.getRequestDispatcher("/login.html").forward(request,response);
} else {
//登录成功
request.getSession().setAttribute("existUser",existUser);
response.sendRedirect("/day61/user?methodName=showIndex");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void showIndex(HttpServletRequest request , HttpServletResponse response) throws Exception {
System.out.println("showIndex");
User existUser = (User) request.getSession().getAttribute("existUser");
StringBuffer responseBody = new StringBuffer();
if (null != existUser) {
//在登录状态
responseBody.append("欢迎回来~~~"+existUser.getUsername());
responseBody.append("注销登录");
} else {
//不在登录状态
responseBody.append("您还没有登录;");
responseBody.append("请登录");
}
response.setContentType("text/html;charset=utf-8");
response.getWriter().write(responseBody.toString());
}
public void logout(HttpServletRequest request , HttpServletResponse response) throws Exception {
request.getSession().invalidate();
response.sendRedirect("/day61/login.html");
}
}
资源跳转问题
响应正文中文乱码
@WebServlet(name = "BaseServlet",urlPatterns = "/base")
public class BaseServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
String methodName = request.getParameter("methodName");
try {
Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
if (null != method) {
//处理返回值,为了资源跳转
String returnValue = (String) method.invoke(this,request,response);
System.out.println(returnValue);
//有的方法有返回值
//登录成功:重定向 redirect:/day61/user?methodName=showIndex
//登录失败: 请求转发 forward:/login.html
//有的方法没有返回值
//null,不用做任何处理
if (returnValue != null){
//有返回值,实现资源跳转,需要资源的路径
if (returnValue.lastIndexOf(":") != -1) {
String path = returnValue.split(":")[1];
System.out.println("path : "+path);
//有":"
//实现资源跳转:重定向、请求转发
if (returnValue.startsWith("redirect")) {
//重定向
response.sendRedirect(request.getContextPath()+path);
} else if(returnValue.startsWith("forward")){
//请求转发
request.getRequestDispatcher(path).forward(request,response);
}
} else {
//没有":",默认就是转发 /login.html
request.getRequestDispatcher(returnValue).forward(request,response);
}
} else {
//不做任何处理
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
之前方案
login.html
登录页面
LoginServlet
处理登录请求
业务处理
调用dao,操作数据
ShowIndexServlet(java代码)
显示用户名
存在的问题
login.html
无法和java后台进行交互,无法显示错误信息
ShowIndexServlet
html无法和java后台进行交互,java代码应该专注于后台代码
现在方案
login.jsp
登录页面
显示错误信息
LoginServlet
处理登录请求
业务处理
调用dao,操作数据
index.jsp
显示用户名
<%--显示错误信息--%>
<%="" + (request.getAttribute("errorMsg") == null ? "" : request.getAttribute("errorMsg") ) + ""%>
<form action="/day62/user" method="post">
<input type="hidden" name="methodName" value="login"/>
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
<button type="submit">登录</button>
</form>
<%
String username = (String) session.getAttribute("username");
if (null == username) {
//不在登录状态
out.write("您还没有登录;");
out.write("请登录");
} else {
//在登录状态
out.write("欢迎回来~~~"+username);
}
%>
存在的问题
jsp页面还存在java代码(后台代码)和html代码(前端代码)融合在一起的问题!
一定程度解决,可能需要jstl标签库。
彻底解决,得使用vue + html
环境准备
tomcat8.5
idea2020.1
jdk1.8
导入jar包
BeanUtils
DbUtils
c3p0
mysql驱动
jstl
开发流程
UserServlet
login方法 调用UserService的login方法
UserService
login方法,返回User对象 调用UserDao的login方法
UserDao
login方法,返回User对象 操作数据库,并将记录封装到User对象
登录成功
修改登录状态,重定向到首页
登录失败
记录错误信息,请求转发到登录页面
@Override
public User login(User inputUser) throws Exception {
return new QueryRunner(JDBCUtils.getDataSource())
.query("select * from tb_user where username = ? and password = ?",
new BeanHandler<User>(User.class),
inputUser.getUsername(),
inputUser.getPassword());
}
@Override
public User login(User inputUser) throws Exception {
UserDao userDao = new UserDaoImpl();
return userDao.login(inputUser);
}
private UserService userService = new UserServiceImpl();
public String login(HttpServletRequest request,HttpServletResponse response){
User inputUser = new User();
//将请求参数封装到inputUser中
try {
BeanUtils.populate(inputUser,request.getParameterMap());
User existUser = userService.login(inputUser);
//没有异常有返回值
if (null != existUser) {
//登录成功 , 修改登录状态,跳转到首页
request.getSession().setAttribute("existUser",existUser);
return "redirect:/index.jsp";
} else {
//登录失败 ,记录错误信息,跳转到登录页面
request.setAttribute("errorMsg","账户或密码错误");
return "/login.jsp";
}
} catch (Exception e) {
e.printStackTrace();
}
//有异常,应该有返回值 。意味着登录失败,返回登录页面,重新登录
return "/login.jsp";
}
<body>
<font color="red">${errorMsg}font>
<form action="${pageContext.request.contextPath}/user" method="post">
<input type="hidden" name="methodName" value="login"/>
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
<button type="submit">登录button>
form>
body>
<body>
<%--
在登录状态
显示用户名
--%>
<c:if test="${existUser != null}">
欢迎回来~~~ ${existUser.username} <a href="${pageContext.request.contextPath}/user?methodName=logout">注销a>
c:if>
<%--
不在登录状态
提示登录
--%>
<c:if test="${existUser == null}">
您还没有登录,<a href="${pageContext.request.contextPath}/login.jsp">请登录a>
c:if>
body>
开发步骤
login.jsp上需要加一个“注销”的按钮
注销成功 跳转到登录页面
注销失败 跳转到首页,重新进行手动注销
public String logout(HttpServletRequest request,HttpServletResponse response){
try {
request.getSession().invalidate();
return "redirect:/login.jsp";
} catch (Exception e) {
e.printStackTrace();
return "/index.jsp";
}
}
开发步骤
登录成功,重定向到UserServlet中的selectUserList方法
在selectUserList方法中获取用户列表,并将其保存
再请求转发到index.jsp
@Override
public List<User> selectUserList() throws Exception {
return new QueryRunner(JDBCUtils.getDataSource())
.query("select * from tb_user",
new BeanListHandler<User>(User.class));
}
public String selectUserList(HttpServletRequest request,HttpServletResponse response) throws Exception {
List<User> userList = userService.selectUserList();
request.getSession().setAttribute("userList",userList);
return "/index.jsp";
}
<body>
<%--
在登录状态
显示用户名
--%>
<c:if test="${existUser != null}">
欢迎回来~~~ ${existUser.username} <a href="${pageContext.request.contextPath}/user?methodName=logout">注销a>
<%--显示用户列表--%>
<table border="1px" cellspacing="0px" cellpadding="15px" width="500px" height="200px">
<tr>
<td>IDtd>
<td>账户td>
<td>密码td>
tr>
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.id}td>
<td>${user.username}td>
<td>${user.password}td>
tr>
c:forEach>
table>
c:if>
<%--
不在登录状态
提示登录
--%>
<c:if test="${existUser == null}">
您还没有登录,<a href="${pageContext.request.contextPath}/login.jsp">请登录a>
c:if>
body>
需求:只要不在登录状态就自动跳转到登录页面
开发步骤
定义过滤器
判断是否和登录相关
和登录相关,直接放行
访问登录页面
处理登录,请求参数methodName="login"
和登录无关,进行登录状态判断
不在登录状态,跳转到login.jsp
在登录状态,直接放行
@WebFilter(filterName = "LoginFilter" ,urlPatterns = "/*")
public class LoginFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("LoginFilter doFilter");
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//登录校验
String requestURI = request.getRequestURI();
String methodName = request.getParameter("methodName");
if (requestURI.contains("login") || ( methodName != null && methodName.equals("login"))) {
//和登录相关的资源,直接放行
chain.doFilter(req, resp);
} else {
//和登录无关的资源
Object existUser = request.getSession().getAttribute("existUser");
if (null == existUser) {
//不在登录状态,跳转到login.jsp
response.sendRedirect("/day64/login.jsp");
} else {
//在登录状态,直接放行
chain.doFilter(req, resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
}
开发步骤
给table每一行加删除按钮
不管是删除成功,还是删除失败,都应该重新获取用户列表并显示
转发到UserServlet类的selectUserList方法
......
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.id}td>
<td>${user.username}td>
<td>${user.password}td>
<td>
<a href="${pageContext.request.contextPath}/user?methodName=deleteUserById&id=${user.id}">删除a>
td>
tr>
c:forEach>
......
@Override
public int deleteUserById(Integer id) throws Exception {
return new QueryRunner(JDBCUtils.getDataSource())
.update("delete from tb_user where id = ?",
id);
}
@Override
public boolean deleteUserById(Integer id) throws Exception {
return userDao.deleteUserById(id) == 1;
}
public String deleteUserById(HttpServletRequest request,HttpServletResponse response){
Integer id = Integer.parseInt(request.getParameter("id"));
try {
boolean flag = userService.deleteUserById(id);
} catch (Exception e) {
e.printStackTrace();
}
//获取用户列表
return "/user?methodName=selectUserList";
}
开发步骤
在table的每一行中加修改按钮
当点击table中修改按钮,跳转到UserServlet类中的toUpdateUserById方法
UserServlet类中的toUpdateUserById方法
获取用户信息
跳转到修改用户页面
当点击修改页面中的修改按钮,开始修改
修改成功,跳转到UserServlet类中的selectUserList方法
修改失败,跳转到toUpdateUserById方法,让它重新进行修改用户
@Override
public void updateUserById(User inputUser) throws Exception {
new QueryRunner(JDBCUtils.getDataSource())
.update("update tb_user set username = ? , password = ? where id = ?",
inputUser.getUsername(),
inputUser.getPassword(),
inputUser.getId());
}
public String toUpdateUserById(HttpServletRequest request,HttpServletResponse response) {
Integer id = Integer.parseInt(request.getParameter("id"));
User user = null;
try {
user = userService.selectUserById(id);
} catch (Exception e) {
e.printStackTrace();
}
request.setAttribute("user",user);
return "/updateUser.jsp";
}
<body>
<font color="red">${
errorMsg}</font>
<form action="${pageContext.request.contextPath}/user" method="post">
<input type="hidden" name="methodName" value="updateUserById"/>
<input type="hidden" name="id" value="${user.id}">
账户:<input type="text" name="username" value="${user.username}"><br>
密码:<input type="text" name="password" value="${user.password}"><br>
<button type="submit">修改</button>
</form>
</body>
public String updateUserById(HttpServletRequest request,HttpServletResponse response){
User inputUser = new User();
try {
BeanUtils.populate(inputUser,request.getParameterMap());
userService.updateUserById(inputUser);
//修改成功
return "/user?methodName=selectUserList";
} catch (Exception e) {
e.printStackTrace();
}
request.setAttribute("errorMsg","修改失败");
//修改失败
return "/user?methodName=toUpdateUserById&id="+inputUser.getId();
}
分页查询概念在MySQL知识点总结10.8
select * from 表名 limit ? , ?
#第一个?:开始查询脚标(从0开始)
#第二个?:查询多少个
# 需求:每页数据有2条
-- 第一页 1、2;开始脚标为0
-- 从脚标为0的记录开始,往后查2个!
SELECT * FROM tb_user LIMIT 0 , 2;
-- 第二页 3、5;开始脚标为2
SELECT * FROM tb_user LIMIT 2 , 2;
-- 第三页 7、8;开始脚标为4
SELECT * FROM tb_user LIMIT 4 , 2;
-- 第四页 12;开始脚标为6
SELECT * FROM tb_user LIMIT 6 , 2;
问题一:当浏览器发起分页请求时,浏览器需要传递什么参数给服务器?
答:当前页数
问题二:当服务器响应浏览器时,服务器需要传递什么参数给浏览器?
答:都是浏览器需要的数据;如下:
当前页数(currentPage)
总页数(totalPage)
总记录数(totalSize)
每页记录数(pageSize)
当前页数据(list)
问题三:当服务器向浏览器响应上面的五个参数时,是将五个参数作为整体传递还是独立传递?
答:服务器向浏览器传递参数,无非是通过域!
如果是独立传递,那么就需要在域中存储五个参数(currentPage、totalPage、totalSize、pageSize、list)
如果是整体传递,那么只需要在域中存储一个参数(PageBean)
总结:将五个参数作为整体
问题四:分页对象PageBean中的五个参数是在哪一层代码中确定的?
答:当前页数(currentPage):controller层
总页数(totalPage):service层
总记录数(totalSize):获取是在dao层,调用是在service层
每页记录数(pageSize):service层
当前页数据(list):获取是在dao层
总结:以上五个参数封装成分页对象(PageBean),是在service层进行的
步骤:
确定每页显示数据数量。
确定分页显示所需总页数
编写SQL查询语句,实现数据查询
在JSP页面中进行分页显示设置
pageSize = 10 //每页10条数据
total = 100; //一共100条数据 分10页
total = 105; // 一共105条数据 分11页
if(total % pageSize == 0){
num = total / pageSize; //页数
}
if(total % pageSize != 0){
num = total / pageSize + 1; //页数
}
int pageNum ; //当前页码
limit (pageNum-1)*pageSize,pageSize //sql语句
处理当前页数
获取分页对象PageBean
页面跳转
public String selectUserListByPage(HttpServletRequest request, HttpServletResponse response){
String currentPageStr = request.getParameter("currentPage");
Integer currentPage = getCurrentPage(currentPageStr);
//调用UserService,传递currentPage
try {
PageBean<User> pageBean = userService.selectUserListByPage(currentPage);
System.out.println(pageBean);
request.setAttribute("pageBean",pageBean);
//转发到用户列表页面
return "/userList.jsp";
} catch (Exception e) {
e.printStackTrace();
}
return "/index.jsp";
}
设置当前页数(currentPage)
设置总记录数(totalSize)
设置每页记录数(pageSize)
设置总页数(totalPage) 总页数 = (总记录数 % 每页记录数 == 0) ? 总记录数/ 每页记录数 : 总记录数/ 每页记录数 + 1;
设置当前页数据(list) begin = (currentPage - 1) * pageSize
public PageBean<User> selectUserListByPage(Integer currentPage) throws Exception {
PageBean<User> pageBean = new PageBean<>();
// currentPage;当前页数
pageBean.setCurrentPage(currentPage);
// totalSize;总记录数
UserDao userDao = new UserDaoImpl();
Integer totalSize = userDao.selectTotalSize();
pageBean.setTotalSize(totalSize);
// pageSize;每页记录数
Integer pageSize = 3;
pageBean.setPageSize(pageSize);
// totalPage:总页数 = 总记录数 / 每页记录数
Integer totalPage = ( totalSize % pageSize == 0 ) ? ( totalSize / pageSize ) : (totalSize / pageSize + 1 ) ;
pageBean.setTotalPage(totalPage);
// list;当前页数据
//select * from tb_user limit ? , ?
//第一个? : 开始脚标 = (当前页数 - 1) * 每页记录数
//第二个? : 查询多少个 (每页记录数) pageSize
Integer begin = (currentPage - 1) * pageSize;
List<User> list = userDao.selectUserListByPage(begin, pageSize);
pageBean.setList(list);
return pageBean;
}
获取总记录数
获取当前页数据
public class UserDaoImpl implements UserDao {
@Override
public Integer selectTotalSize() throws Exception {
return new QueryRunner(JDBCUtils.getDataSource())
.query("select * from tb_user",
new BeanListHandler<User>(User.class))
.size();
}
@Override
public List<User> selectUserListByPage(Integer begin, Integer pageSize) throws Exception {
return new QueryRunner(JDBCUtils.getDataSource())
.query("select * from tb_user limit ? , ?" ,
new BeanListHandler<User>(User.class),
begin,
pageSize);
}
}
<body>
<table border="1px" cellspacing="0px" cellpadding="10px" width="600px" height="200px">
<tr>
<td>IDtd>
<td>账户td>
<td>密码td>
tr>
<c:forEach items="${pageBean.list}" var="user">
<tr>
<td>${user.id}td>
<td>${user.username}td>
<td>${user.password}td>
tr>
c:forEach>
<%--分页工具条--%>
<tr>
<td colspan="3" align="center">
第${pageBean.currentPage}/${pageBean.totalPage}页
总记录数:${pageBean.totalSize}
每页显示${pageBean.pageSize}条
<c:if test="${pageBean.currentPage != 1}">
<a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=1">
[首页]
a>
<a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=${pageBean.currentPage-1}">
[上一页]
a>
c:if>
<c:if test="${pageBean.currentPage != pageBean.totalPage}">
<a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=${pageBean.currentPage+1}">
[下一页]
a>
<a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=${pageBean.totalPage}">
[尾页]
a>
c:if>
td>
tr>
table>
body>
通过Page类分页查询
本质就是将一台电脑中的文件根据网络协议通过io流传递到另外一台电脑(服务器)上
网络编程
ip:电脑在网络中的唯一标识
port:软件在电脑中的唯一标识
协议:不同设备进行沟通的规则!
概念:
当用户在前端页面点击文件上传后,文件数据交给服务器端,实现保存
步骤:
编写文件上传表单
编写服务器程序接收文件
文件上传表单提交方式:post
表单提交类型:mulitipart/form-data
<form action="" method="post" enctype="multipart/form-data">
文件:<input type="file" name="up">
<input type="submit" value="上传">
form>
核心步骤:
· 编写Servlet
· 使用@MultipartConfig注解
· 使用javax.servlet.http.Part接口接收文件类型
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* 文件上传
* 如果让Servlet支持文件上传,需要加一个注解@MultipartConfig(servlet3.0以后才有)
*/
@WebServlet(name = "UploadServlet", urlPatterns = "/upload")
// maxFileSize单个文件大小,单位字节;
// maxRequestSize请求中多个文件的总大小;
@MultipartConfig(maxFileSize = 1024 * 1024, maxRequestSize = 1024 * 1024 * 2)
public class UploadServlet extends HttpServlet {
// 限制上传文件类型
private List<String> FILE_TYPES = new ArrayList<>();
public UploadServlet() {
FILE_TYPES.add(".jpg");
FILE_TYPES.add(".png");
FILE_TYPES.add("gif");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//拿到文件
Part part = request.getPart("up");
String fileName = part.getSubmittedFileName();//用户上传的文件名
String ext = fileName.substring(fileName.lastIndexOf("."));//扩展名
PrintWriter writer = response.getWriter();
if (FILE_TYPES.contains(ext)) {
part.write("D:\\upload\\" + System.currentTimeMillis() + ext);//以文件流的形式存入磁盘,加上时间戳
writer.write("上传成功");
} else {
writer.write("文件格式错误");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
@WebServlet(name = "DownloadServlet", urlPatterns = "/download")
public class DownloadServlet extends HttpServlet {
private String path = "D:\\load\\";// 服务端存放文件的地方
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = request.getParameter("name");
response.setHeader("content-disposition","attachment:filename="+ URLEncoder.encode(name,"utf-8"));
File file = new File(path+name);
FileInputStream inputStream = new FileInputStream(file);//从系统中读取文件
ServletOutputStream outputStream = response.getOutputStream();// 响应流
// 从水池1中取水,放到水池2
byte[] buf = new byte[1024];//水桶
int len = 0;//水桶每次从水池1拿多少水,等于-1,代表水池已经空了
while ((len = inputStream.read(buf))!=-1) {
//从水池1中给水桶灌水
outputStream.write(buf,0,len);//把水桶里的水,倒进水池2,偏移量 最后一次可能桶没装满
}
outputStream.close();
inputStream.close();
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@WebServlet(name = "MutilUploadServlet", urlPatterns = "/mutilupload")
@MultipartConfig(maxFileSize = 1024 * 1024, maxRequestSize = 1024 * 1024 * 2)
public class MutilUploadServlet extends HttpServlet {
private List<String> FILE_TYPES = new ArrayList<>();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//拿到文件
Collection<Part> parts = request.getParts();//多文件上传,拿到的是请求中所有的内容,包括文字和文件
for (Part part : parts) {
String name = part.getName();//参数名
String fileName = part.getSubmittedFileName();//代表当前的part是一个文字参数,文字参数没有文件名
if (fileName != null) {
//是文件
System.out.println("文件名" + fileName);
System.out.println(request.getRemoteAddr());
String ext = fileName.substring(fileName.lastIndexOf("."));
PrintWriter writer = response.getWriter();
if (FILE_TYPES.contains(ext)) {
part.write("D:\\upload\\" + System.currentTimeMillis() + ext);//以文件流的形式,写入磁盘
writer.write("上传成功");
} else {
writer.write("文件格式错误");
}
} else {
System.out.println(name + "=" + request.getParameter(name));
}
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
BaseServlet