一、web.xml
web.xml是web程序中的核心
一个简单的servlet:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet extends HttpServlet { // 继承HttpServlet并实现service方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html"); // 设置响应类型
response.setCharacterEncoding("utf-8"); // 设置字符编码
// 获取响应的输出流
PrintWriter writer = response.getWriter();
writer.println("hello servlet");
}
}
使用web.xml方式配置servlet:
<!--注册servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.yang.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet对应的mapping(映射)-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<!--访问路径-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
使用注解方式配置servlet:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet { // 继承HttpServlet并实现service方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html"); // 设置响应类型
response.setCharacterEncoding("utf-8"); // 设置字符编码
// 获取响应的输出流
PrintWriter writer = response.getWriter();
writer.println("hello servlet");
}
}
pom.xml文件添加依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
二、mapping问题
- 一个servlet可以对应多个mapping,也就是说一个servlet可以有多个映射路径
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.yang.servlet.HelloServlet</servlet-class>
</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>
此时浏览器访问http:localhost:8080/hello和http:localhost:8080/hello2都是访问name为hello的servlet
- 访问固定路径时,优先级比通配符的优先级高
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.yang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>hello02</servlet-name>
<servlet-class>com.yang.servlet.HelloServlet02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello02</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
此时访问到的资源是name为hello的servlet
三、ServletContext
每个web程序都有一个对应的ServletContext对象,可以理解为它是当前的web应用
ServletContext对象的作用:
数据共享
在一个servlet中保存的信息,可以被另一个servlet拿到
package com.yang.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
// setAttribute方法存储信息
servletContext.setAttribute("username","jack");
response.setContentType("text/html;");
response.setCharacterEncoding("utf-8");
response.getWriter().print("存入数据到ServletContext对象中...");
}
}
package com.yang.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String username = (String) servletContext.getAttribute("username");
response.setContentType("text/html;");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.print("从ServletContext对象中获取到username=" + username);
}
}
web.xml:
<servlet>
<servlet-name>servlet01</servlet-name>
<servlet-class>com.yang.servlet.Servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet01</servlet-name>
<url-pattern>/servlet01</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>servlet02</servlet-name>
<servlet-class>com.yang.servlet.Servlet02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet02</servlet-name>
<url-pattern>/servlet02</url-pattern>
</servlet-mapping>
拓展:同一个web应用中的每一个servlet和当前servlet的请求对象request都能通过getServletContext方法获取到当前web应用的servletContext对象
四、HttpServletResponse
常见应用:
- 向浏览器(客户端)输出信息
- 文件下载
- 响应重定向
文件下载
在webapp包下创建static目录,在static目录下创建images目录,该目录下存放需要下载的文件
package com.yang;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
public class FileDownload extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取要下载文件的路径
String realPath = this.getServletContext().getRealPath("/static/images/文件下载.png");
// 2.根据路径获取下载的文件名
String[] pathArr = realPath.split("\\\\");
String fileName = pathArr[pathArr.length - 1];
// 3.设置浏览器支持文件下载
// URLEncoder.encode(fileName,"utf8")解决浏览器下载文件名中有中文时的问题
resp.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName,"utf8"));
// 4.获取需要下载文件的流对象(输入和输出)
FileInputStream fis = new FileInputStream(realPath);
ServletOutputStream sos = resp.getOutputStream();
// 将FileInputStream写入到缓冲区,再使用OutputStream将缓冲区的数据输出到浏览器
byte[] buf = new byte[1024];
int len;
while((len = fis.read(buf)) != -1){
sos.write(buf,0,len);
}
// 5.关闭资源
fis.close();
sos.close();
}
}
web.xml:
<servlet>
<servlet-name>fileDownload</servlet-name>
<servlet-class>com.yang.FileDownload</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>fileDownload</servlet-name>
<url-pattern>/dow</url-pattern>
</servlet-mapping>
响应重定向
当客户端访问一个地址时,后台可以将请求重定向到另一个资源(让客户端继续访问重定向的资源)
package com.yang;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RedirectServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 响应重定向
//response.sendRedirect("/dow");
// response.sendRedirect("/dow");相当于
response.setHeader("Location","/dow"); // 跳转到/dow下
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); // 将状态码设置为302
}
}
web.xml:
<servlet>
<servlet-name>redirectServlet</servlet-name>
<servlet-class>com.yang.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>redirectServlet</servlet-name>
<url-pattern>/red</url-pattern>
</servlet-mapping>
五、HttpServletRequese
- 获取客户端传递的参数
- 请求转发
获取客户端传递的参数
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class TestRequest01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// request对象获取单个请求参数getParameter
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
// 获取多个请求参数getParameterValues
String[] hobbys = req.getParameterValues("hobbys");
System.out.println(Arrays.toString(hobbys));
}
}
web.xml:
<servlet>
<servlet-name>request01</servlet-name>
<servlet-class>com.yang.servlet.TestRequest01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>request01</servlet-name>
<url-pattern>/index</url-pattern>
</servlet-mapping>
jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--${pageContext.request.contextPath}为当前web应用的上下文路径--%>
<form action="${pageContext.request.contextPath}/index">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobbys" value="唱">唱
<input type="checkbox" name="hobbys" value="跳">跳
<input type="checkbox" name="hobbys" value="rap">rap
<input type="checkbox" name="hobbys" value="篮球">篮球
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
请求转发
当访问servlet02:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestRequest02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入到servlet02..");
// 当客户端访问该servlet时,请求转发到servlet03
req.getRequestDispatcher("/servlet03").forward(req,resp);
}
}
servlet03:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestRequest03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 向客户端响应信息
resp.getWriter().write("servlet03...");
}
}
web.xml:
<servlet>
<servlet-name>servlet03</servlet-name>
<servlet-class>com.yang.servlet.TestRequest03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet03</servlet-name>
<url-pattern>/servlet03</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>servlet02</servlet-name>
<servlet-class>com.yang.servlet.TestRequest02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet02</servlet-name>
<url-pattern>/servlet02</url-pattern>
</servlet-mapping>
响应重定向和请求转发:
- 当客户端访问一个servlet时,请求转发和响应重定向都可以实现页面的跳转(从该servlet中向其他servlet或者静态资源中跳转)
- 请求转发时,客户端的访问地址栏不会发生改变
- 响应重定向时,客户端的访问地址栏会变为重定向后的地址
六、Cookie
客户端第一次访问服务器时服务器创建cookie返回给客户端,然后客户端保存在本地,当发送第二次请求的时候,客户但会把上次请求的cookie数据自动的携带给服务器,服务器通过cookie就能区分不同的用户
-
服务器响应给客户端
-
从请求对象中获取
Cookie(String name, String value) // 创建cookie对象,是一个键值对(key-value)
Cookie[] getCookies() // 获取cookie
String getName() // 获取cookie的key
String getValue() // 获取cookie的value
void setMaxAge(int expiry) // 设置cookie的存活时间(以秒为单位),如果不设置该值,默认浏览器关闭后,cookie过期
TestCookie:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
public class TestCookie extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html");
// 获取cookie
Cookie[] cookies = req.getCookies();
if (cookies != null){ // 判断客户端是否携带了cookie
for (Cookie cookie : cookies) {
if (cookie.getName().equals("lastLoginTime")){
// getValue 获取到一个String
// Long.parseLong将一个String转为一个long返回
long lastLoginTime = Long.parseLong(cookie.getValue());
// toLocaleString将标准时间转为年月日时分秒
resp.getWriter().write("上一次登录的时间:" + new Date(lastLoginTime).toLocaleString());
}
}
} else {
resp.getWriter().write("第一次访问...");
}
// 服务器重新访问一个cookie
Cookie cookie = new Cookie("lastLoginTime", new Date().getTime() + "");
// setMaxAge 设置cookie存活时间,以秒为单位
cookie.setMaxAge(30);
resp.addCookie(cookie);
}
}
TestCookie02:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestCookie02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if (cookies != null){
for (Cookie cookie : cookies) {
if (cookie.getName().equals("lastLoginTime")){
// 手动设置cookie的存活时间,将cookie的存活时间设置为0
cookie.setMaxAge(0);
resp.addCookie(cookie);
}
}
}
}
}
web.xml:
<!--设置session存活时间,单位为分-->
<session-config>
<session-timeout>1</session-timeout>
</session-config>
<servlet>
<servlet-name>c1</servlet-name>
<servlet-class>com.yang.servlet.TestCookie</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>c1</servlet-name>
<url-pattern>/c1</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>c2</servlet-name>
<servlet-class>com.yang.servlet.TestCookie02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>c2</servlet-name>
<url-pattern>/c2</url-pattern>
</servlet-mapping>
什么是会话?
客户端打开一个浏览器,访问服务器多个web资源,然后关闭浏览器,这个过程称为一次会话
七、Session(重点)
- 客户端打开一个浏览器时,如果还没有会话,服务器会自动创建一个session对象
- session是一个特殊的cookie(name为JSESSIONID的固定值,value为session对象的ID值)
- 同一个客户端中不同页面的切换,存储在session中的数据不会丢失,session保存在当前会话中
- 当该浏览器关闭后,session销毁
- 当用户打开不同的浏览器时,会创建不同的session
- 服务器会向客户端发送一个特有的sessionID,保存在cookie中
HttpSession getSession() // 获取session
String getId() // 获取session的id
boolean isNew() // 判断session是否为新的
ServletContext getServletContext() // 获取ServletContext对象
void setAttribute(String name, Object value) // 存放数据在session中
void removeAttribute(String name) // 根据key删除数据
void invalidate() // 销毁session,相当于关闭浏览器
TestSession:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class TestSession extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
// 获取session
HttpSession session = req.getSession();
// 在session中存放数据
session.setAttribute("name","admin");
resp.getWriter().write("当前session的id=" + session.getId() + " 是否为新的=" + session.isNew());
}
}
TestSession02:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class TestSession02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
// 获取session
HttpSession session = req.getSession();
// 获取存放在session中的值
resp.getWriter().write("name=" + session.getAttribute("name"));
// 根据key删除数据
session.removeAttribute("name");
}
}
TestSession03:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class TestSession03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
// 获取session
HttpSession session = req.getSession();
// 销魂当前session
session.invalidate();
}
}
web.xml:
<servlet>
<servlet-name>s3</servlet-name>
<servlet-class>com.yang.servlet.TestSession03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>s3</servlet-name>
<url-pattern>/s3</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>s2</servlet-name>
<servlet-class>com.yang.servlet.TestSession02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>s2</servlet-name>
<url-pattern>/s2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>s1</servlet-name>
<servlet-class>com.yang.servlet.TestSession</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>s1</servlet-name>
<url-pattern>/s1</url-pattern>
</servlet-mapping>
Cookie和Session的区别:
- cookie保存在客户端,session保存在服务器上
- cookie受浏览器存储量限制,session不受浏览器存储量大小限制
- session更加安全
- 当用户量大时,session会比较占用服务器性能
总结:
Cookie适用于存储用户的个性化设置和追踪用户行为,数据量通常较小且不涉及高敏感信息。它的优势在于能够在用户的浏览器中持久存储数据
Session适用于需要在服务器端存储用户状态和敏感数据的场景,通常用于用户身份验证和跨请求的数据管理。它的优势在于能够安全地存储较大或敏感的数据,并能在用户会话期间维持状态
八、JSP(了解)
java Servlet Pages:java服务器端页面,和Servlet一样属于动态web技术
- JSP本质上是一个Servlet,每一个JSP继承了HttpJspBase,而HttpJspBase继承了HttpServlet
- 当客户端访问一个JSP文件时,该JSP文件最终会被转换为一个java文件并被编译运行
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
String name = "smith";
%>
<%=name%>
</body>
</html>
在jsp中<%%>中间可已使用java代码,<%=name%>代表将变量name的值输出到客户端
上面的jsp代码转为java代码后(部分):
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" ");
String name = "smith";
out.write("\r\n");
out.write(" ");
out.print(name);
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
在jsp中定义的变量会直接转为java代码,输出的内容会被解析为html输出到客户端
JSP基本使用
jsp表达式(<%=%>):
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--JSP表达式
<%= 变量/表达式%> 作用:将信息输出到客户端
--%>
<%= new Date()%>
</body>
</html>
jsp中使用java语言(使用<%%>):
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--JSP表达式将内容输出到客户端--%>
<%=1+2%>
<hr>
<%--JSP中使用java代码--%>
<%
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
%>
<%
Person jack = new Person("jack", 20);
out.print(jack);
%>
</body>
</html>
上述在jsp文件中的代码都是定义在该jsp生成的java类中的_jspService方法中
想提升变量的作用域:使用<%!%>
<%!
static {
System.out.println("欢迎学习JSP");
}
private static int age;
public void test(){};
%>
使用<%!%>将里面的定义的java代码作用域提升到_jspService方法外
jsp中使用指令(当发生错误时,访问的资源):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--在jsp文件中配置,当页面发生错误时,访问的页面--%>
<%@ page errorPage="/error/500.jsp" %> <%--当发生500错误时,访问静态资源500.jsp--%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
int a = 10/0;
%>
</body>
</html>
当放生错误时,web.xml中的配置:
<!--在web.xml中配置,当发生错误时,需要跳转的页面-->
<error-page>
<!--发生异常的状态码-->
<error-code>404</error-code>
<!--需要跳转的资源-->
<location>/error/404.jsp</location>
</error-page>
页面拼接:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--JSP中使用页面拼接--%>
<%--1:使用指令的形式:本质是一个页面,将多个页面的内容合为一个来展示--%>
<%@ include file="/common/head.jsp" %>
<h1>主体部分</h1>
<%@ include file="/common/tail.jsp" %>
<hr>
<%--2:使用标签的形式:本质上是页面的拼接,多个页面显示,只是一个页面中引用了其他页面--%>
<jsp:include page="/common/head.jsp" />
<h1>主体部分</h1>
<jsp:include page="/common/tail.jsp" />
</body>
</html>
使用使用指令的形式实现页面拼接时,该jsp对应的java代码(部分):
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <h1>这是头部地址栏</h1>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\r\n");
out.write(" <h1>主体部分</h1>\r\n");
out.write(" ");
out.write("\r\n");
out.write("\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <h1>这是底部地址栏</h1>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
结论:本质是一个页面,只是将多个页面的内容合为一个来展示
缺点:页面间定义的java代码会发生冲突
使用标签的形式实现页面拼接时,该jsp对应的java代码(部分):
static {
_jspx_dependants = new java.util.HashMap<java.lang.String,java.lang.Long>(2);
_jspx_dependants.put("/common/head.jsp", Long.valueOf(1722869144000L));
_jspx_dependants.put("/common/tail.jsp", Long.valueOf(1722869144000L));
}
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/common/head.jsp", out, false);
out.write("\r\n");
out.write(" <h1>主体部分</h1>\r\n");
out.write(" ");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/common/tail.jsp", out, false);
out.write("\r\n");
结论:本质上是页面的拼接,多个页面显示,只是一个页面中引用了其他页面
优点:页面之间定义的java代码不冲突
JSP常用内置对象
- pageContext(代表当前页面)
- request
- session
- application(servletContext)
pageContext.setAttribute("name1","jack"); // 保存的数据在当前页面中有效
request.setAttribute("name2","tom"); // 保存的数据在当前的请求中有效(请求转发后的页面可以获取)
session.setAttribute("name3","smith"); // 保存的数据在当前会话中有效
application.setAttribute("name4","milan"); // 保存的数据在当前服务器中有效
pageContext对象的setAttribute方法和findAttribute方法:
void setAttribute(String var1, Object var2) // 普通的存放数据的方法
void setAttribute(String var1, Object var2, int var3) // 可以指定该数据存放的作用域
Object findAttribute(String var1) // 从当前页面(pageContext对象)开始寻找数据,如果找不到则向上寻找(pageContext -> request -> session -> application)
九、MVC三层架构
MVC(Model View Controller)模型视图控制器
Model
- 处理业务(Service)
- 数据持久化(Dao)
View
- 展示内容到客户端
- 向服务器发送请求
Controller
- 接收用户请求
- 根据用户请求将需求交给业务层处理
- 控制视图跳转
总结:客户端(View)访问访问页面时会发送请求,后端控制层(Controller)接收请求,将需求交给业务层(Model)处理,业务层通过访问数据库,将数据返回到控制层,然后控制层将数据返回到客户端
理解:顾客(View)到饭店吃饭,向服务员(控制层Controller)点餐,服务员向后厨(Service)反应,后厨做好菜交给服务员,服务员将餐交给顾客
好处:分工明确
十、Filter(重点)
- 过滤请求
- 处理乱码
创建一个servlet:
package com.yang.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("使用过滤器处理乱码问题...");
}
}
使用过滤器过滤:
package com.yang.filter;
import javax.servlet.*;
import java.io.IOException;
public class TestFilter implements Filter {
/**
* 伴随web服务器的启动而调用
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
/**
* 处理需要过滤的请求,注意需要对过滤后的请求放行
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("进入到过滤器...");
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 放行
chain.doFilter(request,response);
}
/**
* 伴随web服务器的关闭而调用
*/
@Override
public void destroy() {
System.out.println("销毁");
}
}
web.xml文件配置servlet和filter:
<!-- 创建同一个servlet的两个不同访问路径下用来测试过滤器 -->
<servlet>
<servlet-name>servlet01</servlet-name>
<servlet-class>com.yang.servlet.Servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet01</servlet-name>
<url-pattern>/servlet01</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>servlet02</servlet-name>
<servlet-class>com.yang.servlet.Servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet02</servlet-name>
<url-pattern>/servlet/servlet01</url-pattern>
</servlet-mapping>
<filter>
<filter-name>filter01</filter-name>
<filter-class>com.yang.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter01</filter-name>
<!--处理,当访问/servlet下的任何资源时,需要经过该过滤器-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
使用注解的方式配置过滤器:
@WebFilter("/*")
public class MyFilter implements Filter {
}
过滤器链:
多个过滤器可以形成过滤器链。过滤器的执行顺序由它们在web.xml中配置的顺序决定,或者按注解的加载顺序决定。
十一、实现一个简单的登录功能
登录页面Login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>请登录</h1>
<form action="/servlet/s1" method="post">
<input type="text" name="username"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
首页:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登录成功!欢迎访问首页</h1>
<a href="/servlet/s2">注销用户</a>
</body>
</html>
用户跨权限访问,或者用户名错误时的页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>用户名错误或者您无权限访问</h1>
<a href="/Login.jsp">返回登录页面</a>
</body>
</html>
用户登录时的servlet:
package com.yang.servlet;
import com.yang.utils.UserSession;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet/s1")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getParameter("username").equals("admin")){ // 登录成功
req.getSession().setAttribute(UserSession.USER_SESSION,req.getSession().getId());
resp.sendRedirect("/sys/Home.jsp");
} else { // 登录失败
resp.sendRedirect("/error/Error.jsp");
}
}
}
用户登录后注销的页面:
package com.yang.servlet;
import com.yang.utils.UserSession;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet/s2")
public class LoginOut extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getSession().getId() != null){
req.getSession().removeAttribute(UserSession.USER_SESSION);
resp.sendRedirect("/Login.jsp");
}
}
}
过滤器,当用户直接访问登录后才能访问的资源时,对其进行过滤:
package com.yang.filter;
import com.yang.utils.UserSession;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/sys/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if (request.getSession().getAttribute(UserSession.USER_SESSION) == null){
req.getRequestDispatcher("/error/Error.jsp").forward(request,response);
} else {
chain.doFilter(req,resp);
}
}
@Override
public void destroy() {
}
}
思路:
- 用户登录时,向session中存放该sessionID,作为当前用户的唯一标识
- 使用过滤器判断用户是否已经登录,如果已登录,则可以访问主页
- 用户注销后,删除session中的唯一标识该用户的属性sessionID,而不是直接删除session
- 跨权限访问,或者用户名错误,都让其返回登录错误页面
十二、回顾JDBC
package com.yang;
import org.junit.Test;
import java.sql.*;
public class TestJDBC {
@Test
public void insert() throws Exception{
String url = "jdbc:mysql://localhost:3306/jdbc01?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "root";
// 1 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 2 获取Connection数据库对象
Connection connection = DriverManager.getConnection(url, username, password);
// 3 编写sql
String sql = "insert into users values (?,?,?,?)";
// 4 预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 预编译
preparedStatement.setInt(1,10);
preparedStatement.setString(2,"张三");
preparedStatement.setString(3,"123456");
preparedStatement.setString(4,"男");
// 执行sql
int rows = preparedStatement.executeUpdate();
System.out.println(rows > 0 ? "添加成功":"添加失败");
// 关闭资源
preparedStatement.close();
connection.close();
}
@Test
public void query() throws Exception{
String url = "jdbc:mysql://localhost:3306/jdbc01?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "root";
// 1 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 2 获取Connection数据库对象
Connection connection = DriverManager.getConnection(url, username, password);
// 3 获取Statement对象,执行sql语句的对象
Statement statement = connection.createStatement();
String sql = "select * from users";
// 4 执行sql语句
ResultSet resultSet = statement.executeQuery(sql);
// 遍历结果集
while (resultSet.next()){
System.out.println(resultSet.getObject("user_id"));
System.out.println(resultSet.getObject("user_name"));
System.out.println(resultSet.getObject("password"));
System.out.println(resultSet.getObject("sex"));
}
// 关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
JDBC事务操作:
package com.yang;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TestJDBC02 {
private Connection connection = null;
/**
* 模拟转账
*/
@Test
public void test(){
String url = "jdbc:mysql://localhost:3306/jdbc01?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "root";
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(url, username, password);
connection.setAutoCommit(false); // 关闭自动提交(开启事务)
String sql01 = "update account set `money` = `money` - 100 where `name` = 'jack'";
String sql02 = "update account set `money` = `money` + 100 where `name` = 'tom'";
connection.prepareStatement(sql01).executeUpdate();
connection.prepareStatement(sql02).executeUpdate();
// int x = 1 / 0; // 制造错误,让事务回滚
connection.commit(); // 提交事务
} catch (Exception e) {
try {
// 转账发生错误,事务回滚
connection.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(e);
} finally {
if (connection != null){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
}