目录
1、Servlet概念
Servlet(server applet),运行在服务器端的小程序。Servlet就是一个接口,定义了Java类被浏览器访问到(被Tomcat识别)的规则。
将来我们自定义一个类,实现Servlet接口,重写方法。
2、Servlet快速入门
步骤:
1、创建JavaEE项目(参考学习笔记9);
2、定义一个类,如果IDEA无法自动导入Servlet,那么依次点击File -> Project Structure -> Libraries -> + 号 -> 添加 servlet-api.jar 即可。
3、实现接口中的抽象方法:
package web.servlet;
import javax.servlet.*;
import java.io.IOException;
/**
* Servlet快速入门
*/
public class ServletDemo1 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 提供服务的方法
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet!");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
4、配置Servlet:在web.xml中进行配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置Servlet-->
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>web.servlet.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
</web-app>
3、Servlet执行原理
1、当服务器接收到客户端浏览器的请求后,会解析请求的URL路径,获取访问到Servlet的资源路径;
2、查找web.xml文件是否有对应的<url-pattern>标签体内容;
3、如果有,则再找到对应的<servlet-class>的全类名;
4、tomcat会将字节码文件加载进内存,并创建其对象;
5、调用其方法。
4、Servlet中的生命周期
1、被创建:执行init方法,只执行一次。
*Servlet什么时候被创建?
默认情况下,第一次被访问时,Servlet被创建。
可以配置执行Servlet的创建时机。在<servlet>标签下配置:
<!-- 配置Servlet-->
<servlet>
<servlet-name>demo2</servlet-name>
<servlet-class>web.servlet.ServletDemo2</servlet-class>
<!-- 执行Servlet的创建时机
1、第一次被访问时创建;<load-on-startup>的值为负数(默认-1)
2、在服务器启动时,创建:<load-on-startup>的值为0或正整数(一般0~10之间)
-->
<load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>demo2</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
*Servlet的init方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。多个用户同时访问时,可能存在线程安全问题。解决方案:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对其修改。
2、提供服务:执行service方法,执行多次。
*每次访问Servlet时,Servlet方法都会被调用一次。
3、被销毁:执行destroy方法,执行一次。
*Servlet被销毁时执行。服务器关闭时,Servlet被销毁。
*只有服务器正常关闭时,才会执行destroy方法。
*destroy方法在Servlet被销毁之前执行,一般用于释放资源。
package web.servlet;
import javax.servlet.*;
import java.io.IOException;
/**
* Servlet方法
*/
public class ServletDemo2 implements Servlet {
/**
* 初始化方法
* 在Servlet被创建时执行,只会执行一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init......");
}
/**
* 获取ServletConfig对象(Servlet的配置对象)。
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 提供服务的方法
* 每一次Servlet被访问时执行。执行多次。
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service....");
}
/**
* 获取Servlet的一些信息,如版本,作者等等...
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 销毁方法
* 在服务器正常关闭,Servlet被杀死时执行,执行一次
*/
@Override
public void destroy() {
System.out.println("destroy....");
}
}
5、Servlet 3.0
好处:支持注解配置,可以不需要web.xml了。
步骤:
1、创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml;
2、定义一个类,实现Servlet接口;
3、重写方法;
4、在类上使用@WebServlet注解,进行配置。
@WebServlet("资源路径")
6、IDEA与Tomcat相关配置
1、IDEA会为每一个Tomcat部署的项目单独建立一份配置文件。查看控制台的log:
Using CATALINA_BASE: "C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.1\tomcat\85c2d8c2-188c-445c-a915-35ce35309b0e"
2、工作空间项目 和 Tomcat部署的web项目。
*Tomcat真正访问的是"Tomcat部署的web项目"(out文件夹),该项目对应着“工作空间项目”的web目录下的所有资源。
*WEB-INF目录下的资源不能被浏览器直接访问。
3、断点调试:使用 “小虫子” 启动(debug 启动)。
7、Servlet的体系结构
Servlet -- 接口。有一个实现类GenericServlet。
7.1、GenericServlet -- 抽象类
GenericServlet:将Servlet接口中的其它方法做了空实现,只将service()方法作为抽象。将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可。
7.2、HttpServlet -- 抽象类
HttpServlet:对http协议的一种封装,简化操作。HttpServlet是GenericServlet的子类。
1、定义类继承HttpServlet;
2、重写doGet() / doPost() 方法。
package web.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;
@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
}
}
8、Servlet相关配置
1、urlpattern:Servlet访问路径。
*一个Servlet可以定义多个访问路径:
package web.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;
@WebServlet({"/d4","/dd4","/ddd4"})
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo4...");
}
}
*路径的定义规则:① /xxx;② /xxx/xxx:多层路径,目录结构;③ *.xxx。
package web.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;
/**
* Servlet路径配置
*/
//@WebServlet({"/d4","/dd4","/ddd4"})
//@WebServlet("/user/demo4")
//@WebServlet("/*")
//@WebServlet("/user/*") //*代表任意字符串
@WebServlet("*.do") //localhost/demo4.do
public class ServletDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("demo4...");
}
}
9、Request对象
9.1、Request和Response原理
1、Request和Response对象是由服务器创建的,我们来使用它们;
2、Request对象是来获取请求消息,Response对象是来设置响应消息。
9.2、Request对象继承体系结构
ServletRequest -- 接口
^ 继承
HttpServletRequest -- 接口
^ 实现
org.apache.catalina.connector.RequestFacade@76fbbdc4 -- 实现类
9.3、Request的功能
9.3.1、获取请求消息数据
1、获取请求行数据:
请求行:
GET /ServletProject/demo?name=zhangsan HTTP/1.1
方法:
-获取请求方式:GET
String getMethod();
-获取虚拟目录:/ServletProject
String getContextPath();
-获取Servlet路径:/demo
String getServletPath();
-获取get方式的请求参数:name=zhangsan
String getQueryString();
-获取请求URI:/ServletProject/demo
String getRequestURI(); // /ServletProject/demo
StringBuffer getRequestURL(); // http://localhost/ServletProject/demo
URI:统一资源标识符(范围更大)
URL:统一资源定位符
-获取协议及版本:HTTP/1.1
String getProtocol();
-获取客户机的IP地址
String getRemoteAddr();
注:快速创建Servlet的方法:File -> Project Structure -> Facets -> Source Roots 打勾。创建时直接点击 New -> Servlet 即可。
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.net.URI;
/**
* 演示Request对象获取请求行数据
*/
@WebServlet("/requestDemo1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//-获取请求方式:GET
//String getMethod();
String method = request.getMethod();
System.out.println(method);
//-获取虚拟目录:/ServletProject
//String getContextPath();
String contextPath = request.getContextPath();
System.out.println(contextPath);
//-获取Servlet路径:/demo
//String getServletPath();
String servletPath = request.getServletPath();
System.out.println(servletPath);
//-获取get方式的请求参数:name=zhangsan
//String getQueryString();
String queryString = request.getQueryString();
System.out.println(queryString);
//-获取请求URI:/ServletProject/demo
//String getRequestURI(); // /ServletProject/demo
String requestURI = request.getRequestURI();
System.out.println(requestURI);
//StringBuffer getRequestURL(); // http://localhost/ServletProject/demo
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
//-获取协议及版本:HTTP/1.1
//String getProtocol();
String protocol = request.getProtocol();
System.out.println(protocol);
//-获取客户机的IP地址
//String getRemoteAddr();
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
2、获取请求头数据:
方法:
-通过请求头的名称获取请求头的值
String getHeader(String name);
-获取所有的请求头名称
Enumeration<String> getHeaderName();
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet("/requestDemo2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
//演示获取请求头数据
//-获取所有的请求头名称
//Enumeration<String> getHeaderName();
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
//-通过请求头的名称获取请求头的值
//String getHeader(String name);
String value = request.getHeader(name);
System.out.println(name+"----"+value);
}
System.out.println("------------------");
//获取请求头数据 user-agent
String agent = request.getHeader("user-agent"); //不区分大小写
//判断agent的浏览器版本
if(agent.contains("Chrome")) {
//谷歌
System.out.println("谷歌来了。。。");
}else if(agent.contains("Firefox")) {
//火狐
System.out.println("火狐来了...");
}
System.out.println("------------------");
//获取请求头数据 referer
String referer = request.getHeader("referer");
System.out.println(referer); //http://localhost/ServletProject/login.html
//防盗链
if(referer != null) {
if(referer.contains("ServletProject")) {
//正常访问
System.out.println("播放电影...");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("播放电影...");
}else{
//盗链
System.out.println("想看电影吗?来优酷吧..");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("想看电影吗?来优酷吧..");
}
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
3、获取请求体数据:
请求体:只有POST请求方式才有请求体,在请求体中封装了POST请求的请求参数
步骤:
1、获取流对象
BufferedReader getReader(); //获取字符输入流,只能操作字符数据
ServletInputStream getInputStream(); //获取字节输入流,可以操作所有类型的数据
*在文件上传知识点后讲解
2、再从流对象中拿数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/ServletProject/requestDemo3" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br />
<input type="text" placeholder="请输入密码" name="password"><br />
<input type="submit" value="注册">
</form>
</body>
</html>
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.BufferedReader;
import java.io.IOException;
@WebServlet("/requestDemo3")
public class RequestDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求消息体 -- 请求参数
//1、获取字符流
BufferedReader br = request.getReader();
//2、读取数据
String line = null;
while((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
9.3.2、其他功能
-获取请求参数通用方式(兼容GET和POST,不论get还是post请求方式都可以用下列方法获取请求参数)
String getParameter(String name); //根据参数名称获取参数值
如:username=zhangsan&password=123
String getParameterValues(String name); //根据参数名称获取参数值的数组
如:hobby=xx&hobby=game
Enumeration<String> getParameterNames(); //获取所有请求的参数名称
Map<String, String[]> getParameterMap(); //获取所有参数键值对的集合
*常见问题:中文乱码问题
get方式:tomcat8已经将get方式乱码问题解决了
post方式:会乱码。
解决:在获取参数前,设置request的编码:request.setCharacterEncoding("utf-8");
-请求转发:一种在服务器内部的资源跳转方式
步骤:
第一步:通过request对象获取请求转发器对象
RequestDispatcher getRequestDispatcher(String path);
第二步:使用RequestDispatcher对象进行转发
forward(ServletRequest request, ServletResponse response);
特点:
1、浏览器地址栏路径不发生变化;
2、只能转发到当前服务器内部资源中;
3、转发是一次请求。
-共享数据
*域对象:一个有作用范围的对象,可以在范围内共享数据。
*request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据。
*方法:
1、void setAttribute(String name, Object obj); //存储数据
2、Object getAttribute(String name); //通过键获取值
3、void removeAttribute(String name); //通过键移除键值对
-获取ServletContext对象
方法:
ServletContext getServletContext(); //获取ServletContext对象
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
@WebServlet("/requestDemo4")
public class RequestDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//get 获取请求参数
//String getParameter(String name); //根据参数名称获取参数值
// String username = request.getParameter("username");
// System.out.println("get");
// System.out.println(username);
//String getParameterValues(String name); //根据参数名称获取参数值的数组
//Enumeration<String> getParameterNames(); //获取所有请求的参数名称
//Map<String, String[]> getParameterMap(); //获取所有参数键值对的集合
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决中文乱码问题:设置流的编码
request.setCharacterEncoding("utf-8");
//post 获取请求参数
//String getParameter(String name); //根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("post");
System.out.println(username);
System.out.println("------------");
//String getParameterValues(String name); //根据参数名称获取参数值的数组
String[] hobbies = request.getParameterValues("hobby");
for(String hobby : hobbies) {
System.out.println(hobby);
}
//Enumeration<String> getParameterNames(); //获取所有请求的参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
System.out.println(name);
String value = request.getParameter(name);
System.out.println(value);
System.out.println("---");
}
System.out.println("--------------------");
//Map<String, String[]> getParameterMap(); //获取所有参数键值对的集合
Map<String, String[]> parameterMap = request.getParameterMap();
//遍历
Set<String> keySet = parameterMap.keySet();
for (String name : keySet) {
//根据键获取值
String[] values = parameterMap.get(name);
System.out.println(name);
for(String value : values) {
System.out.println(value);
}
System.out.println("---");
}
}
}
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5被访问了");
//转发到demo5a资源
request.getRequestDispatcher("/requestDemo5a").forward(request, response);
}
}
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5被访问了");
//存储数据到request域中
request.setAttribute("msg", "hello");
//转发到demo5a资源
request.getRequestDispatcher("/requestDemo5a").forward(request, response);
}
}
package web.request;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/requestDemo5a")
public class RequestDemo5a extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5a被访问了");
//获取数据
Object msg = request.getAttribute("msg");
System.out.println(msg);
}
}
9.3.3、案例:用户登录
需求:
- 编写login.html登陆页面: username & password 两个输入框;
- 使用Druid数据库连接池技术,操作mysql数据库mydatabase中user表;
- 使用JdbcTemplate技术封装JDBC;
- 登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您;
- 登录失败跳转到FailServlet战士:登录失败,用户名或密码错误。
分析:
开发步骤:
- 创建项目,导入html页面,配置文件和jar包;
- 创建数据库环境;
USE mydatabase; CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) UNIQUE NOT NULL, PASSWORD VARCHAR(32) NOT NULL );
- 创建包domain,创建User类。
package domain; /** * 用户的实体类 */ public class User { private int id; private String username; private String password; public int getId() { return id; } public void setId(int 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 + '\'' + '}'; } }
- 创建包dao,创建UserDao类,提供login方法。
package util; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; /** * JDBC工具类,使用Druid连接池 */ public class JDBCUtils { private static DataSource ds; static { try { //1、加载配置文件 Properties pro = new Properties(); //使用ClassLoader加载配置文件,获取字节输入流 InputStream is = JDBCUtils.class.getClassLoader(). getResourceAsStream("druid.properties"); pro.load(is); //2、初始化连接池对象 ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接池对象 */ public static DataSource getDataSource() { return ds; } /** * 获取连接Connection对象 * @return */ public static Connection Connection() throws SQLException { return ds.getConnection(); } } /****************************************************************************/ package dao; import domain.User; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import util.JDBCUtils; /** * 操作数据库中User表的类 */ public class UserDao { //声明JDBCTemplate对象共用 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /** * 登陆方法 * @param loginUser 只有用户名和密码 * @return user包含用户全部数据,没有查询到,返回null */ public User login(User loginUser) { try { //1、编写sql String sql = "select * from user where username = ? and password = ?"; //2、调用query方法 User user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword()); return user; } catch (DataAccessException e) { e.printStackTrace(); //记录日志 return null; } } }
-
创建web.servlet包,编写LoginServlet类。
注意:login.html中form表单的action路径的写法: "/虚拟目录/Servlet的资源路径" /*****************************************************************************/ package web.servlet; import dao.UserDao; import domain.User; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1、设置编码 request.setCharacterEncoding("utf-8"); //2、获取请求参数 String username = request.getParameter("username"); String password = request.getParameter("password"); //3、封装user对象 User loginUser = new User(); loginUser.setUsername(username); loginUser.setPassword(password); //4、调用UserDao的login方法 UserDao dao = new UserDao(); User user = dao.login(loginUser); //5、判断user if(user == null) { //登录失败 request.getRequestDispatcher("/failServlet"). forward(request,response); }else{ //登录成功 //存储数据 request.setAttribute("user", user); //转发数据 request.getRequestDispatcher("/successServlet"). forward(request,response); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } /*****************************************************************************/ package web.servlet; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/failServlet") public class FailServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //给页面写一句话 //设置页面编码 response.setContentType("text/html;charset=utf-8"); //输出 response.getWriter().write("登录失败,用户名或密码错误!"); } } /*****************************************************************************/ package web.servlet; import domain.User; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.IOException; @WebServlet("/successServlet") public class SuccessServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取request域中共享的user对象 User user = (User) request.getAttribute("user"); if(user != null) { //给页面写一句话 //设置页面编码 response.setContentType("text/html;charset=utf-8"); //输出 response.getWriter().write("登录成功!" + user.getUsername() + "欢迎您!"); } } }
9.3.4、BeanUtils工具类
BeanUtils是用于封装JavaBean的,可以简化数据封装。
JavaBean:标准的Java类。
JavaBean的规范(定义要求):
1、类必须被public修饰;
2、必须提供空参的构造器;
3、成员变量必须使用private修饰;
4、提供公共setter()和getter()方法。
JavaBean的功能:
封装数据。
概念:
成员变量:
属性:setter()和getter()方法截取后的产物。
如:getUsername() --> Username --> username
JavaBean中的方法:
setProperty(); //设置属性值
getProperty(); //获得属性值
populate(Obejct obj, Map map); //封装数据。将map集合的键值对信息,封装到对应的JavaBean对象中
package test;
import domain.User;
import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
public class BeanUtilsTest {
@Test
public void test() {
User user = new User();
try {
//username是属性值而不是成员变量名
BeanUtils.setProperty(user, "username", "zhangsan");
System.out.println(user);
//username为属性值,而不是成员变量名
String username = BeanUtils.getProperty(user, "username");
System.out.println(username);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
}
10、Response对象
10.1、Response的功能
10.1.1、设置响应消息数据
Response对象用来设置响应消息。
1、设置响应行
1、格式:
HTTP/1.1 200 OK
2、设置状态码
setStatus(int sc);
2、设置响应头
1、方法
setHeader(String name, String value);
3、设置响应体
使用步骤:
1、获取输出流
*字符输出流:PrintWriter getWriter();
*字节输出流:ServletOutputStream getOutputStream();
2、使用输出流,将数据输出到浏览器中
10.1.2、案例1:完成重定向
重定向:资源跳转的方式。
1、代码实现:
//1、设置状态码为302
// response.setStatus(302);
// //2、设置响应头location
// response.setHeader("location", "/ServletProject/responseDemo2");
//简单的重定向方法
response.sendRedirect("/ServletProject/responseDemo2");
2、重定向的特点:
1、地址栏发生变化;
2、重定向可以访问其它站点(服务器)的资源;
3、重定向是两次请求,不能使用request对象来共享数据。
*转发的特点:
1、转发地址栏路径不变;
2、转发只能访问当前服务器下的资源;
3、转发是一次请求,可以使用request对象来共享数据。
3、路径写法:
*路径的分类:
① 相对路径:通过相对路径不可以确定唯一资源。
* ./index.html
*不以 / 开头,以 . 开头的路径。
*规则:找到当前资源和目标资源之间的相对位置关系。
② 绝对路径:通过绝对路径可以确定唯一资源。
*如:http://localhost/ServletProject/demo /ServletProject/demo
*以 / 开头的路径
*规则:判断定义的路径是给谁用的?判断请求将来从哪发出?
给客户端浏览器使用:需要加虚拟目录(项目的访问路径)。
建议虚拟目录动态获取:String request.getContextPath();
使用情况:<a>,<form>,重定向,...
给服务器使用:不需要加虚拟目录。
使用情况:转发路径。
package web.response;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
/**
* 重定向
*/
@WebServlet("/responseDemo1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo1...");
//访问responseDemo1,会自动跳转到/responseDemo2资源
//1、设置状态码为302
// response.setStatus(302);
// //2、设置响应头location
// response.setHeader("location", "/ServletProject/responseDemo2");
//动态获取虚拟目录
String contextPath = request.getContextPath();
//简单的重定向方法
response.sendRedirect(contextPath+"/responseDemo2");
//response.sendRedirect("http://www.baidu.com"); //重定向至其它服务器资源
}
}
package web.response;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/responseDemo2")
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo2...");
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>相对路径</title>
</head>
<body>
<p>相对路径:找到当前资源和目标资源之间的相对位置关系。</p>
<p>
当前资源:location.html
http://localhost/ServletProject/location.html
</p>
<p>
目标资源:location.html
http://localhost/ServletProject/responseDemo2
</p>
<!-- ../ :代表向上退一级 -->
<!-- ./ :代表当前目录-->
<a href="./responseDemo2">
responseDemo2
</a>
</body>
</html>
10.1.3、案例2:服务器输出字符数据到浏览器
步骤:
1、获取字符输出流;
2、输出数据。
注意:
乱码问题:
1、PrintWriter pw = response.getWriter(); 获取的流的默认编码是ISO-8859-1
2、设置该流的默认编码;
3、告诉浏览器响应体使用的编码。简单的形式设置编码(在获取流之前设置):
response.setContentType("text/html;charset=utf-8");
package web.response;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/responseDemo3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取流对象之前设置流的默认编码:ISO-8859-1设置为GBK
//response.setCharacterEncoding("utf-8");
//告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码
response.setHeader("content-type", "text/html;charset=utf-8");
//简单形式来设置i编码
//response.setContentType("text/html;charset=utf-8");
//获取字符输出流
PrintWriter pw = response.getWriter();
//输出数据
//pw.write("Hello response");
pw.write("<h1>你好 response<h1>");
}
}
10.1.4、案例3:服务器输出字节数据到浏览器
步骤:
1、获取字节输出流;
2、输出数据
package web.response;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@WebServlet("/responseDemo4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码格式
response.setContentType("text/html;charset=utf-8");
//1、获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//2、输出数据
sos.write("你好".getBytes("utf-8"));
}
}
10.1.5、案例4:验证码
1、验证码的本质:就是一张图片。
2、目的:防止恶意表单注册。
package web.servlet;
import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 100;
int height = 50;
//1、创建一个对象,在内存中代表图片(验证码图片对象)
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//2、美化图片
//2.1、填充背景色
Graphics g = image.getGraphics(); //画笔对象
g.setColor(Color.pink); //设置画笔颜色
g.fillRect(0,0,width,height);
//2.2、画边框
g.setColor(Color.blue);
g.drawRect(0,0,width-1,height-1);
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
//生成随机角标
Random rand = new Random();
//2.3、写验证码
for (int i = 1; i <= 4 ; i++) {
int index = rand.nextInt(str.length());
//获取字符
char ch = str.charAt(index); //随机字符
g.setColor(Color.blue);
g.drawString(ch+"",width/5*i,height/2);
}
//2.4、画干扰线
g.setColor(Color.green);
for (int i = 0; i < 10; i++) {
//随机生成坐标点
int x1 = rand.nextInt(width);
int x2 = rand.nextInt(width);
int y1 = rand.nextInt(height);
int y2 = rand.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//3、将图片输出到页面展示
ImageIO.write(image, "jpg", response.getOutputStream());
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>验证码</title>
</head>
<body>
<img id="checkCode" src="/ServletProject/checkCodeServlet"/>
<a id="change" href="">看不清?换一张</a>
<script>
/**
* 分析:点击超链接或者图片,需要换一张
* 1、给超链接和图片绑定单击事件
* 2、重新设置图片的src属性值
*/
window.onload = function () {
//1、获取图片对象
var img = document.getElementById("checkCode");
//2、绑定单击事件
img.onclick = function () {
//加时间戳,防止一直访问浏览器缓存的同一张图片
var date = new Date().getTime();
img.src = "/ServletProject/checkCodeServlet?1" + date;
}
}
</script>
</body>
</html>
11、ServletContext对象
11.1、ServletContext对象的概念
概念:ServletContext代表整个web应用,可以和程序的容器(服务器)来通信。
11.2、ServletContext对象的功能
1、获取
*通过resuest对象获取:
getServletContext();
*通过HttpServlet来获取:
this.getServletContext();
功能:
1、获取MIME类型;
*MIME类型:在互联网通信过程中定义的一种文件数据类型。
格式:大类型/小类型 如:text/html、image/jpeg
*获取:String getMimeType(String file);
2、域对象:共享数据;
setAttribute(String name, Object value);
getAttribute(String name);
removeAttribute(String name);
*ServletContext对象范围:所有用户所有请求的数据。
3、获取文件的真实(服务器)路径:
方法:String getRealPath("参数");
package web.servletcontext;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/servletContextDemo1")
public class ServletContextDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//ServletContext对象的获取
//方式1:通过resuest对象获取:
// getServletContext();
ServletContext context1 = request.getServletContext();
//方式2:通过HttpServlet来获取:
// this.getServletContext();
ServletContext context2 = this.getServletContext();
System.out.println(context1);
System.out.println(context2);
System.out.println(context1 == context2); //true
}
}
package web.servletcontext;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/servletContextDemo2")
public class ServletContextDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//*MIME类型:在互联网通信过程中定义的一种文件数据类型。
//格式:大类型/小类型 如:text/html、image/jpeg
// *获取:String getMimeType(String file);
//1、通过HttpServlet获取ServletContext
ServletContext context = this.getServletContext();
//2、定义文件名称
String filename = "a.jpg"; //MIME type : image/jpeg
//3、获取MIME类型
String mimeType = context.getMimeType(filename);
System.out.println(mimeType);
}
}
package web.servletcontext;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/servletContextDemo3")
public class ServletContextDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//通过HttpServlet获取ServletContext
ServletContext context = this.getServletContext();
//设置数据
context.setAttribute("msg","haha");
}
}
package web.servletcontext;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet("/servletContextDemo4")
public class ServletContextDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//通过HttpServlet获取ServletContext
ServletContext context = this.getServletContext();
//获取数据
Object msg = context.getAttribute("msg");
System.out.println(msg);
}
package web.servletcontext;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.IOException;
@WebServlet("/servletContextDemo5")
public class ServletContextDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//通过HttpServlet获取ServletContext
ServletContext context = this.getServletContext();
//获取文件的服务器路径
String realPath1 = context.getRealPath("/b.txt"); //web目录下的资源访问
System.out.println(realPath1);
File file1 = new File(realPath1);//获取文件的服务器路径
String realPath2 = context.getRealPath("/WEB_INF/c.txt"); //web/WEB_INF下的资源访问
System.out.println(realPath2);
File file2 = new File(realPath2);
String realPath3 = context.getRealPath("/WEB_INF/classes/a.txt"); //web/WEB_INF/classes(src)目录下的资源访问
System.out.println(realPath3);
File file3 = new File(realPath3);
}
}
11.3、案例:文件下载
文件下载需求:
- 页面显示超链接;
- 点击超链接后弹出下载提示框;
- 完成图片文件下载。
分析:
- 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求。
- 任何资源都必须弹出下载提示框。
- 使用响应头设置资源的打开方式:content-disposition:attachment;filename=xxx 。
步骤:
- 定义页面,编辑超链接的href属性,指向一个Servlet,传递资源名称filename;
- 定义Servlet:① 获取文件名称;② 使用字节输入流加载文件进内存;③ 指定response的响应头: content-disposition:attachment;filename=xxx ;④ 将数据写出到response输出流。
问题:
中文文件名问题:
解决思路:
1、获取客户端使用的浏览器版本信息;
2、根据不同的版本信息,设置filename的编码方式不同。
package util;
//import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class DownLoadUtils {
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if(agent.contains("MSIE")) {
//IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+"," ");
// }else if(agent.contains("Firefox")) {
// //火狐浏览器
// BASE64Encoder base64Encoder = new BASE64Encoder();
// filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")+"?=");
}else {
//其它浏览器
filename = URLEncoder.encode(filename,"utf-8");
}
return filename;
}
}
/***********************************************************************/
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取请求参数:文件名称
String filename = request.getParameter("filename");
//2、使用字节输入流加载文件进内存
//2.1、找到文件服务器路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/" + filename);
//2.2、用字节流关联
FileInputStream fis = new FileInputStream(realPath);
//3、设置response的响应头
//3.1、设置响应头类型:content-type
String mimeType = servletContext.getMimeType(filename); //获取文件的Mime类型
response.setHeader("content-type", mimeType);
//3.2、设置响应头打开方式:content-disposition
//解决中文文件名问题
//3.3、获取user-agent请求头
String agent = request.getHeader("user-agent");
//3.4、使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent, filename);
//3.5、设置文件为附件形式下载
response.setHeader("content-disposition","attachment;filename="+filename);
//4、将输入流写出到输出流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
//fis.read(buff):从该输入流读取最多buff.length个字节的数据到一个字节数组,返回实际读取到的字节数据个数
while((len = fis.read(buff)) != -1) {
sos.write(buff,0,len); //字节流不需要刷新
}
fis.close(); //字节输入流需要手动关闭,释放资源
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>下载案例</title>
</head>
<body>
<a href="/ServletProject/img/1.jpg">图片1</a>
<hr />
<a href="/ServletProject/downloadServlet?filename=1.jpg">图片</a>
</body>
</html>