JAVA EE(二) —— Servlet 1(Servlet生命周期、创建Servlet项目、Servlet对象、Servlet配置文件、Servlet上传下载文件)

一、Servlet 概述

1、Servlet 概述

(1)Servlet 介绍

  • 狭义的 Servlet 是指 Java 语言实现的一个接口,广义的 Servlet 是指任何实现了这个 Servlet 接口的类,一般情况下,人们将 Servlet 理解为后者。
  • Servlet 运行于支持 Java 的应用服务器中,从原理上讲,Servlet 可以相应任何类型的请求,但绝大多数情况下 Servlet 只用来扩展基于 Http 协议的 Web 服务器。

(2)Servlet 特点

  • Servlet 是运行在支持 Java 的应用服务器上;
  • Servlet 的实现遵循了服务器能够识别的规则,也就是服务器会自动的根据请求调用对应的 Servlet 进行请求处理;
  • Servlet 简单方便,可移植性强。

2、Servlet 运行流程

实现Servlet 不管是继承 HttpServlet 还是继承 GenericServlet 类,都是为了实现Servlet 的操作,Servlet 的工作步骤如下

  • 浏览器发送请求到服务器:http://localhost:8080/工程名/类名(资源名)
  • 连接服务器:tomcat
  • 发送头文件 request 请求文件
  • 服务器解析出主机名或者主机地址(Dos解析)
  • 服务器访问工程(即tomcat下的webapps文件下对应的工程)
  • 服务器访问第5步中的工程下的资源(.class文件)
  • 利用反射创建 Servlet 对象,一个 Servlet 工程对应只有一个 Servlet 对象
  • 执行 init() 方法初始化 Servlet 对象
  • 执行 service() 方法,同时创建 request 对象和 response 对象
  • 服务器响应(response)

如果需要在发送请求之前就连接上数据库,即一旦tomcat启动就执行init()方法,则只需要在<servlet>中配置优先级<load-on-startup>1</load-on-startup>

3、开发Java EE工程步骤

以Eclipse开发工具为例,示例创建一个Java EE工程项目
(1)创建一个Dybnamic web project 工程

(2)点击下一步,在最后一步勾上生成web.xml文件

(3)在Eclipse中搭建tomcat服务器环境
window --> Preferences --> Server --> Runtime Environment --> 选择右边的add按钮,然后找到Apache(8.0) --> 选择文件夹 --> Finish

(4)将服务器的一栏的jar包,添加到工程中
工程上右键 --> 选择builder path --> config… --> Libraries --> 选择添加add Lib… --> 选择Server Runtime…

(5)创建一个类,继承GenericServlet,并重写servers方法
response.getOutputStream().write("hello world".getBytes());

(6)配置web.xml文件

//添加Server类的映射
<servlet>
   <servlet-name>类名</servlet-name>
   <servlet-class>完整类名</servlet-class>	//完整类名 = 包名 + 类名
</servlet>

//配置Servlet的访问请求映射
<servlet-mapping>
   <servlet-name>类名</servlet-name>
   <url-pattern>/类名</url-pattern>
</servlet-mapping>
  • 配置servlet,每一个servlet都需要配置一个,其中<servlet-name>类名</servlet-name> //的类名可以自定义,但必须和<servlet-mapping>中的<servlet-name>名字一致,建议使用类名。
  • 配置servlet下面的映射,能够识别浏览器的访问,其中<url-pattern>/类名</url-pattern>中的类名可以自定义,但是要与在浏览- 器访问时输入的请求名一致,还可以用通配符*.后缀
  • 在浏览器访问时输入的url:http://localhost:8080/工程名/请求名
    • http:表示http超文本传输协议
    • localhost:表示主机名,本地tomcat
    • 8080:tomcat的访问端口,可以再servlet.xml文件中配置
    • 工程名:写工程名是因为在访问时需要访问到tomcat下的webapps文件夹下存放的对应的工程名
    • 请求名:和web.xml文件中的<url-pattern>中配置的参数一致

(7)将工程放置到tomcat服务器中
单击服务器,选择 add and remove --> 将自己想要添加到服务器的工程添加进去即可

(8)启动服务器,没有错误之后,在浏览器中打开访问:http://localhost:8080/工程名/类名

4、Servlet生命周期

(1)Servlet 的生命周期:从 Servlet 被创建到 Servlet 被销毁的过程,一次创建,到处服务,一个 Servlet 只会有一个对象,服务所有的请求。

  • 创建对象:使用构造方法创建对象(实例化)
  • 初始化:执行 init() 方法
  • 服务:执行 service() 方法
  • 销毁:执行 destroy() 方法

(2)如果 Servlet 在 web.xml 中配置了 load-on-startup,声明周期为从服务器启动到服务器关闭。

<load-on-startup>1</load-on-startup>

二、创建 Servlet 项目的三种方式

1、方式一:实现 Servlet 接口

(1)因为是实现 Servlet 接口,所以需要实现接口里的方法。

(2)示例:

public class ServletDemo1 implements Servlet {
    //public ServletDemo1(){}
    
     //生命周期方法:当Servlet第一次被创建对象时执行该方法,该方法在整个生命周期中只执行一次
    public void init(ServletConfig arg0) throws ServletException {
		System.out.println("执行int方法......");
	}

    //生命周期方法:对客户端响应的方法,该方法会被执行多次,每次请求该servlet都会执行该方法
    public void service(ServletRequest arg0, ServletResponse arg1)
            throws ServletException, IOException {
        System.out.println("hehe");
    }

    //生命周期方法:当Servlet被销毁时执行该方法
    public void destroy() {
        System.out.println("当Servlet被销毁时执行该方法");
    }
//当停止tomcat时也就销毁的servlet。
    public ServletConfig getServletConfig() {
        return null;
    }

    public String getServletInfo() {
        return null;
    }
}

2、继承 GenericServlet 类

(1)它实现了 Servlet 接口并重写了 service 的方法,不过这种方法极少用。

(2)示例:

public class ServletDemo2 extends GenericServlet {
    @Override
   public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
		response.getOutputStream().write("hello world".getBytes());
	}
}

3、继承 HttpServlet 方法

(1)这种方式在开发中比较常用。

@WebServlet("/ServletDemo2")		//使用注解,可以不用配置xml文件
public class ServletDemo2 extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().append("Served at: ").append(request.getContextPath());
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

继承 HttpServlet 也可以重写service方法,但是传入的参数必须是HttpServletRequest requestHttpServletResponse response。这种重写的service方法会主动调用doGet()方法和doPost()方法。

4、service()、doGet()、doPost() 区别

  • service():可以处理 doGet() / doPost(),如果 Sertlvet 中有 service() 方法,会优先调用 service() 方法;
  • doGet():在没有 service() 方法的情况下处理 doGet() 方式的请求;
  • doPost():在没有 service() 方法的情况下处理 doPost() 方式的请求。
  • 如果要重写的 service() 方法中调用了父类的 service() 方法(super, service(arg0, arg1)),则 service() 方法处理后,会再次根据请求方式响应 doGet() / doPost() 方法,而此时如果没有重写 doGet() / doPost() 方法的话,就会报405的错误,所以一般不再重写的 service() 方法中调用父类的 service() 方法。

5、字节和字符的乱码问题

(1)字节乱码问题

@WebServlet("/ServletDemo3")
public class ServletDemo extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String str = "你好";
		//字节处理乱码
		//修改浏览器响应头中的content-type的值,即修改编码为utf-8
		//第一种
//		response.setHeader("content-type", "text/html;charset=utf-8");
		//第二种
		response.setContentType("text/html;charset=utf-8");
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write(str.getBytes());
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

(2)字符乱码问题

@WebServlet("/ServletDemo3")
public class ServletDemo extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	String str = "你好";
	//字符乱码处理
	//修改浏览器响应头中的content-type的值,即修改编码为utf-8
	//这种方式可以修改,但存在问题:有可能浏览器还是乱码
	response.setHeader("content-type", "text/html;charset=utf-8");
	//所以还需要修改服务器编码
	response.setCharacterEncoding("utf-8");
	PrintWriter writer = response.getWriter();
	writer.write(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

三、Servlet 的对象

1、Request 对象

(1)request 对象概述
服务器接收到浏览器的请求后,会创建一个 Request 对象,对象中存储了此次请求相关的请求数据。服务器在调用 Servlet 时会将创建的 Request 对象作为实参传递给 Servlet 的方法,如 service() 方法。

(2)request 对象使用

  • 获取请求头数据
request.getMethod();		//获取请求方式
request.getRequestURL();		//获取请求URL信息
request.getRequestURI();		//获取请求URI信息
request.getScheme();		//获取协议
  • 获取请求行数据
request.getHeader("键名");		//返回指定的请求头集合
request.getHeaderNames();		//返回请求头的键名的枚举集合
  • 获取用户数据
request.getParameter("键名");		//返回指定的用户数据
request.getParameterValues("键名");		//返回同键不同值的请求数据(多选),返回数组
request.getParameterNames();		//返回所有用户请求数据的枚举集合

设置请求编码格式

request.setCharacterEncoding("utf-8");

如果要获取的请求数据不存在,不会报错,而会返回 null。

(3)request 对象的作用域
使用请求转发后,不同的 Servlet 之间的数据共享需要使用 request 对象的作用域。

request.setAttribute(Object name, Object value);
request.getAttribute(Object obj);

使用 request 对象进行数据共享的方式,数据只在一次请求内有效,解决了在一次请求内的不同Servlet的数据共享问题。

特点:
作用域由服务器创建,每次请求都会创建,并且生命周期只在这一次请求中。

2、Response 对象

(1)Response 对象概述
服务器在调用指定的 Servlet 进行请求处理的时候,会给 Servlet 的方法传递两个实参 request 和 response。其中 request 中封存了请求相关的请求数据,response 则是用来进行响应,将数据响应到浏览器的一个对象,。

(2)Response 对象使用

  • 设置响应头
response.setHeader(String name, String value);	//在响应头中添加响应信息,但是同键会覆盖
response.addHeader(String name, String value); //在响应头中添加响应信息,但是同键不会覆盖
  • 设置响应状态
response.sendError(int num, String msg); 	//自定义响应状态码
  • 设置响应实体
response.getWriter().write(String str);		//响应具体的数据给浏览器
response.getWriter().print(String str); 		//响应具体的数据给浏览器

设置响应编码格式

response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");

3、ServletContext 对象

(1)ServletContext 概述

  • Servlet 创建对象时,在加载 Servlet 容器的时候,会创建一个 ServletContext 容器,因为是伴随着 Servlet 对象的创建而创建,所以整个web工程里面,只有一个 ServletContext。
  • 一个web工程里面,会有很多的 servlet,但是多个 servlet 之间,共用一个 ServletContext 对象,可以实现 servlet 之间的通信,即 ServletContext 解决了不同用户之间的数据共享。
  • ServletContext 由服务器创建,用户共享。其生命周期为服务器启动到服务器关闭,作用域为整个项目。
//发送端
ServletContext context = this.getServletContext();
context.setAttribute("名字",);
接收端
Object obj = context.getAttribute("名字");

(2)获取 ServletContext 对象

  • 方式一:通过 ServletConfig 对象和 ServletContext() 对象可以获取
ServletContext sc = this.getServletConfig().getServletContext();
  • 方式二:通过 this.ServletContext() 获取,response 和 request 都可以获取
ServletContext sc = this.getServletContext();
  • 方式三:通过 getSession() 获取
ServletContext sc = request.getSession().getServletContext();`

(3)ServletContext 数据存储和获取

sc.setAttribute(String name, Object value);
sc.getAttribute(String name);

(4)ServletContext 全局配置数据

  • ServletContext 对象代表的是全局的参数变量,获取的初始化参数不能够配置在单个的 servlet 中。
<context-param>
	<param-name></param-name>
	<param-value></param-value>
</context-param>

注意

  • 如果是编码集,需要通过配置文件来初始化。
  • 一组<context-param>标签只能配置一个键值对,要配置多个可以声明多个<context-param>

getInitParameter();方法:根据键的名字返回web.xml中配置的全局数据的值,返回String类型数据,如果数据不存在返回null;
getInitParameterNames();方法:返回键名的枚举

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext text = this.getServletContext();	
		String name = text.getInitParameter("name");
		response.getOutputStream().write(name.getBytes());
		Enumeration<String> e = text.getInitParameterNames();
		while(e.hasMoreElements()) {
			System.out.println(e.nextElement());
		}
	}
}

(5)ServletContext 对象的请求转发
① html文件

<body>
    <form action="Demo2" method="post">
	          用户名:<input type = "text" name = "userName" />
	          密码:<input type = "password" name = "passWord" />
	    <input type = "submit" >
    </form>
</body>

② 请求转发

@WebServlet("/Demo2")
public class Demo2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//测试是否执行1
		response.getOutputStream().write("是否执行1".getBytes());
		
		//请求转发
		RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/Demo3");
		rd.forward(request, response);
		
		//测试是否执行1
		response.getOutputStream().write("是否执行1".getBytes());
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

请求转发之前和之后的代码依旧可以执行,执行的结果都不会再页面显示,前面的没有显示是因为 response 在执行的时候只是先把结果存放在内存中,等待所有内容执行完毕后才会显示在页面,但是在请求转发的时候,会清空 response 的所有数据,所以 response 的结果会被清空。

后面的执行是因为 doGet() 方法并没有结束,当 doGet() 执行完转发请求rd.forward(request, response);后会回到方法中执行其后的代码,但是执行的结果不会再页面上是因为在请求转发之后页面已经跳转,并且不会跳转回来,所以不会显示。

③ 获取输入框信息

@WebServlet("/Demo3")
public class Demo3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//默认一个账号和密码
		String userName = "张三";
		String passWord = "1234";
		//获取输入的内容
		String name = request.getParameter("userName");
		String word = request.getParameter("passWord");
		//设置编码
		response.setContentType("text/html;charset=utf-8");
		//判断账号密码是否正确
		if(name.equals(userName) && word.equals(passWord)) {
			response.getOutputStream().write("成功".getBytes("utf-8"));
		}else {
			response.getOutputStream().write("错误".getBytes("utf-8"));
		}
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

4、ServletConfig 对象

(1)ServletConfig 对象是 Servlet 的专属配置对象,每个 Servlet 都单独拥有一个 ServletConfig 对象,用来获取 wen.xml 中配置信息。

(2)配置一个 Servlet 的初始化参数,在<servlet>中配置中添加以下信息

<init-param>
	<param-name>userName</param-name>
	<param-value>张三</param-value>
</init-param>

(3)主要方法

  • getInitParameter()

① 获取 ServletConfig 对象,第一种:在init()方法中

ServletConfig config = this.getServletConfig();
获取参数
config.getInitParameter("userName");

② 获取 ServletConfig 对象,第二种:在 init(ServletConfig config) 方法中

String userName = config.getInitParameter("userName");
  • getInitParameterNames()
ServletConfig config = this.getServletConfig();
Enumeration e = config.getInitParameterNames();

5、请求转发和重定向

(1)请求转发介绍
请求转发能够实现多个servlet联动操作处理请求,这样可以避免代码冗余,然servlet的职责更加明确

RequestDispather rd = request.getRequestDispacher(”要转发的地址“);
rd.forward(request,response);

请求转发特点:
请求转发只发送一次请求,浏览器的地址栏信息不会发生改变。

(2)重定向 sendRedirect(“前端的url路径”)
重定向能够解决表单重复提交的问题,以及当前servlet无法处理的请求的问题
重定向示例:

response.sendRedirect("路径");

status:300-399,表示请求成功,等待其他的请求响应,作用是实现页面的跳转。

重定向特点:
重定向有两次请求,创建两个request对象,并且浏览器的地址栏信息会发生改变。

重定向使用时机:

  • 如果请求中有表单数据,而数据又比较重要,不能重复提交,建议使用重定向。
  • 如果请求被Servlet接收后,无法进行处理,建议使用重定向定位到可以处理的资源。

(3) 重定向和请求转发的区别

  • 重定向不会等携带数据,只做页面跳转,而请求转发可以携带request对象的数据
  • 重定向页面的请求地址会发生改变,而请求转发不会修改地址栏
  • 重定向会发送两次请求,而请求转发只会请求一次,所以不建议频繁使用重定向
  • 重定向不能访问WEB-INF下面的html文件,请求转发可以访问
  • 重定向属于客户端的行为,而请求转发属于服务器的行为
  • 重定向解决了表单数据重复提交,以及当前 Servlet 无法处理的请求的问题,请求转发会造成表单数据重复提交

四、Servlet 配置文件

1、web.xml 文件

(1)web.xml 文件作用
配置项目相关的配置信息,保护 Servlet ,解耦一些数据对程序的依赖。

(2)web.xml 的位置

  • 每个web项目中会有一个web.xml 文件,在tomcat 服务器中(服务器目录conf目录下)也有一个web.xml 文件。
  • 区别:
    • Web 项目下的 web.xml 文件为局部配置,针对本项目的位置。
    • Tomcat 下的 web.xml 文件为全局配置,配置公共信息。

(3)web.xml 配置内容

  • 全局上下文配置
  • Servlet 配置
  • 过滤器配置
  • 监听器配置

加载顺序:Web 容器会按照 ServletContext -> context-param -> listener -> filter -> servlet 的顺序加载组件,这些元素可以配置在 web.xml 文件中的任意位置。

2、server.xml 文件

1、server.xml 文件的核心组件

<Server>
	<Service>
		<connector />	//配置端口号
		<connector />
		<Engine>		//一个<Service>便签下只能有一个<Engine>标签组
			<Host>
				<Context />
			</Host>
		</Engine>
	</Service>
</Server>

五、Servlet 代码示例

1、WEB 读取文件

(1)方式一:使用类加载器读取配置文件

@WebServlet("/Demo1")
public class Demo1 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		InputStream is = Demo1.class.getClassLoader().getResourceAsStream("test1.properties");
		Properties prop = new Properties();
		prop.load(is);
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

(2)方式二:通过 ServletContext 对象中的 getResourceAsStream();

@WebServlet("/Demo2")
public class Demo2 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletConfig config = this.getServletConfig();
		InputStream is = config.getServletContext().getResourceAsStream("/WEB-INF/classes/test1.properties");
		Properties prop = new Properties();
		prop.load(is);
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

通过方式这种方式获取配置文件需要注意的是:文件路径已经改变,不在存在于src下面,而是存放在工程名下/WEB-INF/classes文件夹下

(3)方式三:getRealPath()

@WebServlet("/Demo3")
public class Demo3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext context = this.getServletContext();
		String path = context.getRealPath("/WEB-INF/classes/test1.properties");
		FileInputStream fis = new FileInputStream(path);
		Properties prop = new Properties();
		prop.load(fis);
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

(4)方式四:将读取文件放在 WebContent 或者 WebRoot 文件目录下

@WebServlet("/Demo3")
public class Demo3 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext context = this.getServletContext();
		InputStream is = context.getResourceAsStream("test1.properties");
		Properties prop = new Properties();
		prop.load(is);	
		String str = prop.getProperty("user");
		System.out.println(str);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

2、Servlet 文件下载

(1)Servlet 下载文件概述
包括字节读取流、字节输出流两个步骤,但是有一个关键步骤:想要使网页能够下载文件,需要设置其中的一个头文件信息。

response.setHeader("content-disposition", "attchment;filename=文件名");

(2)Servlet 下载文件示例

@WebServlet("/Demo4")
public class Demo4 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String path = this.getServletContext().getRealPath("/WEB-INF/classes/图片.jpg");
		//截取文件名
		String newPath = path.substring(path.lastIndexOf("\\") + 1);
		//设置头文件
		response.setHeader("content-disposition", "attchment;filename=" + URLEncoder.encode(newPath,"utf-8"));
		FileInputStream fis = new FileInputStream(path);
		ServletOutputStream out = response.getOutputStream();
		int len = -1;
		byte[] bytes = new byte[1024];
		while((len = fis.read(bytes)) != -1) {
			out.write(bytes, 0 , len);
		}
		out.close();
		fis.close();
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}
发布了104 篇原创文章 · 获赞 58 · 访问量 7515

猜你喜欢

转载自blog.csdn.net/baidu_27414099/article/details/104440889
今日推荐