JSP总结

学习资料汇总

day1

1. 使用 JavaEE 版的 Eclipse 开发动态的 WEB 工程(JavaWEB 项目)


1). 把开发选项切换到 JavaEE
2). 可以在 Window -> Show View 中找到 Package Explorer, 并把其拖拽到开发区的左边
3). 在 Servers 面板中新建 Tomcat 服务器. 一定要关联到 Tomcat 安装的根目录
4). 新建一个 Dynamic Web Project. 其中 Target Runtime 需选择 Tomcat6.0 
5). 开发 Java WEB 应用
6). 可以通过 run on server  来运行 WEB 项目. 


2. Servlet 的 HelloWorld
1). 创建一个 Servlet 接口的实现类. 

public class HelloServlet implements Servlet

2). 在 web.xml 文件中配置和映射这个 Servlet

<!-- 配置和映射 Servlet -->
	<servlet>
		<!-- Servlet 注册的名字 -->
		<servlet-name>helloServlet</servlet-name>
		<!-- Servlet 的全类名 -->
		<servlet-class>com.atguigu.javaweb.HelloServlet</servlet-class>
	</servlet>
	
	<servlet-mapping>
		<!-- 需要和某一个 servlet 节点的 serlvet-name 子节点的文本节点一致 -->
		<servlet-name>helloServlet</servlet-name>
		<!-- 映射具体的访问路径: / 代表当前 WEB 应用的根目录. -->
		<url-pattern>/hello</url-pattern>
	</servlet-mapping>

3. Servlet 容器: 运行 Servlet、JSP、Filter 等的软件环境. 

1). 可以来创建 Servlet, 并调用 Servlet 的相关生命周期方法. 
2). JSP, Filter, Listener, Tag ...

4. Servlet 生命周期的方法: 以下方法都是由 Serlvet 容器负责调用. 
1). 构造器: 只被调用一次. 只有第一次请求 Servlet 时, 创建 Servlet 的实例. 调用构造器. 
这说明 Serlvet 的单实例的!
2). init 方法: 只被调用一次. 在创建好实例后立即被调用. 用于初始化当前 Servlet. 
3). service: 被多次调用. 每次请求都会调用 service 方法. 实际用于响应请求的. 
4). destroy: 只被调用一次. 在当前 Servlet 所在的 WEB 应用被卸载前调用. 用于释放当前 Servlet 所占用的资源. 

5. load-on-startup 参数:
1).  配置在 servlet 节点中:

<servlet>
		<!-- Servlet 注册的名字 -->
		<servlet-name>secondServlet</servlet-name>
		<!-- Servlet 的全类名 -->
		<servlet-class>com.atguigu.javaweb.SecondServlet</servlet-class>
		<!-- 可以指定 Servlet 被创建的时机 -->
		<load-on-startup>2</load-on-startup>
	</servlet>



2). load-on-startup: 可以指定 Serlvet 被创建的时机. 若为负数, 则在第一次请求时被创建.若为 0 或正数, 则在当前 WEB 应用被
Serlvet 容器加载时创建实例,  且数组越小越早被创建. 

6. 关于 serlvet-mapping:


1). 同一个Servlet可以被映射到多个URL上,即多个 <servlet-mapping> 元素的<servlet-name>子元素的设置值可以是同一个
Servlet的注册名。 

2). 在Servlet映射到的URL中也可以使用 * 通配符,但是只能有两种固定的格式:
一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

<servlet-mapping>
	<servlet-name>secondServlet</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>


OR


<servlet-mapping>
	<servlet-name>secondServlet</servlet-name>
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

注意: 以下的既带 / 又带扩展名的不合法. 
<servlet-mapping>
	<servlet-name>secondServlet</servlet-name>
	<url-pattern>/*.action</url-pattern>
</servlet-mapping>

7. ServletConfig: 封装了 Serlvet 的配置信息, 并且可以获取 ServletContext 对象


1). 配置 Serlvet 的初始化参数

<servlet>
		<servlet-name>helloServlet</servlet-name>
		<servlet-class>com.atguigu.javaweb.HelloServlet</servlet-class>
		
		<!-- 配置 Serlvet 的初始化参数。 且节点必须在 load-on-startup 节点的前面 -->
		<init-param>
			<!-- 参数名 -->
			<param-name>user</param-name>
			<!-- 参数值 -->
			<param-value>root</param-value>
		</init-param>
		
		<init-param>
			<param-name>password</param-name>
			<param-value>1230</param-value>
		</init-param>
		
		<load-on-startup>-1</load-on-startup>
		
	</servlet>

2). 获取初始化参数: 

> getInitParameter(String name): 获取指定参数名的初始化参数
        > getInitParameterNames(): 获取参数名组成的 Enumeration 对象. 
    
       
 String user = servletConfig.getInitParameter("user");
	System.out.println("user: " + user);
	
	Enumeration<String> names = servletConfig.getInitParameterNames();
	while(names.hasMoreElements()){
		String name = names.nextElement();
		String value = servletConfig.getInitParameter(name);
		System.out.println("^^" + name + ": " + value);
	}

3). 获取 Serlvet 的配置名称(了解)

8. ServletContext

1). 可以由  SerlvetConfig 获取:
ServletContext servletContext = servletConfig.getServletContext();

2). 该对象代表当前 WEB 应用: 可以认为 SerlvetContext 是当前 WEB 应用的一个大管家. 可以从中获取到当前 WEB 应用的各个方面的信息.

①. 获取当前 WEB 应用的初始化参数

设置初始化参数: 可以为所有的 Servlet 所获取, 而 Servlet 的初始化参数只用那个 Serlvet 可以获取. 

<!-- 配置当前 WEB 应用的初始化参数 -->
<context-param>
	<param-name>driver</param-name>
	<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
方法:
getInitParameter
getInitParameterNames
代码:
ServletContext servletContext = servletConfig.getServletContext();
		
String driver = servletContext.getInitParameter("driver");
System.out.println("driver:" + driver);

Enumeration<String> names2 = servletContext.getInitParameterNames();
while(names2.hasMoreElements()){
	String name = names2.nextElement();
	System.out.println("-->" + name); 
}

②. 获取当前 WEB 应用的某一个文件在服务器上的绝对路径, 而不是部署前的路径

getRealPath(String path);

代码:

String realPath = servletContext.getRealPath("/note.txt");
System.out.println(realPath);

③. 获取当前 WEB 应用的名称: 

getContextPath()

代码:

String contextPath = servletContext.getContextPath();
System.out.println(contextPath); 

④. 获取当前 WEB 应用的某一个文件对应的输入流. 

getResourceAsStream(String path): path 的 / 为当前 WEB 应用的根目录. 

代码:

InputStream is2 = servletContext.getResourceAsStream("/WEB-INF/classes/jdbc.properties");

⑤. 和 attribute 相关的几个方法: 

9. GET 请求和 POST 请求:

1). 使用GET方式传递参数:

①. 在浏览器地址栏中输入某个URL地址或单击网页上的一个超链接时,浏览器发出的HTTP请求消息的请求方式为GET。 
②. 如果网页中的<form>表单元素的 method 属性被设置为了“GET”,浏览器提交这个FORM表单时生成的HTTP请求消息的请求方式也为GET。 
③. 使用GET请求方式给WEB服务器传递参数的格式:  

http://www.atguigu.com/counter.jsp ?name=lc&password=123

④. 使用GET方式传送的数据量一般限制在 1KB 以下。 

2). 使用 POST 方式传递参数:

①. POST 请求方式主要用于向 WEB 服务器端程序提交 FORM 表单中的数据: form 表单的 method 置为 POST
②. POST 方式将各个表单字段元素及其数据作为 HTTP 消息的实体内容发送给 WEB 服务器,传送的数据量要比使用GET方式传送的数据量大得多。  

POST /counter.jsp HTTP/1.1
referer: http://localhost:8080/Register.html
content-type: application/x-www-form-urlencoded
host: localhost:8080
content-length: 43
name=zhangsan&password=123              --请求体中传递参数. 

10. 如何在 Serlvet 中获取请求信息:

1). Servlet 的 service() 方法用于应答请求: 因为每次请求都会调用 service() 方法

public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException

ServletRequest: 封装了请求信息. 可以从中获取到任何的请求信息.
ServletResponse: 封装了响应信息, 如果想给用户什么响应, 具体可以使用该接口的方法实现. 

这两个接口的实现类都是服务器给予实现的, 并在服务器调用 service 方法时传入. 

2). ServletRequest: 封装了请求信息. 可以从中获取到任何的请求信息.

①. 获取请求参数: 

> String getParameter(String name): 根据请求参数的名字, 返回参数值. 
若请求参数有多个值(例如 checkbox), 该方法只能获取到第一个提交的值. 

> String[] getParameterValues(String name): 根据请求参数的名字, 返回请求参数对应的字符串数组. 

> Enumeration getParameterNames(): 返回参数名对应的 Enumeration 对象, 
类似于 ServletConfig(或 ServletContext) 的 getInitParameterNames() 方法. 

> Map getParameterMap(): 返回请求参数的键值对: key: 参数名,  value: 参数值, String 数组类型. 

②. 获取请求的 URI:
HttpServletRequest httpServletRequest = (HttpServletRequest) request;		
	String requestURI = httpServletRequest.getRequestURI();
	System.out.println(requestURI); //  /day_29/loginServlet

③. 获取请求方式: 
String method = httpServletRequest.getMethod();
System.out.println(method); //GET

④. 若是一个 GET 请求, 获取请求参数对应的那个字符串, 即 ? 后的那个字符串. 
String queryString = httpServletRequest.getQueryString();
System.out.println(queryString); //user=atguigu&password=123456&interesting=game&interesting=party&interesting=shopping

⑤. 获取请求的 Serlvet 的映射路径   
   String servletPath = httpServletRequest.getServletPath();
   System.out.println(servletPath);  //  /loginServlet
   
⑥. 和 attribute 相关的几个方法:     
3). HttpServletRequest: 是 SerlvetRequest 的子接口. 针对于 HTTP 请求所定义. 里边包含了大量获取 HTTP 请求相关的方法. 
 
4). ServletResponse: 封装了响应信息, 如果想给用户什么响应, 具体可以使用该接口的方法实现. 

①. *getWriter(): 返回 PrintWriter 对象. 调用该对象的 print() 方法, 将把 print() 中的参数直接打印
到客户的浏览器上. 

②. 设置响应的内容类型: response.setContentType("application/msword");

③. void sendRedirect(String location): 请求的重定向. (此方法为 HttpServletResponse 中定义.)

实例:

在 web.xml 文件中设置两个 WEB 应用的初始化参数, user, password.
定义一个 login.html, 里边定义两个请求字段: user, password. 发送请求到 loginServlet
在创建一个 LoginServlet, 在其中获取请求的 user, password. 比对其和 web.xml 文件中定义的请求参数是否一致
若一致, 响应 Hello:xxx, 若不一致, 响应 Sorry: xxx  xxx 为 user.

LoginServlet

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LoginServlet extends GenericServlet {

	//对当前的 Servlet 进行初始化: 覆盖 init 方法
	@Override
	public void init(javax.servlet.ServletConfig config) throws ServletException {
		super.init(config);
	}
	
	@Override
	public void service(ServletRequest request, ServletResponse response)
			throws ServletException, IOException {
		
		//获取请求方式是 GET 还是 POST
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		String method = httpServletRequest.getMethod();
		System.out.println(method); 
		
		//1. 获取请求参数: username, password
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		//2. 获取当前 WEB 应用的初始化参数: user, password.
		//需要使用 ServletContext 对象. 
		String initUser = getServletContext().getInitParameter("user");
		String initPassword = getServletContext().getInitParameter("password");

		PrintWriter out = response.getWriter();
		
		//3. 比对
		//4. 打印响应字符串. 
		if(initUser.equals(username) && initPassword.equals(password)){
			out.print("Hello: " + username);
		}else{
			out.print("Sorry: " + username);
		}
		
		
	}

}
login.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<form action="loginServlet" method="post">
		
		user: <input type="text" name="username"/>
		password: <input type="password" name="password"/>
		
		<input type="submit" value="Submit"/>
	
	</form>
	
</body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <context-param>
    <param-name>user</param-name>
    <param-value>hello</param-value>
  </context-param>
  <context-param>
    <param-name>password</param-name>
    <param-value>123567</param-value>
  </context-param>
  <servlet>
    <servlet-name>loginServlet</servlet-name>
    <servlet-class>com.atguigu.javaweb.LoginServlet3</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>loginServlet</servlet-name>
    <url-pattern>/loginServlet</url-pattern>
  </servlet-mapping>

---------------------------------------

day2

7. MVC 设计模式. 

6. 和属性相关的方法:

1). 方法
void setAttribute(String name, Object o): 设置属性  
Object getAttribute(String name): 获取指定的属性
Enumeration getAttributeNames(): 获取所有的属性的名字组成的 Enumeration 对象
removeAttribute(String name): 移除指定的属性 
2). pageContext, request, session, application 对象都有这些方法!
这四个对象也称之为域对象. 
pageContext: 属性的作用范围仅限于当前 JSP 页面
request:  属性的作用范围仅限于同一个请求. 
session: 属性的作用范围限于一次会话: 浏览器打开直到关闭称之为一次会话(在此期间会话不失效)
application: 属性的作用范围限于当前 WEB 应用. 是范围最大的属性作用范围, 只要在一处设置属性, 在其他各处的 JSP 或 Servlet 中
都可以获取到. 
5. JSP:

1). WHY: 
JSP 是简 Servlet 编写的一种技术, 它将 Java 代码和 HTML 语句混合在同一个文件中编写,
只对网页中的要动态产生的内容采用 Java 代码来编写,而对固定不变的静态内容采用普通静态 HTML 页面的方式编写。 
2). Java Server Page: Java 服务器端网页. 在 HTML 页面中编写 Java 代码的页面. 
2). helloworld: 

新建一个 JSP 页面, 在 body 节点内的 <% %> 即可编写 Java 代码.

<body>
<% 
Date date = new Date();
System.out.print(date); 
%>
</body>

3). JSP 可以放置在 WEB 应用程序中的 除了 WEB-INF 及其子目录外的其他任何目录中,
JSP 页面的访问路径与普通 HTML 页面的访问路径形式也完全一样。

4). JSP的运行原理: JSP 本质上是一个 Servlet.

每个JSP 页面在第一次被访问时, JSP 引擎将它翻译成一个 Servlet 源程序, 接着再把这个 Servlet 源程序编译成 Servlet 的 class 类文件.
然后再由WEB容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。 

5). JSP 页面的隐含变量: 没有声明就可以使用的对象. JSP页面一共有 9 个隐含对象. 

public void _jspService(HttpServletRequest request, HttpServletResponse response)
        throws java.io.IOException, ServletException {
    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
//...
//使用  <% %> 编写的代码在此位置. 可以用到 request, response, pageContext, session
//application, config, out, page 这 8 个隐含对象. (实际上还可以使用一个叫 exception 的隐含对象)   
}
①. request: HttpServletRequest 的一个对象. *
②. response: HttpServletResponse 的一个对象(在 JSP 页面中几乎不会调用 response 的任何方法.)
③. pageContext: 页面的上下文, 是 PageContext 的一个对象. 可以从该对象中获取到其他 8 个隐含对象. 也可以从中获取到当前
页面的其他信息. (学习自定义标签时使用它) *
④. session: 代表浏览器和服务器的一次会话, 是 HttpSession 的一个对象. 后面详细学习. *
⑤. application: 代表当前 WEB 应用. 是 ServletContext 对象. *
⑥. config: 当前 JSP 对应的 Servlet 的 ServletConfig 对象(几乎不使用). 若需要访问当前 JSP 配置的初始化参数, 
需要通过映射的地址才可以.
映射 JSP:
  
<servlet>
   <servlet-name>hellojsp</servlet-name>
   <jsp-file>/hello.jsp</jsp-file>
   <init-param>
   <param-name>test</param-name>
   <param-value>testValue</param-value>
   </init-param>
  </servlet>
  
  <servlet-mapping>
   <servlet-name>hellojsp</servlet-name>
<url-pattern>/hellojsp</url-pattern>  
  </servlet-mapping>
⑦. out: JspWriter 对象. 调用 out.println() 可以直接把字符串打印到浏览器上. *
⑧. page: 指向当前 JSP 对应的 Servlet 对象的引用, 但为 Object 类型, 只能调用 Object 类的方法(几乎不使用)
⑨. exception: 在声明了 page 指令的 isErrorPage="true" 时, 才可以使用. *
<%@ page isErrorPage="true" %>
pageContext, request, session, application (对属性的作用域的范围从小到大)
out, response, config, page 
exception
6). JSP模版元素: JSP页面中的静态HTML内容称 

7). JSP表达式(expression)提供了将一个 java 变量或表达式的计算结果输出到客户端的简化方式,
它将要输出的变量或表达式直接封装在<%= 和 %>之中。

<% 
Date date = new Date();
out.print(date);
%>
<%= date %>

8). JSP脚本片断(scriptlet)是指嵌套在<% 和 %>之中的一条或多条Java程序代码。 
多个脚本片断中的代码可以相互访问
<% 
String ageStr = request.getParameter("age");
Integer age = Integer.parseInt(ageStr);
if(age >= 18){
%>
成人...
<%
}else{
%>
未成人...
<%
}
%>

9). JSP 声明: JSP 声明将 Java 代码封装在<%!和 %>之中,它里面的代码将被插入进 Servle t的 _jspService 方法的外面
(在 JSP 页面中几乎从不这样使用)

10). JSP注释的格式:<%-- JSP 注释 --%> <!-- HTML 注释 -->
区别: JSP 注释可以阻止 Java 代码的执行. 


4. 请求的转发和重定向:


1). 本质区别:
请求的转发只发出了一次请求, 而重定向则发出了两次请求. 

具体:

①. 请求的转发: 地址栏是初次发出请求的地址.
       请求的重定向: 地址栏不再是初次发出的请求地址. 地址栏为最后响应的那个地址 
       
②. 请求转发: 在最终的 Servlet 中, request 对象和中转的那个 request 是同一个对象. 
       请求的重定向: 在最终的 Servlet 中, request 对象和中转的那个 request 不是同一个对象.       
   
③. 请求的转发: 只能转发给当前 WEB 应用的的资源
       请求的重定向: 可以重定向到任何资源. 
       
④. 请求的转发: / 代表的是当前 WEB 应用的根目录
       请求的重定向: / 代表的是当前 WEB 站点的根目录. 
       
3. 
-----------------------------------------------------------------------------
在 MySQL 数据库中创建一个 test_users 数据表, 添加 3 个字段: id, user, password. 并录入几条记录. 
定义一个 login.html, 里边定义两个请求字段: user, password. 发送请求到 loginServlet
在创建一个 LoginServlet(需要继承自 HttpServlet, 并重写其 doPost 方法), 
在其中获取请求的 user, password. 
利用 JDBC 从 test_users 中查询有没有和页面输入的 user, password 对应的记录
SELECT count(id) FROM test_users WHERE user = ? AND password = ?
若有, 响应 Hello:xxx, 若没有, 响应 Sorry: xxx  xxx 为 user.
LoginServlet
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginServlet2 extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L; 

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws IOException {

		// 获取请求方式是 GET 还是 POST
		String method = request.getMethod();
		System.out.println(method);

		// 1. 获取请求参数: username, password
		String username = request.getParameter("username");
		String password = request.getParameter("password");

		// 2. 获取当前 WEB 应用的初始化参数: user, password.
		// 需要使用 ServletContext 对象.
		String initUser = getServletContext().getInitParameter("user");
		String initPassword = getServletContext().getInitParameter("password");

		PrintWriter out = response.getWriter();

		// 3. 比对
		// 4. 打印响应字符串.
		if (initUser.equals(username) && initPassword.equals(password)) {
			out.print("Hello: " + username);
		} else {
			out.print("Sorry: " + username);
		}

	}

}


login.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	
	<form action="loginServlet" method="post">
		
		user: <input type="text" name="username"/>
		password: <input type="password" name="password"/>
		
		<input type="submit" value="Submit"/>
	
	</form>
	
</body>
</html>

2. HttpServlet:

1). 是一个 Servlet, 继承自 GenericServlet. 针对于 HTTP 协议所定制. 
2). 在 service() 方法中直接把 ServletReuqest 和  ServletResponse 转为 HttpServletRequest 和 HttpServletResponse.
并调用了重载的 service(HttpServletRequest, HttpServletResponse)
在 service(HttpServletRequest, HttpServletResponse) 获取了请求方式: request.getMethod(). 根据请求方式有创建了
doXxx() 方法(xxx 为具体的请求方式, 比如 doGet, doPost)

@Override
 public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {
    HttpServletRequest  request;
    HttpServletResponse response;
    
    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    service(request, response);
}
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1. 获取请求方式.
String method = request.getMethod();
//2. 根据请求方式再调用对应的处理方法
if("GET".equalsIgnoreCase(method)){
doGet(request, response);
}else if("POST".equalsIgnoreCase(method)){
doPost(request, response);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException{
// TODO Auto-generated method stub
}
public void doGet(HttpServletRequest request, HttpServletResponse response) 
throws ServletException, IOException {
// TODO Auto-generated method stub
}

3). 实际开发中, 直接继承 HttpServlet, 并根据请求方式复写 doXxx() 方法即可. 

4). 好处: 直接由针对性的覆盖 doXxx() 方法; 直接使用 HttpServletRequest 和  HttpServletResponse, 不再需要强转. 
1. GenericServlet:
1). 是一个 Serlvet. 是 Servlet 接口和 ServletConfig 接口的实现类. 但是一个抽象类. 其中的 service 方法为抽象方

2). 如果新建的 Servlet 程序直接继承 GenericSerlvet 会使开发更简洁.

3). 具体实现:

①. 在 GenericServlet 中声明了一个 SerlvetConfig 类型的成员变量, 在 init(ServletConfig) 方法中对其进行了初始化 
②. 利用 servletConfig 成员变量的方法实现了 ServletConfig 接口的方法
③. 还定义了一个 init() 方法, 在 init(SerlvetConfig) 方法中对其进行调用, 子类可以直接覆盖 init() 在其中实现对 Servlet 的初始化. 
④. 不建议直接覆盖 init(ServletConfig), 因为如果忘记编写 super.init(config); 而还是用了 SerlvetConfig 接口的方法,
则会出现空指针异常. 
⑤. 新建的 init(){} 并非 Serlvet 的生命周期方法. 而 init(ServletConfig) 是生命周期相关的方法. 

public abstract class GenericServlet implements Servlet, ServletConfig {
/** 以下方法为 Servlet 接口的方法 **/
@Override
public void destroy() {}
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}


@Override
public String getServletInfo() {
return null;
}
private ServletConfig servletConfig;
@Override
public void init(ServletConfig arg0) throws ServletException {
this.servletConfig = arg0;
init();
}
public void init() throws ServletException{}


@Override
public abstract void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException;
/** 以下方法为 ServletConfig 接口的方法 **/
@Override
public String getInitParameter(String arg0) {
return servletConfig.getInitParameter(arg0);
}


@Override
public Enumeration getInitParameterNames() {
return servletConfig.getInitParameterNames();
}

@Override
public ServletContext getServletContext() {
return servletConfig.getServletContext();
}
@Override
public String getServletName() {
return servletConfig.getServletName();
}

}

day3

1. JSP 指令: JSP指令(directive)是为JSP引擎而设计的, 
它们并不直接产生任何可见输出, 而只是告诉引擎如何处理JSP页面中的其余部分。

2. 在目前的JSP 2.0中,定义了page、include 和 taglib这三种指令

3. page 指令:

1). page指令用于定义JSP页面的各种属性, 无论page指令出现在JSP页面中的什么地方, 
它作用的都是整个JSP页面, 为了保持程序的可读性和遵循良好的编程习惯, page指令最好是放在整个JSP页面的起始位置。 

2). page 指令常用的属性: 

①. import 属性: 指定当前 JSP 页面对应的 Servlet 需要导入的类. 
<%@page import="java.text.DateFormat"%>

②. session 属性: 取值为 true 或 false, 指定当前页面的 session 隐藏变量是否可用, 也可以说访问当前页面时是否一定要生成 HttpSession
对象. 
<%@ page session="false" %> 

③. errorPage 和 isErrorPage: 
> errorPage 指定若当前页面出现错误的实际响应页面时什么. 其中 / 表示的是当前 WEB 应用的根目录. 
<%@ page errorPage="/error.jsp" %> 

> 在响应 error.jsp 时, JSP 引擎使用的请求转发的方式. 
> isErrorPage 指定当前页面是否为错误处理页面, 可以说明当前页面是否可以使用 exception 隐藏变量. 需要注意的是: 若指定 
isErrorPage="true", 并使用 exception 的方法了, 一般不建议能够直接访问该页面. 

> 如何使客户不能直接访问某一个页面呢 ? 对于 Tomcat 服务器而言, WEB-INF 下的文件是不能通过在浏览器中直接输入地址的方式
来访问的. 但通过请求的转发是可以的!
> 还可以在 web.xml 文件中配置错误页面: 

<error-page>
   <!-- 指定出错的代码: 404 没有指定的资源, 500 内部错误. -->
   <error-code>404</error-code>
   <!-- 指定响应页面的位置 -->
   <location>/WEB-INF/error.jsp</location>
</error-page>
  
<error-page>
   <!-- 指定异常的类型 -->
   <exception-type>java.lang.ArithmeticException</exception-type>
   <location>/WEB-INF/error.jsp</location>
</error-page>

④. contentType: 指定当前 JSP 页面的响应类型. 实际调用的是 response.setContentType("text/html; charset=UTF-8");
通常情况下, 对于 JSP 页面而言其取值均为 text/html; charset=UTF-8. charset 指定返回的页面的字符编码是什么. 通常取值为 UTF-8

⑤. pageEncoding: 指定当前 JSP 页面的字符编码. 通常情况下该值和 contentType 中的 charset 一致. 

⑥. isELIgnored: 指定当前 JSP 页面是否可以使用 EL 表达式. 通常取值为 false. 

3. include 指令: <%@ include file="b.jsp" %>

1). include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中,
这种在源文件级别进行引入的方式称之为静态引入, 当前JSP页面与静态引入的页面紧密结合为一个Servlet。

2). file属性的设置值必须使用相对路径

3). 如果以 / 开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前文件。

4. jsp:incluce 标签:

1). <jsp:include page="b.jsp"></jsp:include>

2). 动态引入: 并不是像 include 指令生成一个 Servlet 源文件, 而是生成两个 Servlet 源文件, 然后通过一个方法的方式把目标页面包含
进来. 
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "b.jsp", out, false);

5. jsp:forward:

1).
<jsp:forward page="/include/b.jsp"></jsp:forward>    
相当于. 
<% 
request.getRequestDispatcher("/include/b.jsp").forward(request, response);
%>
2). 但使用 jsp:forward 可以使用 jsp:param 子标签向 b.jsp 传入一些参数. 同样 jsp:include 也可以使用 jsp:param 子标签. 

<jsp:forward page="/include/b.jsp">
<jsp:param value="abcd" name="username"/>
</jsp:forward> 
OR
<jsp:include page="/include/b.jsp">
<jsp:param value="abcd" name="username"/>
</jsp:include>
在  b.jsp 页面可以通过 request.getParameter("username") 获取到传入的请求参数. 

6. 关于中文乱码:
1). 在 JSP 页面上输入中文, 请求页面后不出现乱码: 保证  contentType="text/html; charset=UTF-8", 
    pageEncoding="UTF-8" charset 和 pageEncoding 的编码一致, 且都支持中文. 通常建议取值为UTF-8
    
     还需保证浏览器的显示的字符编码也和请求的 JSP 页面的编码一致. 
        
2). 获取中文参数值: 默认参数在传输过程中使用的编码为 ISO-8859-1

①. 对于 POST 请求: 只要在获取请求信息之前(在调用 request.getParameter 或者是 request.getReader 等), 
调用 request.setCharacterEncoding("UTF-8") 即可.
②. 对于 GET 请求: 前面的方式对于 GET 无效. 可以通过修改 Tomcat 的 server.xml 文件的方式. 
参照 http://localhost:8989/docs/config/index.html 文档的 useBodyEncodingForURI 属性. 
为 Connector 节点添加 useBodyEncodingForURI="true" 属性即可. 
<Connector connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>

day4

1. Session 的创建和销毁

page 指定的 session 属性: 

1). 默认情况下, 第一次访问一个 WEB 应用的一个 JSP 页面时, 该页面都必须有一个和这个请求相关联的 Session 对象. 
因为 page 指定的 session 属性默认为 true

2). 若把 session 属性改为 false, JSP 页面不会要求一定有一个 Session 对象和当前的 JSP 页面相关联
所以若第一次访问当前 WEB 应用的 JSP 页面时, 就不会创建一个 Session 对象. 

3). 创建一个 Session 对象: 若 page 指定的 session 设置为 false 或 在 Servlet 中可以通过以下 API 获取 Session 对象. 
request.getSession(flag): 若 flag 为 true, 则一定会返回一个 HttpSession 对象, 如果已经有和当前 JSP 页面关联的 HttpSession
对象, 直接返回; 如果没有, 则创建一个新的返回. flag 为 false: 若有关联的, 则返回; 若没有, 则返回 null
request.getSession(): 相当于 request.getSession(true);

4). Session 对象的销毁: 

①. 直接调用 HttpSession 的 invalidate()
②. HttpSession 超过过期时间. 
> 返回最大时效: getMaxInactiveInterval() 单位是秒
> 设置最大时效: setMaxInactiveInterval(int interval)
> 可以在 web.xml 文件中配置 Session 的最大时效, 单位是分钟. 
<session-config>
        <session-timeout>30</session-timeout>
    </session-config>

③. 卸载当前 WEB 应用. 
注意: 关闭浏览器不会销毁 Session!

day5

4. 使用 HttpSession 实现验证码

1). 基本原理: 和表单重复提交一致:


> 在原表单页面, 生成一个验证码的图片, 生成图片的同时, 需要把该图片中的字符串放入到 session 中. 
> 在原表单页面, 定义一个文本域, 用于输入验证码. 
> 在目标的 Servlet 中: 获取 session 和 表单域 中的 验证码的 值
> 比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 验证码 属性清除
> 若不一致, 则直接通过重定向的方式返回原表单页面, 并提示用户 "验证码错误"
3. 表单的重复提交

1). 重复提交的情况: 
①. 在表单提交到一个 Servlet, 而 Servlet 又通过请求转发的方式响应一个 JSP(HTML) 页面, 
此时地址栏还保留着 Serlvet 的那个路径, 在响应页面点击 "刷新" 
②. 在响应页面没有到达时重复点击 "提交按钮". 
③. 点击 "返回", 再点击 "提交"
2). 不是重复提交的情况: 点击 "返回", "刷新" 原表单页面, 再 "提交"。
3). 如何避免表单的重复提交: 在表单中做一个标记, 提交到 Servlet 时, 检查标记是否存在且是否和预定义的标记一致, 若一致, 则受理请求,
并销毁标记, 若不一致或没有标记, 则直接响应提示信息: "重复提交" 
①. 仅提供一个隐藏域: <input type="hidden" name="token" value="atguigu"/>. 行不通: 没有方法清除固定的请求参数. 
②. 把标记放在 request 中. 行不通, 因为表单页面刷新后, request 已经被销毁, 再提交表单是一个新的 request.
③. 把标记放在 session 中. 可以!
> 在原表单页面, 生成一个随机值 token
> 在原表单页面, 把 token 值放入 session 属性中
> 在原表单页面, 把 token 值放入到 隐藏域 中.
> 在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
> 比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
> 若不一致, 则直接响应提示页面: "重复提交"

2. 使用绝对路径:使用相对路径可能会有问题, 但使用绝对路径肯定没有问题. 
1). 绝对路径: 相对于当前 WEB 应用的路径. 在当前 WEB 应用的所有的路径前都添加 contextPath 即可. 
2). / 什么时候代表站点的根目录, 什么时候代表当前 WEB 应用的根目录
若 / 需要服务器进行内部解析, 则代表的就是 WEB 应用的根目录. 若是交给浏览器了, 则 / 代表的就是站点的根目录
若 / 代表的是 WEB 应用的根目录, 就不需要加上 contextPath 了. 

1. HttpSession 的生命周期:

1). 什么时候创建 HttpSession 对象

①. 对于 JSP: 是否浏览器访问服务端的任何一个 JSP, 服务器都会立即创建一个 HttpSession 对象呢?
不一定。
> 若当前的 JSP 是客户端访问的当前 WEB 应用的第一个资源,且 JSP 的 page 指定的 session 属性值为 false, 
则服务器就不会为 JSP 创建一个 HttpSession 对象;
> 若当前 JSP 不是客户端访问的当前 WEB 应用的第一个资源,且其他页面已经创建一个 HttpSession 对象,
则服务器也不会为当前 JSP 页面创建一个 HttpSession 对象,而回会把和当前会话关联的那个 HttpSession 对象返回给当前的 JSP 页面.

②. 对于 Serlvet: 若 Serlvet 是客户端访问的第一个 WEB 应用的资源,
则只有调用了 request.getSession() 或 request.getSession(true) 才会创建 HttpSession 对象

2). page 指令的 session=“false“  到底表示什么意思?

> 当前 JSP 页面禁用 session 隐含变量!但可以使用其他的显式的 HttpSession 对象

3). 在 Serlvet 中如何获取 HttpSession 对象?

> request.getSession(boolean create): 
create 为 false, 若没有和当前 JSP 页面关联的 HttpSession 对象, 则返回 null; 若有, 则返回 true
create 为 true, 一定返回一个 HttpSession 对象. 若没有和当前 JSP 页面关联的 HttpSession 对象, 则服务器创建一个新的
HttpSession 对象返回, 若有, 直接返回关联的. 

> request.getSession(): 等同于 request.getSession(true)

4). 什么时候销毁 HttpSession 对象:


①. 直接调用 HttpSession 的 invalidate() 方法: 该方法使 HttpSession 失效

②. 服务器卸载了当前 WEB 应用. 

③. 超出 HttpSession 的过期时间.

> 设置 HttpSession 的过期时间: session.setMaxInactiveInterval(5); 单位为秒

> 在 web.xml 文件中设置 HttpSession 的过期时间: 单位为 分钟. 

<session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    
④. 并不是关闭了浏览器就销毁了 HttpSession. 

day6

4. 使用 HttpSession 实现验证码

1). 基本原理: 和表单重复提交一致:
> 在原表单页面, 生成一个验证码的图片, 生成图片的同时, 需要把该图片中的字符串放入到 session 中. 
> 在原表单页面, 定义一个文本域, 用于输入验证码. 
> 在目标的 Servlet 中: 获取 session 和 表单域 中的 验证码的 值
> 比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 验证码 属性清除
> 若不一致, 则直接通过重定向的方式返回原表单页面, 并提示用户 "验证码错误"
3. 表单的重复提交
1). 重复提交的情况: 
①. 在表单提交到一个 Servlet, 而 Servlet 又通过请求转发的方式响应一个 JSP(HTML) 页面, 
此时地址栏还保留着 Serlvet 的那个路径, 在响应页面点击 "刷新" 
②. 在响应页面没有到达时重复点击 "提交按钮". 
③. 点击 "返回", 再点击 "提交"
2). 不是重复提交的情况: 点击 "返回", "刷新" 原表单页面, 再 "提交"。
3). 如何避免表单的重复提交: 在表单中做一个标记, 提交到 Servlet 时, 检查标记是否存在且是否和预定义的标记一致, 若一致, 则受理请求,
并销毁标记, 若不一致或没有标记, 则直接响应提示信息: "重复提交" 
①. 仅提供一个隐藏域: <input type="hidden" name="token" value="atguigu"/>. 行不通: 没有方法清除固定的请求参数. 
②. 把标记放在 request 中. 行不通, 因为表单页面刷新后, request 已经被销毁, 再提交表单是一个新的 request.
③. 把标记放在 session 中. 可以!
> 在原表单页面, 生成一个随机值 token
> 在原表单页面, 把 token 值放入 session 属性中
> 在原表单页面, 把 token 值放入到 隐藏域 中.
> 在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
> 比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
> 若不一致, 则直接响应提示页面: "重复提交"

2. 使用绝对路径:使用相对路径可能会有问题, 但使用绝对路径肯定没有问题. 


1). 绝对路径: 相对于当前 WEB 应用的路径. 在当前 WEB 应用的所有的路径前都添加 contextPath 即可. 
2). / 什么时候代表站点的根目录, 什么时候代表当前 WEB 应用的根目
若 / 需要服务器进行内部解析, 则代表的就是 WEB 应用的根目录. 若是交给浏览器了, 则 / 代表的就是站点的根目录
若 / 代表的是 WEB 应用的根目录, 就不需要加上 contextPath 了. 

1. HttpSession 的生命周期:



1). 什么时候创建 HttpSession 对象
①. 对于 JSP: 是否浏览器访问服务端的任何一个 JSP, 服务器都会立即创建一个 HttpSession 对象呢?
不一定。
> 若当前的 JSP 是客户端访问的当前 WEB 应用的第一个资源,且 JSP 的 page 指定的 session 属性值为 false, 
则服务器就不会为 JSP 创建一个 HttpSession 对象;
> 若当前 JSP 不是客户端访问的当前 WEB 应用的第一个资源,且其他页面已经创建一个 HttpSession 对象,
则服务器也不会为当前 JSP 页面创建一个 HttpSession 对象,而回会把和当前会话关联的那个 HttpSession 对象返回给当前的 JSP 页面.

②. 对于 Serlvet: 若 Serlvet 是客户端访问的第一个 WEB 应用的资源,
则只有调用了 request.getSession() 或 request.getSession(true) 才会创建 HttpSession 对象
2). page 指令的 session=“false“  到底表示什么意思?

> 当前 JSP 页面禁用 session 隐含变量!但可以使用其他的显式的 HttpSession 对象

3). 在 Serlvet 中如何获取 HttpSession 对象?
> request.getSession(boolean create): 
create 为 false, 若没有和当前 JSP 页面关联的 HttpSession 对象, 则返回 null; 若有, 则返回 true
create 为 true, 一定返回一个 HttpSession 对象. 若没有和当前 JSP 页面关联的 HttpSession 对象, 则服务器创建一个新的
HttpSession 对象返回, 若有, 直接返回关联的. 
> request.getSession(): 等同于 request.getSession(true)

4). 什么时候销毁 HttpSession 对象:

①. 直接调用 HttpSession 的 invalidate() 方法: 该方法使 HttpSession 失效

②. 服务器卸载了当前 WEB 应用. 

③. 超出 HttpSession 的过期时间.

> 设置 HttpSession 的过期时间: session.setMaxInactiveInterval(5); 单位为秒

> 在 web.xml 文件中设置 HttpSession 的过期时间: 单位为 分钟. 

<session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    
④. 并不是关闭了浏览器就销毁了 HttpSession. 
---------------
day7
3. JSTL:
1)*. c:out 主要用于对特殊字符进行转换. 真正进行输出时, 建议使用 c:out, 而不是使用 EL
2)*. c:set: 可以为域赋属性值。 而对域对象中的 JavaBean 的属性赋值用的并不多. 
3). c:remove: 移除指定域对象的指定属性值(较少使用, 即便移除也是在 Servlet 中完成
4)*. c:if: 在页面上对现实的内容进行过滤, 把结果存储到域对象的属性中. 但不灵活, 会被其他的自定义标签所取代. 
5). c:choose, c:when, c:otherwise: 作用同上, 但麻烦, 不灵活.
6)*. c:forEach: 对集合进行遍历的. 常用!
7). c:forTokens: 处理字符串, 类似于 String 累的 split() 方法(知道即可)
8). c:import: 导入页面到当前页面的. (了解)
9). c:redirect: 当前页面进行重定向的. (使用较少)
10)*. c:url: 产生一个 URL 的, 可以进行 URL 重写, 变量值编码, 较为常用. 

2. 开发有父标签的标签:


1). 父标签无法获取子标签的引用, 父标签仅把子标签作为标签体来使用. 

2). 子标签可以通过 getParent() 方法来获取父标签的引用(需继承 SimpleTagSupport 或自实现 SimpleTag 接口的该方法):
若子标签的确有父标签, JSP 引擎会把代表父标签的引用通过  setParent(JspTag parent)  赋给标签处理器
3). 注意: 父标签的类型是 JspTag 类型. 该接口是一个空接口, 但是来统一 SimpleTag 和 Tag 的. 实际使用需要进行类型的强制转换.
4). 在 tld 配置文件中, 无需为父标签有额外的配置. 但, 子标签是是以标签体的形式存在的, 所以父标签的 <body-content></body-content>
需设置为 scriptless
5). 实现 
<c:choose>
<c:when test="${param.age > 24}">大学毕业</c:when>
<c:when test="${param.age > 20}">高中毕业</c:when>
<c:otherwise>高中以下...</c:otherwise>
</c:choose>
> 开发 3 个标签: choose, when, otherwise
> 其中 when 标签有一个 boolean 类型的属性: test
> choose 是 when 和 otherwise 的父标签
> when 在 otherwise 之前使用

> 在父标签 choose 中定义一个 "全局" 的 boolean 类型的 flag: 用于判断子标签在满足条件的情况下是否执行. 

* 若 when 的 test 为 true, 且 when 的父标签的 flag 也为 true, 则执行 when 的标签体(正常输出标签体的内容), 
     同时把 flag 设置为 false
* 若 when 的 test 为 true, 且 when 的父标签的 flag 为 false, 则不执行标签体. 
* 若 flag 为 true, otherwise 执行标签体. 

1. 带标签体的自定义标签: 
1). 若一个标签有标签体: 

<atguigu:testJspFragment>abcdefg</atguigu:testJspFragment>

在自定义标签的标签处理器中使用 JspFragment 对象封装标签体信息. 

2). 若配置了标签含有标签体, 则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 传递给标签处理器类
在 SimpleTagSupport 中还定义了一个 getJspBody() 方法, 用于返回 JspFragment 对象. 

3). JspFragment 的 invoke(Writer) 方法: 把标签体内容从 Writer 中输出, 若为 null, 
则等同于 invoke(getJspContext().getOut()), 即直接把标签体内容输出到页面上

有时, 可以 借助于 StringWriter, 可以在标签处理器类中先得到标签体的内容: 

//1. 利用 StringWriter 得到标签体的内容.
StringWriter sw = new StringWriter();
bodyContent.invoke(sw);

//2. 把标签体的内容都变为大写
String content = sw.toString().toUpperCase();

4). 在 tld 文件中, 使用 body-content 节点来描述标签体的类型: 

<body-content>: 指定标签体的类型, 大部分情况下, 取值为 scriptless。可能取值有 3 种:
empty: 没有标签体
scriptless: 标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素
tagdependent: 表示标签体交由标签本身去解析处理。
若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器

<body-content>tagdependent</body-content>

5). 定义一个自定义标签: <atguigu:printUpper time="10">abcdefg</atguigu> 把标签体内容转换为大写, 并输出 time 次到
浏览器上. 

6). 实现 forEach 标签: 

> 两个属性: items(集合类型, Collection), var(String 类型)
> doTag: 

* 遍历 items 对应的集合
* 把正在遍历的对象放入到 pageContext 中, 键: var, 值: 正在遍历的对象. 
* 把标签体的内容直接输出到页面上. 
    <c:forEach items="${requestScope.customers }" var="cust2">
${pageScope.cust2.id } -- ${cust2.name } <br>
</c:forEach>

<atguigu:saveAsFile src="d:\\haha.txt">
abcde
</atguigu>

day8

1. Filter:
1). Filter 是什么 ?
①. JavaWEB 的一个重要组件, 可以对发送到 Servlet 的请求进行拦截, 并对响应也进行拦截. 
②. Filter 是实现了 Filter 接口的 Java 类.
③. Filter 需要在 web.xml 文件中进行配置和映射. 

2). 如何创建一个 Filter, 并把他跑起来

①. 创建一个 Filter 类: 实现 Filter 接口: public class HelloFilter implements Filter
②. 在 web.xml 文件中配置并映射该 Filter. 其中 url-pattern 指定该 Filter 可以拦截哪些资源, 即可以通过哪些 url 访问到该 Filter

<!-- 注册 Filter -->
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.atguigu.javaweb.HelloFilter</filter-class>
</filter>


<!-- 映射 Filter -->
<filter-mapping>
<filter-name>helloFilter</filter-name>
<url-pattern>/test.jsp</url-pattern>
</filter-mapping>

3). Filter 相关的 API:

①. Filter 接口:

> public void init(FilterConfig filterConfig): 类似于 Servlet 的 init 方法. 在创建 Filter 对象(Filter 对象在 Servlet 容器加载当前 WEB 应用时即被创建)后, 
立即被调用, 且只被调用一次. 该方法用于对当前的 Filter 进行初始化操作. Filter 实例是单例的. 
*  FilterConfig 类似于 ServletConfig

* 可以在 web.xml 文件中配置当前 Filter 的初始化参数. 配置方式也和 Servlet 类似。
<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.atguigu.javaweb.HelloFilter</filter-class>
<init-param>
<param-name>name</param-name>
<param-value>root</param-value>
</init-param>
</filter>

> public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain): 真正 Filter 的逻辑代码需要编写在该方法中. 每次拦截都会调用该方法. 

* FilterChain: Filter 链. 多个 Filter 可以构成一个 Filter 链. 

- doFilter(ServletRequest request, ServletResponse response): 把请求传给 Filter 链的下一个 Filter,
若当前 Filter 是 Filter 链的最后一个 Filter, 将把请求给到目标 Serlvet(或 JSP)

- 多个 Filter 拦截的顺序和 <filter-mapping> 配置的顺序有关, 靠前的先被调用. 

> public void destroy(): 释放当前 Filter 所占用的资源的方法. 在 Filter 被销毁之前被调用, 且只被调用一次. 

4). <dispatcher> 元素: 指定过滤器所拦截的资源被 Servlet 容器调用的方式,
可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST. 
可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截

①. REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。

通过 GET 或 POST 请求直接访问。 

②. FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。

或 <jsp:forward page="/..." /> 或 通过 page 指令的 errorPage 转发页面. <%@ page errorPage="test.jsp" %>

②. INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。

或 <jsp:include file="/..." />

④. ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
在 web.xml 文件中通过 error-page 节点进行声明:
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/test.jsp</location>
</error-page>
<filter-mapping>
<filter-name>secondFilter</filter-name>
<url-pattern>/test.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

day9


2. HttpServletWrapper 和 HttpServletResponseWrapper

1). Servlet API 中提供了一个 HttpServletRequestWrapper 类来包装原始的 request 对象,
HttpServletRequestWrapper 类实现了 HttpServletRequest 接口中的所有方法, 
这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法

//包装类实现 ServletRequest 接口. 
public class ServletRequestWrapper implements ServletRequest {

    //被包装的那个 ServletRequest 对象
    private ServletRequest request;

//构造器传入 ServletRequest 实现类对象
    public ServletRequestWrapper(ServletRequest request) {
if (request == null) {
    throw new IllegalArgumentException("Request cannot be null");   
}
this.request = request;
    }

//具体实现 ServletRequest 的方法: 调用被包装的那个成员变量的方法实现。 
    public Object getAttribute(String name) {
return this.request.getAttribute(name);
}

    public Enumeration getAttributeNames() {
return this.request.getAttributeNames();
}    

//... 
} 

相类似 Servlet API 也提供了一个 HttpServletResponseWrapper 类来包装原始的 response 对象

2). 作用: 用于对 HttpServletRequest 或 HttpServletResponse 的某一个方法进行修改或增强.

public class MyHttpServletRequest extends HttpServletRequestWrapper{

public MyHttpServletRequest(HttpServletRequest request) {
super(request);
}

@Override
public String getParameter(String name) {
String val = super.getParameter(name);
if(val != null && val.contains(" fuck ")){
val = val.replace("fuck", "****");
}
return val;
}
}

3). 使用: 在 Filter 中, 利用 MyHttpServletRequest 替换传入的 HttpServletRequest
HttpServletRequest req = new MyHttpServletRequest(request);
filterChain.doFilter(req, response);

此时到达目标 Servlet 或 JSP 的 HttpServletRequest 实际上是 MyHttpServletRequest

1. 使用 Filter 完成一个简单的权限模型:
1). 需求: 

①. 管理权限
> 查看某人的权限
> 修改某人的权限
②. 对访问进行权限控制: 有权限则可以访问, 否则提示: 没有对应的权限, 请 返回

2). 实现:
②. 对访问进行权限控制:
> 使用 Filter 进行权限的过滤: 检验用户是否有权限, 若有, 则直接响应目标页面; 若没有重定向到 403.jsp
*   403.jsp
<h4>
没有对应的权限, 
请 <a href="">返回</a>
</h4>
* 使用 Filter 如何进行过滤: 
- 获取 servletPath, 类似于 /app_3/article1.jsp
- 在用户已经登录(可使用 用户是否登录 的过滤器)的情况下, 获取用户信息. session.getAttribute("user")
- 再获取用户所具有的权限的信息: List<Authority>
- 检验用户是否有请求  servletPath 的权限: 可以思考除了遍历以外, 有没有更好的实现方式
- 若有权限则: 响应
- 若没有权限: 重定向到 403.jsp 

* others: 
- 用户若登录, 需要把用户信息(User 对象)放入到 HttpSession 中.
- 在检验权限之前, 需要判断用户是否已经登录.  

①. 管理权限:
> 封装权限信息: Authority
Authority{
//显示到页面上的权限的名字
private String displayName;

//权限对应的 URL 地址: 已权限对应着一个 URL, 例如 Article_1 -> /app_4/article1.jsp
private String url;
}

> 封装用户信息: User

User{
private String username;
private List<Autority> authorities;

//...
}

> 创建一个 UserDao:
User get(String username);
void update(String username, List<Autority>);
> 页面
authority-manager.jsp: 
* 有一个 text 文本框, 供输入 username, 提交后, 使用 checkbox 显示当前用户所有的权限的信息.
<form action="/day_40/AuthorityServlet?method=get" method="post">
Name: <input name="name" type="text"/>
<input type="submit" value="Submit"/>
</form>
* 检查 request 中是否有 user 信息, 若有, 则显示 
xxx 的权限为: 对应的权限的 checkbox 打上对号. 提示, 页面上需要通过两层循环的方式来筛选出被选择的权限. 
<form action="/day_40/AuthorityServlet?method=get" method="post">
Name: <input name="name" type="text"/>
<input type="submit" value="Submit"/>
</form>
<br><br>
AAA 的权限是:
<br><br>
<form action="/day_40/AuthorityServlet?method=update" method="post">
<!-- 使用隐藏域来保存用户的 name -->
<input name="name" type="hidden" value="AAA"/>
<input type="checkbox" name="authority" value="/app_4/article1.jsp" 
checked="checked"/>Article_1
<br><br
<input type="checkbox" name="authority" value="/app_4/article2.jsp" 
checked="checked"/>Article_2
<br><br>
<input type="checkbox" name="authority" value="/app_4/article3.jsp" 
checked="checked"/>Article_3
<br><br>
<input type="checkbox" name="authority" value="/app_4/article4.jsp" />Article_4
<br><br>
<input type="submit" value="Submit"/>
</form> 
> Servlet

authority-manager.jsp 提交表单后 get 方法: 获取表单的请求参数: username, 再根据 username 获取 User 信息. 把 user 放入到
request 中, 转发到 authority-manager.jsp.

authority-manager.jsp 修改权限的表单提交后 update 方法: 获取请求参数: username, authory(多选); 把选项封装为 List; 调用
UserDao 的 update() 方法实现权限的修改; 重定向到 authority-manager.jsp

day10


7. HttpSessionActivationListener


1). 监听实现了该接口和 Serializable 接口的 Java 类的对象随 session 钝化和活化事件

> 活化: 从磁盘中读取 session 对象

> 钝化: 向磁盘中写入 session 对象

> session 对象存储在tomcat 服务器的 work\Catalina\localhost\contextPath 目录下. SESSION.SER

2). 该监听器不需要在 web.xml 文件中进行配置. 

3). 
//在活化之后被调用. 
public void sessionDidActivate(HttpSessionEvent se)
//在钝化之前被调用
public void sessionWillPassivate(HttpSessionEvent se)


HttpSessionEvent: getSession()

4). 该监听器较少被使用. 

6. HttpSessionBindingListener

1). 监听实现了该接口的 Java 类的对象被绑定到 session 或从 session 中解除绑定的事件.

//当前对象被绑定到 session 时调用该方法
public void valueBound(HttpSessionBindingEvent event) 

//当前对象从 session 中解除绑定调用该方法
public void valueUnbound(HttpSessionBindingEvent event) 

2). 注意: 该监听器不需要在 web.xml 文件中进行配置. 

3). HttpSessionBindingEvent:
getName()
getValue()
getSession()

4). 该监听器较少被使用. 

5.  XxxAttributeListener
1). 监听 ServletContext, HttpSession, ServletRequest 中添加属性, 替换属性, 移除属性的事件监听器. 

2). 以 ServletRequestAttributeListener 为例:

//添加属性时被调用
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("向 request 中添加了一个属性...");
}
//移除属性时被调用
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("从 request 中移除了一个属性...");
}
//替换属性时被调用. 
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("request 中属性替换了...");
}

3). 这三个 ServletContextAttributeListener,
ServletRequestAttributeListener, HttpSessionAttributeListener 监听器较少被使用. 

4). API:

ServletRequestAttributeEvent:
> getName(): 获取属性的名字
> getValue(): 获取属性的值. 

4. ServletRequestListener & HttpSessionListener

1). 和 ServletContextListener 类似。 

2). 利用 ServletRequestListener、HttpSessionListener 以及 ServletContextListener 可以把 request, session
及 application 的生命周期进一步的做一了解. 

> request: 是一个请求, 当一个响应返回时, 即被销毁. 当发送一个请求时被创建. 注意, 请求转发的过程是一个 request 对象.
重定向是两个请求
> session: 当第一次访问 WEB 应用的一个 JSP 或 Servlet 时, 且该 JSP 或 Servlet 中还需要创建 session 对象. 此时服务器会
创建一个 session 对象. 
  session 销毁: session 过期, 直接调用 session 的 invalidate 方法, 当前 web 应用被卸载(session 可以被持久化).
  
  * 关闭浏览器, 并不意味着 session 被销毁, 还可以通过 sessionid 找到服务器中的 session 对象.  

JSESSIONID=F4119DE0FC93ED38E8EC83B24CFA3B81
http://localhost:8989/day_40/session.jsp;jsessionid=F4119DE0FC93ED38E8EC83B24CFA3B81

> application: 贯穿于当前的 WEB 应用的生命周期. 当前 WEB 应用被加载时创建 application 对象, 当前 WEB 应用被卸载时
销毁 application 对象.

3. ServletContextListener:

1). what: 监听 ServletContext 对象被创建或销毁的 Servlet 监听器

2). how:

> 创建一个实现了 ServletContextListener 的类, 并且实现其中的两个方法

public class HelloServletContextListner implements ServletContextListener

> 在 web.xml 文件中配置 Listener

<listener>
<listener-class>com.atguigu.javaweb.test.HelloServletContextListner</listener-class>
</listener>

3). why: ServletContextListener 是最常用的 Listener, 可以在当前 WEB 应用被加载时对当前 WEB 应用的相关资源进行初始化操作:
创建数据库连接池, 创建 Spring 的 IOC 容器, 读取当前 WEB 应用的初始化参数 ...


4). API: 


// SerlvetContext 对象被创建(即, 当前 WEB 应用被加载时)的时候, Servlet 容器调用该方法. 
public void contextInitialized(ServletContextEvent sce) 


// SerlvetContext 对象被销毁之前(即, 当前 WEB 应用被卸载时)的时候, Servlet 容器调用该方法. 
public void contextDestroyed(ServletContextEvent sce) 

ServletContextEvent 中的: getServletContext() 获取 ServletContext 


2. About JavaWEB


JavaSE

SQL
PL\SQL
JDBC
Html 
JavaScript 
jQuery             不熟练
XML, 
XML 解析(使用 DOM4J - 学习 Spring 的时候)
Servlet: 用于做 MVC 中的 Controller, 接受请求, 调用方法, 响应页面
JSP: 用于显示, 需要结合 EL 和 JSTL
EL, 
JSTL,
MVC 
HttpSession: 表示用于会话. pageContext, request, session, application, response, out, config, page, exception
JDBC(DBUtils, 数据库连接池),
Filter
Cookie, 
自定义标签, 

Listener
文件的上传下载
国际化

Ajax
ATGUIGU 网上商城

Struts2, Spring, Hibernate 
WebService, 第一个 JavaEE 项目.
JBPM, SpringMVC, SpringSecurity, 第二个 JavaEE 项目
Android, Android 的 2 个项目

1. request.getParameter() VS request.getAttribute() 

1). request.getParameter() 获取 GET、POST 请求的请求参数的值. 返回值一定是 String 类型. 

> 表单
> URL 后边的 ? 后面附着的值 index.jsp?name=tom

2). request.getAttribute() 获取 request 域中的属性值, 一定是先有 setAttribute(name, value). 然后再通过 
request.getAttribute(name) 获取属性值. 且属性值可以是任意类型. 

2. 以下 equals 方法中的


if (getClass() != obj.getClass())
return false;

1). 可以省略吗? 不能省略, 若省略则, 下边的强制类型转换可能会出现异常. 
2). 比较两个对象是否相等不是使用 equals 吗, 为什么使用 == 了呢? 每个类的 Class 对象只有一个! 所以可以使用 == 

public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Authority other = (Authority) obj;
if (url == null) {
if (other.url != null)
return false;
} else if (!url.equals(other.url))
return false;
return true;
}
day 11
5. 文件下载的需求:
1). 在文件上传成功后的 success.jsp 页面上提供一个 "下载资源" 的超链接
2). 点击 "下载资源" 的超链接, 会把请求发送到 Servlet, 读取数据库, 在页面上显示可以下载的资源信息
FileName: 11.尚硅谷_JavaWEB_监听器.pptx
Desc: AA
下载
FileName: 12.尚硅谷_JavaWEB_文件的上传和下载.pptx
Desc: BB
下载 
3). 再点击下载, 即可完成对应文件的下载. 
4. 文件的下载:
1). 步骤:
I.  设置 contentType 响应头: 设置响应的类型是什么 ? 通知浏览器是个下载的文件
response.setContentType("application/x-msdownload");
II. 设置 Content-Disposition 响应头: 通知浏览器不再有浏览器来自行处理(或打开)要下载的文件, 而由用户手工完成
response.setHeader("Content-Disposition", "attachment;filename=abc.txt");

III. 具体的文件: 可以调用 response.getOutputStream 的方式, 以 IO 流的方式发送给客户端
OututStream out = response.getOutputStream();
String pptFileName = "C:\\Users\\Think Pad\\Desktop\\__正在上课__\\11.尚硅谷_JavaWEB_监听器.pptx";
InputStream in = new FileInputStream(pptFileName);


byte [] buffer = new byte[1024];
int len = 0;


while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
in.close();


3. 如何修改小工具或框架的源代码 ?

1). 原则: 能不修改就不修改. 

2). 修改的方法:

> 修改源代码, 替换 jar 包中对应的 class 文件. 

> 在本地新建相同的包, 和类, 在这个类中修改即可. 

2. 使用 fileupload 组件完成文件的上传应用

1). 需求:

I.  上传

> 在 upload.jsp 页面上使用 jQuery 实现 "新增一个附件", "删除附件". 但至少需要保留一个.

> 对文件的扩展名和文件的大小进行验证. 以下的规则是可配置的. 而不是写死在程序中的. 

>> 文件的扩展名必须为 .pptx, docx, doc
>> 每个文件的大小不能超过 1 M
>> 总的文件大小不能超过 5 M.

> 若验证失败, 则在 upload.jsp 页面上显示错误消息: 

>> 若某一个文件不符合要求: xxx 文件扩展名不合法 或 xxx 文件大小超过 1 M
>> 总的文件大小不能超过 5 M.

> 若验证通过, 则进行文件的上传操作

>> 文件上传, 并给一个不能和其他文件重复的名字, 但扩展名不变
>> 在对应的数据表中添加一条记录. 

id  file_name  file_path  file_desc

II. 下载

问题: 如何清除上传文件临时文件夹的文件. 

1. 进行文件上传时, 表单需要做的准备:

1). 请求方式为 POST: <form action="uploadServlet" method="post" ... >
2). 使用 file 的表单域: <input type="file" name="file"/>
3). 使用 multipart/form-data 的请求编码方式: <form action="uploadServlet" method="post" enctype="multipart/form-data">


<form action="uploadServlet" method="post" enctype="multipart/form-data">

File: <input type="file" name="file"/>
<input type="submit" value="Submit"/>

</form>

4). 关于 enctype:


> application/x-www-form-urlencoded:表单 enctype 属性的默认值。这种编码方案使用有限的字符集,当使用了非字母和数字时,
必须用”%HH”代替(H 代表十六进制数字)。对于大容量的二进制数据或包含非 ASCII 字符的文本来说,这种编码不能满足要求。

> multipart/form-data:form 设定了enctype=“multipart/form-data”属性后,表示表单以二进制传输数据 

2. 服务端:

1). 不能再使用 request.getParameter() 等方式获取请求信息. 获取不到, 因为请求的编码方式已经改为 multipart/form-data, 以
二进制的方式来提交请求信息. 

2). 可以使用输入流的方式来获取. 但不建议这样做.

3). 具体使用 commons-fileupload 组件来完成文件的上传操作. 

I. 搭建环境: 加入 
commons-fileupload-1.2.1.jar
commons-io-2.0.jar

II. 基本思想: 

> commons-fileupload 可以解析请求, 得到一个 FileItem 对象组成的 List
> commons-fileupload 把所有的请求信息都解析为 FileItem 对象, 无论是一个一般的文本域还是一个文件域. 
> 可以调用 FileItem 的 isFormField() 方法来判断是一个 表单域 或不是表单域(则是一个文件域)
> 再来进一步获取信息

if (item.isFormField()) {
    String name = item.getFieldName();
    String value = item.getString();
    ...
} 

if (!item.isFormField()) {
    String fieldName = item.getFieldName();
    String fileName = item.getName();
    String contentType = item.getContentType();
    boolean isInMemory = item.isInMemory();
    long sizeInBytes = item.getSize();
    
    InputStream uploadedStream = item.getInputStream();
    ...
    uploadedStream.close();
}


III. 如何得到 List<FileItem> 对象. 


> 简单的方式

// Create a factory for disk-based file items
FileItemFactory factory = new DiskFileItemFactory();

// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);

// Parse the request
List /* FileItem */ items = upload.parseRequest(request);

> 复杂的方式: 可以为文件的上传加入一些限制条件和其他的属性

// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();

//设置内存中最多可以存放的上传文件的大小, 若超出则把文件写到一个临时文件夹中. 以 byte 为单位
factory.setSizeThreshold(yourMaxMemorySize);
//设置那个临时文件夹
factory.setRepository(yourTempDirectory);

// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);

//设置上传文件的总的大小. 也可以设置单个文件的大小. 
upload.setSizeMax(yourMaxRequestSize);

// Parse the request
List /* FileItem */ items = upload.parseRequest(request);

问题1: 如果是一个多选, 若何获取对应的字符串数组. 每一个都对应一个 FileItem 对象. 

<input type="checkbox" name="interesting" value="Reading"/>Reading
<input type="checkbox" name="interesting" value="Party"/>Party
<input type="checkbox" name="interesting" value="Sports"/>Sports
<input type="checkbox" name="interesting" value="Shopping"/>Shopping

问题2. 临时文件夹如何清空的问题: 手工删除的方式. 


day 12



1. 什么是国际化和本地化:


I.   本地化:一个软件在某个国家或地区使用时,采用该国家或地区的语言,数字,货币,日期等习惯。
II.  国际化:软件开发时,让它能支持多个国家和地区的本地化应用。使得应用软件能够适应多个地区的语言和文化风俗习惯
III. 本地敏感数据: 随用户区域信息而变化的数据称为本地信息敏感数据。例如数字,货币, 日期,时间等数据

2. 相关的 API:
I.   DateFormat 和 SimpleDateFormat √. 
II.  NumberFormat 
III. MessageFormat
IV.  ResourceBundle
V.   Locale

3. 关于国际化资源文件:
I.   properties 文件格式
II.  必须提供 基名.properties 文件和 基名_语言代码_国家代码.properties 文件
III. 相同的 基名 的资源文件必须有相同的 key. 
IV.  可能需要使用 native2ascii 工具把非 asc 码转为 asc 码.

4. WEB 的国际化

I.   可以使用 request.getLocale() 获取 Locale 对象
II.  可以使用 JSTL 的 fmt 标签完成的国际化. 后面使用框架提供的标签完成. 
III. 实现 "中文" "英文" 的切换:
> 提供两个超简洁. 携带不同的变量值
> 根据变量值确定对应的 Locale 对象
> 把 Locale 对象放入到  session 中
> 绑定 Locale 对应的资源文件.
IV.  其他 fmt 标签可以参考 standard-examples.war 中的例子. 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值