一、Servlet
Servle是和平台无关的服务器端组件,它运行在Servlet容器中。Servlet容器负责Servlet和客户的通信以及调用Servlet的方法。Servlet和客户的通信采用“请求/响应”的模式。
1.Servlet的功能:
- 创建并返回基于客户请求的动态HTML页面
- 创建可嵌入到现有HTML页面中的部分HTML页面
- 与其他服务器资源进行通信(如数据库等)
2.Servlet容器响应客户请求的过程
3.Servlet容器响应客户请求的过程
- (1)浏览器依据IP建立与容器的连接
- (2)浏览器将请求数据打包
- (3)容器解析请求数据包,封装request和response对象
- (4)容器依据请求资源路径找到Servlet创建对象
- (5)容器将request和response对象作为参数传入service方法,并调用
- (6)容器将响应数据打包发给浏览器
- (7)浏览器取出结果,生成页面
4.简单的Servlet应用
HelloServlet.class1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package pers.zx.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HelloServlet implements Servlet{
@Override
public void destroy() {
// TODO Auto-generated method stub
System.out.println("destroy");
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}
@Override
public void init(ServletConfig arg0) throws ServletException {
// TODO Auto-generated method stub
System.out.println("init");
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("service");
}
//构造方法
public HelloServlet() {
System.out.println("构造方法初始化");
}
}
web.xml配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<?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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<!-- 配置Servlet映射 -->
<servlet>
<!-- Servlet注册名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- Servlet全类名 -->
<servlet-class>pers.zx.servlet.HelloServlet</servlet-class>
</servlet>
<!-- /为根目录路径 -->
<servlet-mapping>
<!-- 需要和某一个servlet的节点的servlet-name子节点的文本节点一致-->
<servlet-name>HelloServlet</servlet-name>
<!-- 映射具体的访问路径:/代表当前WEB应用的根目录,hello标识具体url-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
5.Servlet的生命周期
(1)Servlet容器:是运行Servlet、JSP、Filter等的软件环境。
- 可以用来创建Servlet,并调用Servlet的相关生命周期方法。
- JSP、Filter、Listener、Tag…
(2)Servlet生命周期方法:都是由Servlet容器负责
- 构造器:只被调用一次,只有第一次请求Servlet时,创建Servlet的实例,调用构造器。所以Servlet是单实例的。
- init():只被调用一次,在创建好实例后立即被调用,用于初始化当前的Servlet。
- service():被多次调用,每次请求都会调用service(),实际用于响应请求。
- destroy():只被调用一次,当前Servlet所在的WEB应用被卸载前,用于释放当前Servlet所占用资源。
(3)load-on-startup标签:1
2
3
4
5
6
7
8<servlet>
<!-- Servlet注册名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- Servlet全类名 -->
<servlet-class>pers.zx.servlet.HelloServlet</servlet-class>
<!--指定Servlet被创建的时机-->
<load-on-startup>2</load-on-startup>
</servlet>
可以指定Servlet被创建的时机,若为负数,则在第一次请求时被创建(所配置的那个动作被调用),若为0或正数,则在当前WEB应用被Servlet容器加载时创建实例,且数字越小越早被创建。
(4)关于servlet-mapping
- 同一个Servlet可以被映射到多个URL上,即多个< servlet-mapping>标签的< servlet-name>标签可以设置同一个Servlet注册名。
1 | <servlet-mapping> |
- 在Servlet映射到的URL中也可以使用 通配符。但是只有两种固定的格式,一种是 .扩展名,另一种是 / 。
其中, .扩展名举个例子 : .html 所有html文件。
/ 是任意的文件都可以
/ 路径名 就是上面用的。
6.Servlet init()方法中的ServletConfig对象
配置了Servlet的配置信息,并且可以获取ServletContext对象
(1)配置Servlet的初始化参数1
2
3
4
5
6
7
8
9
10<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>pers.zx.servlet.HelloServlet</servlet-class>
<!-- 配置Servlet的初始化参数-->
<init-param>
<init-name>user</init-name>
<init-value>root</init-value>
</init-param>
</servlet>
(2)获取初始化参数
- getInitParameter(String name):获取指定参数名的初始化参数
- getInitParameterNames():获取参数名组成的Enumeration对象。
1
2
3
4
5
6
7
8
9//在init()方法中
String user=servletConfig.getInitParameter("user");
Enumeration<String> names=servletConfig.getInitParameterNames();
While(names.hasMoreElements()){
String name=names.nextElement();
String value=servletConfig.getInitParameterNames(name);
System.out.println(name+":"+value);
}
(3)获取Servlet的名1
String servletName=servletConfig.getServletName();
(4)获取Servlet的Context的方法:getServletContext();
ServletContext
1.可以由ServletConfig的对象获取
1
2ServletConfig c;
ServletContext s=c.getServletContext();2.该对象代表当前WEB应用:可以认为ServletContext是当前web应用的一个大管家,可以从中获取到当前web应用的各个方面的信息。
(1)设置初始化参数:可以看成是全局变量,可以被所有的Servlet使用,而上面Servlet的< init>初始化参数只由当前Servlet获取。1
2
3
4
5<!--配置当前Web应用的初始化参数,放在Servlet标签外-->
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</value>
</context-param>
如何获取?和上面获取值的方法一样:
getInitParameter(String name)
getInitParameterNames()1
2
3
4
5ServletConfig servletConfig;
ServletContext s=servletConfig.getServletContext();
String driver=servletContext.getInitParameter("driver");
......
(2)获取当前web应用的某一个文件在服务器上的绝对路径,而不是部署前的路径(文件应在WebContent下):
servletContext.getRealPath(String path);1
String path=servletContext.getRealPath("/filrname");
(3)获取当前web应用的名称:1
String path=servletContext.getContextPath();
(4)获取当前web应用的某一个文件对应的输入流
getResourceAsStream(String path):path的 / 为当前web应用的根目录1
2
3
4
5//原始方法
ClassLoader classLoader=getClass.getClassLoader();
InputStream in=classLoader.getResourceAsStream("jdbc.properties");
//上面说的方法
InputStream in1=servletContext.getResourceAsStream("/WEB_INF/classes/jdbc.properties");
7.HTTP协议
HTTP协议即超文本传输协议,是TCP/IP协议集中的一个应用层协议。用于定义WEB浏览器与WEB服务器之间交换数据的过程以及数据本身的格式。
(1)HTTP的会话方式
(2)浏览器访问多图网页的过程
即每获取一个部分都会发送一次请求。
(3)HTTP请求/响应消息结构
8.GET与POST
8.ServletRequest
(1)如何在Serlvet中获取请求的信息(用户名,密码等)
Servlet的service()方法用于应答请求,每次请求都会调用service()方法。我们可以在该方法中通过ServletRequest的对象获取响应的信息,以及ServletResponse的对象响应请求。
1
2
3public void service(ServletRequest request,ServletResponse)throws ServletException,IOException{
}ServletRequest:封装了请求信息,可以从中获取任何请求信息
- ServletResponse:封装了响应信息,如果想给用户什么响应,具体可以使用该接口的方法实现
(2)ServletRequest:
获取请求参数:
Stirng getParameter(String name):获取请求参数的名字,返回参数值
1
2//表单提交的name
String name = request.getParameter("name");String[] getParameterValues(String name):根据请求参数的名字,返回请求参数所对应的字符串数组,为什么是数组,是因为可能一个参数会对应多个值,比如多选框checkbox
1
2
3
4
5
6
7
8
9
10
11<form action="servletLogin" method="post">
food:
<input type="checkbox" name="food" value="apple"/>apple
<input type="checkbox" name="food" value="duck"/>duck
<input type="checkbox" name="food" value="drink"/>drink
</form>
//相应的service
String[] food=request.getParameterValues("food");
for(String f : food){
System.out.println(f);
}注意:当用getParameter获取时只会获取第一个选的,只有getParameterValues才会获取所有选的
Enumeration getParameterNames():返回对应的Enumeration对象,和前面的ServletConfig或者ServletContext的getInitParameterNames方法类似。
即获取一些参数和值,web页面传的参数Map getParameterMap():返回请求的键值对,key:参数名 value:参数值(String数组类型的value)
获取请求的 URI:
1
2
3
4HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String requestURI = httpServletRequest.getRequestURI();
System.out.println(requestURI); // /test/loginServlet获取请求方式:
1
2String method = httpServletRequest.getMethod();
System.out.println(method);
若是一个 GET 请求, 获取请求参数对应的那个字符串, 即 ? 后的那个字符串,post请求返回null1
2String queryString = httpServletRequest.getQueryString();
System.out.println(queryString); //user=atguigu&password=123456&interesting=game&interesting=party&interesting=shopping
- 获取请求的 Serlvet 的映射路径
1
2String servletPath = httpServletRequest.getServletPath();
System.out.println(servletPath); // /loginServlet
注意:HttpServletRequest: 是 SerlvetRequest 的子接口. 针对于 HTTP 请求所定义. 里边包含了大量获取 HTTP 请求相关的方法.
9.ServletResponse
ServletResponse: 封装了响应信息, 如果想给用户什么响应, 具体可以使用该接口的方法实现.。
getWriter(): 返回 PrintWriter 对象. 调用该对象的 print() 方法, 将把 print() 中的参数直接打印到客户的浏览器上。
1
2PrintWriter out=response.getWriter();
oit.print("写在web页面上");设置响应的内容类型: response.setContentType()
1
2//响应word类型,一般用于文件下载
response.setContentType("application/msword");sendRedirect(String location)
1
response.sendRedirect(/login.jsp);
10.GerericServlet
下面的代码和GenericServlet的源码相似,该抽象类是为了简化开发。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 自定义的一个 Servlet 接口的实现类: 让开发的任何 Servlet 都继承该类. 以简化开发
*/
public abstract class MyGenericServlet 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();
}
}
(1)说明:GenericServlet是一个Servlet,是Servlet接口和ServletConfig接口的实现类。它是一个抽象类,其中的service方法为抽象方法。
(2)具体实现原理:
- 1.在GenericServlet中声明了一个ServletConfig类型的成员变量,在init(ServletConfig)方法中进行初始化。
- 2.然后利用servletConfig成员变量实现了ServletConfig接口的方法
- 3.还指定了一个init()方法,在默认的init(ServletConfig)中进行调用,这样在实现该抽象类的子类中,就可以用init()方法直接初始化Servlet。
小总结,我们在开发代码的时候可以巧妙结合成员变量和初始化参数的方法(可能是构造函数)1
2
3
4
5
6
7
8
9
10
11//比如上面的,开始由于只有一个方法内部有ServletConfig的对象,而其他方法要实现相应的操作也要用到该对象,所以就可以在方法外面写个成员变量,在初始化时将其赋值就好了,此时其他方法也就可以用了
private ServletConfig servletConfig;
@Override
public void init(ServletConfig arg0) throws ServletException {
this.servletConfig = arg0;
}
//其他方法
public String getServletName() {
return servletConfig.getServletName();
}
11.HttpServlet
(1)它是一个 Servlet, 继承自 GenericServlet. 针对于 HTTP 协议所定制。
(2)在 service() 方法中直接把 ServletReuqest 和 ServletResponse 转为 HttpServletRequest 和 HttpServletResponse.并调用了重载的 service(HttpServletRequest, HttpServletResponse)
在 service(HttpServletRequest, HttpServletResponse) 获取了请求方式: request.getMethod(). 根据请求方式有创建了
doXxx() 方法(xxx 为具体的请求方式, 比如 doGet, doPost)
即根据具体的请求方式重写相应的方法。并在方法中可以调用具体的各种操作。如获取表单值然后。。。。。。
(3)实际开发中, 直接继承 HttpServlet, 并根据请求方式复写 doXxx() 方法即可
(4)好处: 直接由针对性的覆盖 doXxx() 方法; 直接使用 HttpServletRequest 和 HttpServletResponse, 不再需要强转
我们来看一下它的源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40@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
}
二、JSP
1.JSP概述
Java Server Page: Java 服务器端网页
JSP 是Servlet 编写的一种技术, 它将 Java 代码和 HTML 语句混合在同一个文件中编写,它只对网页中的要动态产生的内容采用 Java 代码来编写,而对固定不变的静态内容采用普通静态 HTML 页面的方式编写。1
2
3<%
jsp代码
%>
(1)Jsp运行原理
JSP 本质上是一个 Servlet.
每个JSP 页面在第一次被访问时, JSP 引擎将它翻译成一个 Servlet 源程序, 接着再把这个 Servlet 源程序编译成 Servlet 的 class 类文件。然后再由WEB容器(Servlet引擎)像调用普通Servlet程序一样的方式来装载和解释执行这个由JSP页面翻译成的Servlet程序。
(2)Jsp放的位置
JSP 可以放置在 WEB 应用程序中的除了 WEB-INF 及其子目录外的其他任何目录中,JSP 页面的访问路径与普通 HTML 页面的访问路径形式也完全一样。
2.Jsp页面隐含的九个对象
在<%%>中没有声明就可以用的对象。直接调用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public 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 的一个对象。可以获取请求的各种信息。1
2
3
4<%
//http://localhost:8080/loginServlet.jsp?name=zx
String name=request.getParameter("name");
%>
②. response: HttpServletResponse 的一个对象(在 JSP 页面中几乎不会调用 response 的任何方法.)1
2
3<%
Class clazz=response.getClass();
%>
③. pageContext: 页面的上下文, 是 PageContext 的一个对象. 可以从该对象中获取到其他 8 个隐含对象. 也可以从中获取到当前页面的其他信息. (学习自定义标签时使用它)1
2
3
4<%
ServletRequest req=pageContext.getRequest();
System.out.println(request== req);
%>
④. session: 代表浏览器和服务器的一次会话, 是 HttpSession 的一个对象. 后面详细学习。
<%
session.getId();//同一个session的id相同
%>
⑤. application: 代表当前 WEB 应用. 是 ServletContext 对象.1
2
3<%
application.getInitPatameter("user");
%>
⑥. config: 当前 JSP 对应的 Servlet 的 ServletConfig 对象(几乎不使用). 若需要访问当前 JSP 配置的初始化参数, 需要通过映射的地址才可以.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15映射 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>
1
2
3
4<%
config.getParameter("test");
//输出testValue
%>
⑦. out: JspWriter 对象. 调用 out.println() 可以直接把字符串打印到浏览器上.
<%
out.println(“hello”);
%>
⑧. page: 指向当前 JSP 对应的 Servlet 对象的引用, 但为 Object 类型, 只能调用 Object 类的方法(几乎不使用)
⑨. exception: 在声明了 page 指令的 isErrorPage=”true” 时, 才可以使用.1
2
3
4
5<%@ page isErrorPage="true" %>
<%
exception. ........
%>
注意:
pageContext, request, session, application(对属性的作用域的范围从小到大)
out, response, config, page
exception
3.Jsp语法
(1)Jsp模板元素:1
jsp页面中静态的HTML标签内容,以及相应的头
(2)JSP表达式(expression):提供了将一个 java 变量或表达式的计算结果输出到客户端的简化方式,它将要输出的变量或表达式直接封装在<%= 和 %>之中。1
2
3
4
5<%
Date date = new Date();
out.print(date);
%>
<%= date %>
(3)SP脚本片断(scriptlet):是指嵌套在<% 和 %>之中的一条或多条Java程序代码。 多个脚本片断中的代码可以相互访问。1
2
3
4
5
6
7
8
9
10
11
12
13<%
String ageStr =request.getParameter("age");
Integer age = Integer.parseInt(ageStr);
if(age >= 18){
%>
成人...
<%
}else{
%>
未成人...
<%
}
%>
(4) JSP 声明: JSP 声明将 Java 代码封装在<%!和 %>之中,它里面的代码将被插入进 Servle t的 _jspService 方法的外面。(在 JSP 页面中几乎从不这样使用)1
2
3<!%
void test();//这个代码就会编在上面所说的地方
%>
(5)JSP注释的格式:<%– JSP 注释 –%> < !– HTML 注释 –>
区别: JSP 注释可以阻止 Java 代码的执行。
4.Jsp域对象的属性操作
(1)方法
- void setAttribute(String name, Object o): 设置属性
- Object getAttribute(String name): 获取指定的属性
pageContext, request, session, application 对象都有这些方法。这四个对象也称之为域对象。1
2
3
4
5
6
7
8<%
pageContext.setAttribute("pageContextAttr","pageContextValue");
request.setAttribute("requestAttr","requestValue");
session.setAttribute("sessionAttr","sessionValue");
application.setAttribute("applicationAttr","applicationValue");
%>
pageContext:<%= pageContext.getAttribute("pageContextAttr")%>
..............
(1)pageContext: 属性的作用范围仅限于当前 JSP 页面,同一个页面。
(2)request: 属性的作用范围仅限于同一个请求,不同页面有不同请求。
(3)session: 属性的作用范围限于一次会话: 浏览器打开直到关闭称之为一次会话(在此期间会话不失效)
(4)application: 属性的作用范围限于当前 WEB 应用。是范围最大的属性作用范围, 只要在一处设置属性, 在其他各处的 JSP 或 Servlet 中都可以获取到。
- Enumeration getAttributeNames(): 获取所有的属性的名字组成的 Enumeration 对象
- removeAttribute(String name): 移除指定的属性
5.请求的重定向与请求的转发
请求转发与请求重定向都是对页面进行跳转。
(1)本质区别:请求的转发只发出了一次请求, 而重定向则发出了两次请求。
(所以对于request域属性,可以继续利用转发获取先前的值)
(2)具体:
①. 请求的转发: 地址栏是初次发出请求的地址。
请求的重定向: 地址栏不再是初次发出的请求地址. 地址栏为最后响应的那个地址 。
②. 请求转发: 在最终的 Servlet 中, request 对象和中转的那个 request 是同一个对象.。
请求的重定向: 在最终的 Servlet 中, request 对象和中转的那个 request 不是同一个对象.
③. 请求的转发: 只能转发给当前 WEB 应用的的资源
请求的重定向: 可以重定向到任何资源.
④. 请求的转发: / 代表的是当前 WEB 应用的根目录(http://localhost:8080/test/)
请求的重定向: / 代表的是当前 WEB 站点的根目录。(http://localhost:8080/)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<%
//1.请求转发的代码
request.getRequestDispatcher(".jsp").forward(request,response);
//2.请求的重定向
response.sendRedirect(.jsp);
//可以任意地址
response.sendRedirect("http://www.baidu.com");
%>
class中
//1.调用HttpServletRequest的getRequestDispatcher()方法获取RequestDispatcher对象,然后调用getRequestDispatcher()传入需要转发的地址
RequestDispatcher requestDispatcher=request.getRequestDispatcher(/+path);
//2.调用HttpServletRequest的forword(request,response)进行转发
requestDispatcher.forward(request,response);
//3.执行请求的重定向,直接调用response.sendRedirect(path)方法,path为要重定向的地址。
response.senRedirect(path);//不用加 /
6.JSP指令
JSP指令(directive)是为JSP引擎而设计的, 它们并不直接产生任何可见输出, 而只是告诉引擎如何处理JSP页面中的其余部分。
(1) 在目前的JSP 2.0中,定义了page、include 和 taglib这三种指令。
(2) page 指令:page指令用于定义JSP页面的各种属性, 无论page指令出现在JSP页面中的什么地方, 它作用的都是整个JSP页面, 为了保持程序的可读性和遵循良好的编程习惯, page指令最好是放在整个JSP页面的起始位置。
(3)page 指令常用的属性:
①. import 属性: 指定当前 JSP 页面对应的 Servlet 需要导入的类。1
<%@page import="java.text.DateFormat"%>
②. session 属性: 取值为 true 或 false, 指定当前页面的 session 隐藏变量是否可用, 也可以说访问当前页面时是否一定要生成 HttpSession对象。1
<%@ page session="false" %>
③. errorPage 和 isErrorPage:
(1) errorPage 指定若当前页面出现错误的实际响应页面时什么. 其中 / 表示的是当前 WEB 应用的根目录.1
<%@ page errorPage="/error.jsp" %>
(2)在响应 error.jsp 时, JSP 引擎使用的请求转发的方式.
(3)isErrorPage 指定当前页面是否为错误处理页面, 可以说明当前页面是否可以使用 exception 隐藏变量. 需要注意的是: 若指定 isErrorPage=”true”, 就可以使用 exception 的方法了, 一般不建议能够直接访问该页面.
(4)如何使客户不能直接访问某一个页面呢 ? 对于 Tomcat 服务器而言, WEB-INF 下的文件是不能通过在浏览器中直接输入地址的方式来访问的. 但通过请求的转发是可以的!
(5)还可以在 web.xml 文件中配置错误页面:1
2
3
4
5
6
7
8
9
10
11
12<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.
⑦.include指令:<%@ include file=”.jsp” %>
(1)include 指令用于通知 JSP 引擎在翻译当前 JSP 页面时将其他文件中的内容合并进当前 JSP 页面转换成的 Servlet 源文件中,这种在源文件级别进行引入的方式称之为静态引入, 当前JSP页面与静态引入的页面紧密结合为一个Servlet。
(2)file属性的设置值必须使用相对路径, 如果以 / 开头,表示相对于当前WEB应用程序的根目录(注意不是站点根目录),否则,表示相对于当前文件。
7.JSP标签
(1)jsp:incluce 标签:
< jsp:include page=”b.jsp”></jsp:include>
动态引入: 并不是像 include 指令生成一个 Servlet 源文件, 而是生成两个 Servlet 源文件, 然后通过一个方法的方式把目标页面包含进来。
包含的Servlet源文件会引入
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, “b.jsp”, out, false);
(2)jsp:forward:转发页面
< !–/是根目录,include是文件夹–>
< jsp:forward page=”/include/b.jsp”>< /jsp:forward>
相当于.
<%
request.getRequestDispatcher(“/include/b.jsp”).forward(request, response);
%>
使用 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”) 获取到传入的请求参数.
8.中文乱码
(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:8080/docs/config/index.html 文档的 useBodyEncodingForURI 属性. 为 Connector 节点添加 useBodyEncodingForURI=”true” 属性即可.
<Connector connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443" useBodyEncodingForURI="true"/>
//补充:在java类的service代码中
//防止中文乱码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
三、MVC设计模式
1.概念:
MVC(Model-View-Controller)的简称,即模型-视图-控制器。
MVC是一种设计模式,它把应用程序分成三个核心模块:模型、视图、控制器,它们各自处理自己的任务。
- ①.Model:
(1)模型是应用程序的主体部分,模型表示业务数据(dao)和业务逻辑(service)
(2)一个模型可以为多个视图提供数据
(3)由于应用于模型的代码只需写一次就可以被多个视图重用,所以提高了代码的可重用性 - ②.View
视图是用户看到并与之交互的界面,作用如下:
(1)视图向用户显示相关数据
(2)接收用户输入
(3)不进行任何实际的业务处理 - ③.Controller
(1)控制器接收用户的输入并调用模型和视图去完成用户的需求
(2)控制器接收请求并决定调用那个模型组件去处理请求,然后决定调用那个视图来显示模型处理返回的数据
2.MVC处理过程
说明:首先,客户端发送请求给服务器,服务器收到请求后,根据相应的请求调用Servlet,Servlet根据请求信息调用POJO(简单java对象)来访问数据库并返回相应的结果,根据结果Servlet会转向相应的Jsp页面来显示用户的请求结果,然后响应给客户端。
注意:POJO与JavaBean
(1)POJO:Plain Ordinary Java Object(简单普通的java对象)主要用来指代那些没有遵循特定java对象模型,约定或者框架的对象。
POJO定义一些private属性,并设置相应的get/set方法。没有任何继承也没有任何接口实现,更没有被其他框架侵入的java对象。
(2)JavaBean: 在是Java开发中一种可重用组件。是一种编写代码的规范。
- 1.所有属性为private。
- 2.这个类必须有一个公共的缺省构造函数。即是提供无参数的构造器。
- 3.这个类的属性使用getter和setter来访问,其他方法遵从标准命名规范。
- 4.这个类应是可序列化的。实现serializable接口。
其实JavaBean就是特殊的POJO。
(3)POJO与JavaBean的区别
- ①.POJO其实是比JavaBean更纯净的简单类或接口。POJO严格地遵守简单对象的概念,而一些JavaBean中往往会封装一些简单逻辑。
- ②.POJO主要用于数据的临时传递,它只能装载数据, 作为数据存储的载体,而不具有业务逻辑处理的能力。
- ③.Javabean虽然数据的获取与POJO一样,但是Javabean当中可以有其它的方法。
3.MVC小总结
四、Cookie
由于HTTP协议是一种无状态的协议,不能够识别每一个请求是否一样。所以作为web服务器,必须能够采用一种机制来唯一地标识一个用户,同时记录该用户的状态。所以提出了会话跟踪的概念。
在Servlet中,常用以下两种机制完成会话跟踪。
- Cookie
- Session
(1)Cookie机制
(2)Cookie传送过程
(3)在Servlet程序中使用Cookie1
2
3
4
5
6
7<%
//在JavaWeb规范中,使用Cookie类代表cookie
//1.创建一个Cookie对象
Cookie cookie=new Cookie("name","zx");
//2.调用response的一个方法把cookie传个客户端
response.addCookie(cookie);
%>
获取Cooike
<%
Cookie [] cookies=request.getCookies();
if(cookies !=null&&cookies.length>0){
for(Cookie cookie:cookies){
out.print(cookie.getName()+":"+cookie.getValue());
}else{
out.print("没有Cookie,正在创建并返回");
//1.创建一个Cookie对象
Cookie cookie=new Cookie("name","zx");
//2.调用response的一个方法把cookie传个客户端
response.addCookie(cookie);
}
}
%>
(4)Cookie发送
(5)Cookie读取
(6)会话cookie与持久化cookie的区别
设置cookie存在时间
/setMaxAge:设置Cookie的最大时效,以秒为单位,若为0,表示立即删除该Cookie,若为负数,表示不储存该Cookie,若为正数,则表示Cookie的存储时间/
int time;
cookie.steMaxAge(time);
1.Cooike自动登陆
就是设置Cookie的过期时间,若在过期时间内就可以通过存储在Cookie中的信息自动登陆。登陆时读取登陆信息。
//1.第一次登陆时,若name存在获取请求参数的name信息储存在Cookie中,并设置最大时效30s
String name=request.getParameter("name");
if(name!=null&& !name.trim().equals("")){
out.print("Hello: "+name);
Cookie cookie=new Cookie("name",name);
cookie.setMaxAge(30);
response.addCookie(cookie);
}else{
//2.下次访问页面时从cookie中读取用户信息
Cookie[] cookies=request.getCookies();
if(coolies!=null&& cookies.length>0)
for(Cookie cookie: cookies){
String coookieName=cookie.getName();
//"name"为cookie名
String val=cookie.getValue90;
name=val;
}
}
}
if(name!=null&& !name.trim().equals("")){
out.print("Hello: "+name);
}else{
//若没有请求参数,也没有Cookie,则重定向到login.jsp
response.sendRedirect("login.jsp");
}
2.Cooike显示最近浏览商品
book.jsp
<h4>Book Detail Page</h4>
Book: <%= request.getParameter("book") %>
<br><br>
<a href="books.jsp">Return</a>
<%
String book = request.getParameter("book");
//把书的信息以 Cookie 方式传回给浏览器,删除一个 Cookie
//1. 确定要被删除的 Cookie:
//前提: ATGUIGU_BOOK_ 开头的 Cookie 数量大于或等于 5,
Cookie [] cookies = request.getCookies();
//保存所有的 ATGUIGU_BOOK_ 开头的 Cookie
List<Cookie> bookCookies = new ArrayList<Cookie>();
//用来保存和 books.jsp 传入的 book 匹配的那个 Cookie
Cookie tempCookie = null;
if(cookies != null && cookies.length > 0){
for(Cookie c: cookies){
String cookieName = c.getName();
if(cookieName.startsWith("ATGUIGU_BOOK_")){
bookCookies.add(c);
if(c.getValue().equals(book)){
tempCookie = c;
}
}
}
}
//①. 且若从 books.jsp 页面传入的 book 不在 ATGUIGU_BOOK_ 的 Cookie 中则删除较早的那个 Cookie
//( ATGUIGU_BOOK_ 数组的第一个 Cbookie),
if(bookCookies.size() >= 5 && tempCookie == null){
tempCookie = bookCookies.get(0);
}
//②. 若在其中,则删除该 Cookie
if(tempCookie != null){
tempCookie.setMaxAge(0);
response.addCookie(tempCookie);
}
//2. 把从 books.jsp 传入的 book 作为一个 Cookie 返回
Cookie cookie = new Cookie("ATGUIGU_BOOK_" + book, book);
response.addCookie(cookie);
%>
books.jsp
<h4>Books Page</h4>
<a href="book.jsp?book=JavaWeb">Java Web</a><br><br>
<a href="book.jsp?book=Java">Java</a><br><br>
<a href="book.jsp?book=Oracle">Oracle</a><br><br>
<a href="book.jsp?book=Ajax">Ajax</a><br><br>
<a href="book.jsp?book=JavaScript">JavaScript</a><br><br>
<a href="book.jsp?book=Android">Android</a><br><br>
<a href="book.jsp?book=Jbpm">Jbpm</a><br><br>
<a href="book.jsp?book=Struts">Struts</a><br><br>
<a href="book.jsp?book=Hibernate">Hibernate</a><br><br>
<a href="book.jsp?book=Spring">Spring</a><br><br>
<br><br>
<%
//显示最近浏览的 5 本书
//获取所有的 Cookie
Cookie [] cookies = request.getCookies();
//从中筛选出 Book 的 Cookie:如果 cookieName 为 ATGUIGU_BOOK_ 开头的即符合条件
//显示 cookieValue
if(cookies != null && cookies.length > 0){
for(Cookie c: cookies){
String cookieName = c.getName();
if(cookieName.startsWith("ATGUIGU_BOOK_")){
out.println(c.getValue());
out.print("<br>");
}
}
}
%>
通过存取cookie里的信息来操作。
3.Cooike的作用路径
如何访问当前项目其他目录的cookie信息,可以通过 cookie.setPath(request.getContextPath()); 来设置cookie的作用范围。
//设置 Cookie 的作用范围: cookie.setPath(request.getContextPath());
//Cookie 的 作用范围: 可以作用当前目录和当前目录的子目录. 但不能作用于当前目录的上一级目录. 可以通过 setPath 方法来设置 Cookie 的作用范围, 其中 / 代表站点的根目录.
//访问上一级目录的cookie
< a href=”../cookie2.jsp”>To Read Cookie< /a>
4.Cooike小结
cookie可以用于自动登陆,也可以用来记录用户在web界面的访问行为。并获取这些访问行为。
以自动登陆为例,在用户第一次登录的时候,记录用户的信息放在cookie中,下次再次访问时读取cookie中的信息,自动登陆。
五、HttpSession
1.HttpSession概述
一类用来在客户端与服务器之间保持状态的解决方案,有时候Session也用来指这种解决方案的存储结构。
(1)Session机制
如果客户端请求时没有设置session,服务器在响应客户端时会生成一个cookie返回给客户端,客户端下次再访问服务器时会带着这个cookie一起访问,找到相应的HttpSession对象。
<%
String str=session.getId();
out.print(str);
%>
(2)保存Session id的几种方式
(3)Session cookie
// 持久化session的cookie
<%
String str=session.getId();
out.print(str);
Cookie cookie=new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(20);
response.addCookie(cookie);
%>
2.HttpSession生命周期
(1)什么时候创建session对象
①. 对于 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.
<%
//可以获得session
HttpSession session = request.getSession(true);
out.print(session.getId());
//设置过期时间
session.setMaxInactiveInterval(60);
out.print(session.getMaxInactiveInterval());
//使当前的 HttpSession 对象失效.
session.invalidate();
%>
3.HttpSession常用方法
hello.jsp
//隐式session调用方法
SessionID: <%= session.getId() %>
<br><br>
IsNew: <%= session.isNew() %>
<br><br>
MaxInactiveInterval: <%= session.getMaxInactiveInterval() %>
<br><br>
CreateTime: <%= session.getCreationTime() %>
<br><br>
LastAccessTime: <%= session.getLastAccessedTime() %>
<br><br>
Hello: <%= request.getParameter("username") %>
<br><br>
<%
//通过session储存用户名,然后在后面使用,由于返回login界面时会用它,所以就用session
session.setAttribute("username", request.getParameter("username"));
%>
<a href="login.jsp">重新登录</a>
<a href="logout.jsp">注销</a>
login.jsp
SessionID: <%= session.getId() %>
<br><br>
IsNew: <%= session.isNew() %>
<br><br>
MaxInactiveInterval: <%= session.getMaxInactiveInterval() %>
<br><br>
CreateTime: <%= session.getCreationTime() %>
<br><br>
LastAccessTime: <%= session.getLastAccessedTime() %>
<br><br>
<%
//根据session获取name
Object username = session.getAttribute("username");
if(username == null){
username = "";
}
%>
<form action="hello.jsp" method="post">
username: <input type="text" name="username"
value="<%= username %>"/>
<input type="submit" value="Submit"/>
</form>
注意:之所以在hello.jsp中设置session储存值是因为login在登陆后会产生session。然后获取值供后面调用
//不用session也可以获得用户名,用request
request.getParameter("username");
4.HttpSession URL重写
(1)利用URL重写实现Session跟踪
<form action="<%= response.encodeURL("hello.jsp") %>" method="post">
//此时可以在禁用cookie的浏览器中,找到相同的session对象。就是在url后面加了hello.jsp:jsession:id值
5.HttpSession 小结
HttpSession相关的API
6.Javaweb中的绝对路径与相对路径
(1)相对路径:即指相对于当前调用该路径的方法的路径。
举个例子:一个文件夹下有个a.jsp,和b.jsp,当在a.jsp中调用b.jsp时,这时相对路径就是在a.jsp的路径的基础上的路径。
比方说:访问a的路径localhost/test/a.jsp,此时在a中访问b,则路劲就是<href=”b.jsp”>< /href>这里隐含了相对路径localhost/test/
(2)绝对路径:要访问文件的具体位置,在web程序中一般用
request.contextPath()+具体路径(/test/a.jsp)
// request.contextPath()就是项目的上下文路径
7.防止表单重复提交
(1)说明一下表单重复提交的情况
①. 在表单提交到一个 Servlet, 此时地址栏还保留着 Serlvet 的那个路径, 在响应页面点击 “刷新” Servlet 又通过请求转发的方式响应一个 JSP(HTML) 页面。
②. 在响应页面没有到达时重复点击 “提交按钮”.
③. 点击 “返回”, 再点击 “提交”。
(2)不是重复提交的情况
点击返回,然后刷新原先的登陆页面,再提交。
(3)如何避免重复提交
①. 仅提供一个隐藏域: < input type=”hidden” name=”token” value=”123”/>. 行不通: 没有方法清除固定的请求参数.
②. 把标记放在 request 中. 行不通, 因为表单页面刷新后, request 已经被销毁, 再提交表单是一个新的 request.
③. 把标记放在 session 中. 可以。
在表单中做一个标记, 提交到 Servlet 时, 检查标记是否存在且是否和预定义的标记一致, 若一致, 则受理请求,并销毁标记, 若不一致或没有标记, 则直接响应提示信息: “重复提交” 。
1.在原表单页面, 生成一个随机值 token
2.在原表单页面, 把 token 值放入 session 属性中
3.在原表单页面, 把 token 值放入到 隐藏域 中.
4.在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
5.比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
6.若不一致, 则直接响应提示页面: “重复提交”
代码示例:
//TokenServlet:处理响应信息,并获取页面的session和隐藏域中的值来进行比较
public class TokenServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
Object token = session.getAttribute("token");
String tokenValue = request.getParameter("token");
System.out.println(token);
System.out.println(tokenValue);
if(token != null && token.equals(tokenValue)){
//获取一次后删除
session.removeAttribute("token");
}else{
response.sendRedirect(request.getContextPath() + "/token/token.jsp");
return; }
String name = request.getParameter("name");
//访问数据库服务器...
System.out.println("name: " + name);
request.getRequestDispatcher("/token/success.jsp").forward(request, response);
//response.sendRedirect(request.getContextPath() + "/token/success.jsp");
}
}
//登陆界面
<body>
<%--
String tokenValue = new Date().getTime() + "";
session.setAttribute("token", tokenValue);
--%>
<form action="<%= request.getContextPath() %>/tokenServlet" method="post">
<input type="hidden" name="token" value="<%= tokenValue%>"/>
name: <input type="text" name="name"/>
<input type="submit" value="Submit"/>
</form>
</body>
注意:所谓隐藏域就是在原页面不想被显示的值,但在后面的页面又想通过name或id获取它的值。比如上面的隐藏域只是给表单做标记但是不想它显示出来。
8.验证码原理
1.在原表单页面, 生成一个验证码的图片, 生成图片的同时, 需要把该图片中的字符串放入到 session 中.
2.在原表单页面, 定义一个文本域, 用于输入验证码.
3.在目标的 Servlet 中: 获取 session 和 表单域 中的 验证码的 值
4.比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 验证码 属性清除
5.若不一致, 则直接通过重定向的方式返回原表单页面, 并提示用户 “验证码错误”
六、JavaBean
七、EL表达式
${}都是以这种形式展现的
//以前的写法
User user=(User)session.getAttribute("user");
String sex=user.getSex();
//EL写法
${sessionScope.user.sex}
都是取sex,可以看出EL表达式更简洁。
1.EL表达式的两种使用方式
EL有两种储存数据的方式:. 和 [ ]都用在${ }中
${sessionScope.user.sex}
等同于
${sessionScope.user["user"]}
[ ]这种方式都是用于有属性名为:com.zx.user(举个例子),这时用 . 取不到值。
User user=new User();
user.setSex("M");
session.setAttribute("com.zx.uesr",user);
sex:${sessionScope["com.zx.user"].sex}
而用sessionScope[“com.zx.user”].sex
2.EL变量
EL存取变量数据的方法很简单,打个比方:${ username}就是取出某一范围中名称为username变量。由于没有指定是哪一个范围的username,所以它的默认值会先从Page范围找,如果找不到,在依次到Request、Session、Application中找。如果中途找到它就直接传回,停止寻找。如果所有地方都没找到,就返回null。
- 自动搜索的范围
- 也可以直接指出范围内的值
- EL自动类型转换
age: ${ param.age+10} //输出结果和
要是不自动转换就会输出一个字符串
比如:age: <%=request.getParameter(“age”)+10%>
3.EL隐含对象
①.与范围有关的
- applicationScope
范围和JSP Scope中的application相同,它的属性范围是从服务器一开始执行到服务器关闭为止。
- sessionScope
范围和JSP Scope中的session相同,它的属性范围就是用户持续在服务器连接的时间。
- requestScope
范围和JSP的Request相同,requestScope的范围是指从一个JSP网页请求到另一个JSP网页请求之间,随后此属性就会失效
- pageScope
范围和JSP的Page相同,也就是单单一页JSP Page的范围
②.与输入有关的隐含对象
param:一个元素
paramValues:一组元素
param与request.getParamer(String)类似
paramValues与request.getParamerValues(String)类似
举个例子:
${param.name}
request.getParamer(“name”);
③.其他隐含对象
cookie
JSESSIONID: ${cookie.JSESSIONID}
JSESSIONID: ${cookie.JSESSIONID.value}
header
headerValues
initParam
获取当前web应用的构造参数
pageContext
使用${pageContext}来获取有关用户或页面的详细信息。
八、自定义标签
用户自定义的一种jsp标记。当一个含有自定义标签的jsp页面被jsp引擎编译成servlet时,tag标签被转化成了对一个称为标签处理类的对象的操作。于是,当jsp页面被jsp引擎转化为servle后,实际上tag标签被转化为了对tag处理类的操作。
自定义标签可以降低jsp开发的复杂度和维护量。
1.自定义标签例子:
①. 创建一个标签处理器类: 实现 SimpleTag 接口.
②. 在 WEB-INF 文件夹下新建一个 .tld(标签库描述文件) 为扩展名的 xml 文件. 并拷入固定的部分: 并对
description, display-name, tlib-version, short-name, uri 做出修改。
//固定的部分
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<short-name>zx</short-name>
<uri>http://java.sun.com/jsp/jstl/core</uri>
</taglib>
③. 在 tld 文件中描述自定义的标签:
<!-- 描述自定义的 HelloSimpleTag 标签 -->
<tag>
<!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
<name>hello</name>
<!-- 标签所在的全类名 -->
<tag-class>pers.zx.javaweb.tag.HelloSimpleTag</tag-class>
<!-- 标签体的类型 -->
<body-content>empty</body-content>
</tag>
④. 在 JSP 页面上使用自定义标签:
使用 taglib 指令导入标签库描述文件: <%@taglib uri=” < url>” prefix=”< short-name>” %>
使用自定义的标签: < zx:hello/>
2.通过自定义标签为其设置属性,并输出在页面上
接续上面,我们主要用到实现 SimpleTag 接口中的doTag方法。
①.setJspContext: 一定会被 JSP 引擎所调用, 先于 doTag, 把代表 JSP 引擎的 pageContext 传给标签处理器类.
这里我们在类里定义一个PageContext的成员变量
private PageContext pageContext;
用在该类中
注意:由于有pagecontext所以他可以在该类中调用前面说的任何隐含变量,来获取值。
private PageContext pageContext;
@Override
public void setJspContext(JspContext arg0) {
this.pageContext = (PageContext) arg0;
}
②.定义属性
先在标签处理器类中定义 setter 方法. 建议把所有的属性类型都设置为 String 类型.
private String value;
private String count;
public void setValue(String value) {
this.value = value;
}
public void setCount(String count) {
this.count = count;
}
③.在 tld(标签描述文件) 描述文件中来描述属性:
<!-- 描述当前标签的属性 -->
<attribute>
<!-- 属性名, 需和标签处理器类的 setter 方法定义的属性相同 -->
<name>value</name>
<!-- 该属性是否被必须 -->
<required>true</required>
<!-- rtexprvalue: runtime expression value
当前属性是否可以接受运行时表达式的动态值 -->
<rtexprvalue>true</rtexprvalue>
</attribute>
④.在页面中使用属性, 属性名同 tld 文件中定义的名字.
//name是传的值,一般是表单提交的
<zx:hello value="${param.name }" count="10"/>
⑤.通常情况下开发简单标签直接继承SimpleTagSupport 就可以了. 可以直接调用其对应的 getter 方法得到对应的 API
public class SimpleTagSupport implements SimpleTag{
public void doTag()
throws JspException, IOException{}
private JspTag parentTag;
public void setParent( JspTag parent ) {
this.parentTag = parent;
}
public JspTag getParent() {
return this.parentTag;
}
private JspContext jspContext;
public void setJspContext( JspContext pc ) {
this.jspContext = pc;
}
protected JspContext getJspContext() {
return this.jspContext;
}
private JspFragment jspBody;
public void setJspBody( JspFragment jspBody ) {
this.jspBody = jspBody;
}
protected JspFragment getJspBody() {
return this.jspBody;
}
}
3.带标签体的自定义标签SetJspBody
①.若一个标签有标签体:
< zx:testJspFragment>abcdefg< /zx:testJspFragment>
在自定义标签的标签处理器中使用 JspFragment 对象封装标签体信息
②.若配置了标签含有标签体, 则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 传递给标签处理器类,在 SimpleTagSupport 中还定义了一个 getJspBody() 方法, 用于返回 JspFragment 对象。
③.JspFragment 的 invoke(Writer) 方法: 把标签体内容从 Writer 中输出, 若为 null, 则等同于 invoke(getJspContext().getOut()), 即直接把标签体内容输出到页面上
public void doTag() throws JspException, IOException {
JspFragment bodyContent = getJspBody();
//JspFragment.invoke(Witer): Writer 即为标签体内容输出的字符流, 若为 null, 则
//输出到 getJspContext().getOut(), 即输出到页面上.
//1. 利用 StringWriter 得到标签体的内容.
StringWriter sw = new StringWriter();
bodyContent.invoke(sw);
//2. 把标签体的内容都变为大写
String content = sw.toString().toUpperCase();
//3. 获取 JSP 页面的 out 隐含对象, 输出到页面上
getJspContext().getOut().print(content);
}
tld文件
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>MyTag 1.2 core library</description>
<display-name>MyTag core</display-name>
<tlib-version>1.2</tlib-version>
<short-name>zx</short-name>
<uri>http://zx.com/myTag/core</uri>
<tag>
<name>testJspFragment</name>
<tag-class>com.zx.javaweb.TestJspFragment</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
④.在 tld 文件中, 使用 body-content 节点来描述标签体的类型:
1.scriptless: 标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素
2.tagdependent: 表示标签体交由标签本身去解析处理。
若指定 tagdependent,在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器
3.empty: 没有标签体
4.标签体与属性一起用
tld
<tag>
<name>printUpper</name>
<tag-class>com.zx.javaweb.PrintUpperTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>time</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
doTag()代码
public class PrintUpperTag extends SimpleTagSupport {
//属性+set方法
private String time;
public void setTime(String time) {
this.time = time;
}
@Override
public void doTag() throws JspException, IOException {
//1. 得到标签体的内容
JspFragment bodyContent = getJspBody();
StringWriter sw = new StringWriter();
bodyContent.invoke(sw);
String content = sw.toString();
//2. 变为大写
content = content.toUpperCase();
//3. 得到 out 隐含变量
//4. 循环输出
int count = 1;
try {
count = Integer.parseInt(time);
} catch (Exception e) {}
for(int i = 0; i < count; i++){
getJspContext().getOut().print(i + 1 + "." + content + "<br>");
}
}
}
调用代码
//其中value是属性值,xiaohuzixiansheng是标签体内容
<zx:printUpper value="10">xiaohuzixiansheng</zx:printUpper>
5.SetParent带父标签的自定义标签
①. 父标签无法获取子标签的引用, 父标签仅把子标签作为标签体来使用.
②. 子标签可以通过 getParent() 方法来获取父标签的引用(需继承 SimpleTagSupport 或自实现 SimpleTag 接口的该方法):若子标签的确有父标签, JSP 引擎会把代表父标签的引用通过 setParent(JspTag parent) 赋给标签处理器
③. 注意: 父标签的类型是 JspTag 类型. 该接口是一个空接口, 但是来统一 SimpleTag 和 Tag 的. 实际使用需要进行类型的强制转换.
④. 在 tld 配置文件中, 无需为父标签有额外的配置. 但, 子标签是是以标签体的形式存在的, 所以父标签的 < body-content>< /body-content>需设置为 scriptless
tld
<tag>
<name>parentdoTag</name>
<tag-class>com.zx.javaweb.ParentdoTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>name</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>sondoTag</name>
<tag-class>com.zx.javaweb.SondoTag</tag-class>
<body-content>empty</body-content>
</tag>
ParentdoTag
public class ParentdoTag extends SimpleTagSupport {
private String name;
public void setName(String name) {
this.name=name;
}
@Override
public void doTag() throws JspException, IOException {
System.out.println("父标签的标签处理器类 name: " + name);
getJspBody().invoke(null);
}
}
SondoTag
public class SondoTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
//1. 得到父标签的引用
JspTag parent = getParent();
//2. 获取父标签的 name 属性
ParentdoTag parentTag = (ParentdoTag) parent;
String name = parentTag.setName();
//3. 把 name 值打印到 JSP 页面上.
getJspContext().getOut().print("子标签输出name: " + name);
}
}
jsp调用
<zx:parentdoTag value="zx">
<zx:sondoTag />
</zx:parentdoTag>
6.自定义EL函数
三步:
1.定义静态方法
public static String concat(String str1, String str2){
return str1 + str2;
}
2.编写tld
<!-- 描述 EL 的自定义函数 -->
<function>
<name>concat</name>
<function-class>com.zx.javaweb.MyELFunction</function-class>
<function-signature>java.lang.String concat(java.lang.String, java.lang.String)</function-signature>
</function>
3.jsp页面导入和使用
<%@ taglib prefix="zx" uri="http://zx.com/myTag/core" %>
<!-- 测试自定义的 EL 函数 -->
${zx:concat(param.name1, param.name2)}
总结一下自定义标签的使用步骤:
1.创建自定义标签类实现接口SimpleTag(一般extends SimpleTagSupport 重写doTag方法)
2.在WEB/INF文件下新建 .tld 配置文件,配置它,具体操作看上面。
3.设置属性,在tld中设置属性,在实现类中实现相应的getter和setter方法,既可以在doTag中调用,关键是doTag和PageContext
(输出到浏览器用JSPWriter out =pageContext.getOut()方法,out.print()),这是由于该类中可以用pageContext调用任何隐含变量。
4.得到标签内容JspFragment bodyContent = getJspBody();,在tld中设置标签体< body-content>scriptless< /body-content>
5.在jsp页面调用,通过标签的shortname+属性值调用,实现相应想要的输出。< zx:printUpper value=”10”>xiaohuzixiansheng< /zx:printUpper>
所谓自定义标签就是自定义内容(body)和属性(value)把它放到doTag中去运行,然后返回一个运行结果给jsp页面。
九、JSTL
JSTL,即JSP标准标签函数库。
1.JSTL提供的标签库主要分为五大类
注意:使用JSLTL时需要jar包
jstl.jar
standard.jar
2.输出赋值移除标签
①.c:out
< c:out value=”${username}/>
②.c:set
③.c:remove
<h4>c:remove: 移除指定域对象的指定属性值</h4>
<c:set value="1997-09-1" var="date" scope="session"></c:set>
date: ${sessionScope.date }
<br><br>
<c:remove var="date" scope="session"/>
date: --${sessionScope.date }--
<br><br>
<h4>c:set: 可以为域赋属性值, 其中 value 属性支持 EL 表达式; 还可以为域对象中的 JavaBean 的属性赋值, target, value都支持 EL 表达式</h4>
<c:set var="name" value="ATGUIGU" scope="page"></c:set>
<%--
pageContext.setAttribute("name", "atguigu");
--%>
name: ${pageScope.name }
<br><br>
<c:set var="subject" value="${param.subject }" scope="session"></c:set>
subject: ${sessionScope.subject }
<br><br>
<%
Customer cust = new Customer();
cust.setId(1001);
request.setAttribute("cust", cust);
%>
ID: ${requestScope.cust.id }
<c:set target="${requestScope.cust }" property="id" value="${param.id }"></c:set>
<br>
ID: ${requestScope.cust.id }
<br><br>
<h4>c:out: 可以对特殊字符进行转换. </h4>
<%
request.setAttribute("book", "<<Java>>");
%>
book: ${requestScope.book }
<br><br>
book: <c:out value="${requestScope.book }" default="booktitle"></c:out>
3.控制流程
①.c:if
< c:if>就和平时用的if一样
<c:set value="20" var="age" scope="request"></c:set>
<h4>c:if: 不能实现 else 操作, 但可以把结果储存起来。 </h4>
<c:if test="${requestScope.age > 18 }">成年了!</c:if>
<br><br>
<c:if test="${param.age > 18 }" var="isAdult" scope="request"></c:if>
isAdult: <c:out value="${requestScope.isAdult }"></c:out>
②.c:choose
③.c:when
④.c:otherwise
<h4>
c:choose, c:when, c:otherwise: 可以实现 if...else if...else if...else 的效果. 但较为麻烦
其中: c:choose 以 c:when, c:otherwise 的父标签出现.
c:when, c:otherwise 不能脱离 c:choose 单独使用.
c:otherwise 必须在 c:when 之后使用。
</h4>
<c:choose>
<c:when test="${param.age > 60 }">
老年
</c:when>
<c:when test="${param.age > 35 }">
中年
</c:when>
<c:when test="${param.age > 18 }">
青年
</c:when>
<c:otherwise>
未成年.
</c:otherwise>
</c:choose>
相当于
if{
}else if{
}else if{
}else{
}
4.迭代操作
迭代主要包含两个标签< c:forEach>和< c:forTokens>。
①.< c:forEach>
<h4>c:forEach: 可以对数组, Collection, Map 进行遍历, begin(对于集合 begin 从 0 开始算), end, step</h4>
<c:forEach begin="1" end="10" step="3" var="i">
${ i} --
</c:forEach>
<br><br>
<%
List<Customer> custs = new ArrayList<Customer>();
custs.add(new Customer(1, "AAA")); //index: 0
custs.add(new Customer(2, "BBB")); //1
custs.add(new Customer(3, "CCC"));
custs.add(new Customer(4, "DDD")); //3
custs.add(new Customer(5, "EEE"));
custs.add(new Customer(6, "FFF")); //5
request.setAttribute("custs", custs);
%>
<br><br>
<!-- 遍历 Collection, 遍历数组同 Collection -->
<c:forEach items="${requestScope.custs }" var="cust"
varStatus="status">
${status.index}, ${status.count}, ${status.first}, ${status.last}: ${cust.id }: ${cust.name }<br>
</c:forEach>
<!-- 遍历 Map -->
<%
Map<String, Customer> custMap = new HashMap<String, Customer>();
custMap.put("a", new Customer(1, "AAA"));
custMap.put("b", new Customer(2, "BBB"));
custMap.put("c", new Customer(3, "CCC"));
custMap.put("d", new Customer(4, "DDD"));
custMap.put("e", new Customer(5, "EEE"));
custMap.put("f", new Customer(6, "FFF"));
request.setAttribute("custMap", custMap);
%>
<br><br>
<c:forEach items="${requestScope.custMap }" var="cust">
${cust.key } - ${cust.value.id } - ${cust.value.name }<br>
</c:forEach>
<%
String [] names = new String[]{"A", "B", "C"};
request.setAttribute("names", names);
%>
<br><br>
<c:forEach var="name" items="${names }">${name }-</c:forEach>
②.< c:forTokens>
//其中的delims就是以什么分隔,例子中以,号分隔
<h4>c:forTokens: 处理字符串的, 类似于 String 的 split() 方法</h4>
<c:set value="a,b,c.d.e.f;g;h;j" var="test" scope="request"></c:set>
<c:forTokens items="${requestScope.test }" delims="." var="s">
${s }<br>
</c:forTokens>
5.URL相关操作
JSTL包含三个与URL相关的标签:< c:import>、< c:redirect>、< c:url>。它们的主要功能是:用来将其他文件的内容包含起来、网页的重定向、还有url的产生。
①.< c:import>
<h4>c:import 可以包含任何页面到当前页面</h4>
<c:import url="http://www.baidu.com"></c:import>
②.< c:redirect>
<h4>
c:redirect 使当前 JSP 页面重定向到指定的页面. 使当前 JSP 转发到指定页面可以使用
<%--
<jsp:forward page="/test.jsp"></jsp:forward>
--%>
/ 代表的是当前 WEB 应用的根目录.
response.sendRedirect("/test.jsp") / 代表 WEB 站点的根目录
</h4>
<%--
<c:redirect url="http://www.atguigu.com"></c:redirect>
<c:redirect url="/test.jsp"></c:redirect>
--%>
③.< c:url>
<h4>
c:url 产生一个 url 地址. 可以根据 Cookie 是否可用来智能进行 URL 重写, 对 GET 请求的参数进行编码
可以把产生的 URL 存储在域对象的属性中.
还可以使用 c:param 为 URL 添加参数. c:url 会对参数进行自动的转码.
value 中的 / 代表的是当前 WEB 应用的根目录.
</h4>
<c:url value="/test.jsp" var="testurl" scope="page">
<c:param name="name" value="尚硅谷"></c:param>
</c:url>
url: ${testurl }
注意:由于标签要交给servlet处理,所以“/”代表当前WEB应用的根目录。而类似于response.sendRedirect(“/test.jsp”) / 代表 WEB 站点的根目录。
十、Filter过滤器
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 接口:
1.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>
2.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> 配置的顺序有关, 靠前的先被调用.
3.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>Filter</filter-name>
<url-pattern>/test.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
2.Filter简介
3.Filter过滤过程
4.Filter的基本工作原理
5.Filter接口
6.FilterChain接口
7.FilterConfig接口
8.过滤器的部署
9.创建HttpFilter实现类来简化Filter创建类
①.为什么用抽象类:实现接口如果不实现它的所有的方法就要声明为抽象类。
②.为什么声明为抽象方法:父类不提供实现,由继承的子类实现。
/**
* 自定义的 HttpFilter, 实现自 Filter 接口
*
*/
public abstract class HttpFilter implements Filter {
/**
* 用于保存 FilterConfig 对象.
*/
private FilterConfig filterConfig;
/**
* 不建议子类直接覆盖. 若直接覆盖, 将可能会导致 filterConfig 成员变量初始化失败
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
init();
}
/**
* 供子类继承的初始化方法. 可以通过 getFilterConfig() 获取 FilterConfig 对象.
*/
protected void init() {}
/**
* 直接返回 init(ServletConfig) 的 FilterConfig 对象
*/
public FilterConfig getFilterConfig() {
return filterConfig;
}
/**
* 原生的 doFilter 方法, 在方法内部把 ServletRequest 和 ServletResponse
* 转为了 HttpServletRequest 和 HttpServletResponse, 并调用了
* doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
*
* 若编写 Filter 的过滤方法不建议直接继承该方法. 而建议继承
* doFilter(HttpServletRequest request, HttpServletResponse response,
* FilterChain filterChain) 方法
*/
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
doFilter(request, response, chain);
}
/**
* 抽象方法, 为 Http 请求定制. 必须实现的方法.
* @param request
* @param response
* @param filterChain
* @throws IOException
* @throws ServletException
*/
public abstract void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException;
/**
* 空的 destroy 方法。
*/
@Override
public void destroy() {}
}
10.理解多个Filter的执行过程
1,2,3分别代表三个代码块,1,2是两个过滤器中的doFilter方法,3是jsp页面调用。
它们3个就组成了一个chain:1->2->3
至于调用过程就是:先1,通过方法chain.doFilter会调用下个链,下个链会直接调用jsp。调用完jsp后,在倒着调用没有执行完的方法。
也就是说链是会直接连着执行的。
11.jsp加载错误页面的两种方式
①.在页面上面加入标签
<%@ page errorPage=”error.jsp” %>
②.利用过滤器
编写web.xml
< error-page>
< exception-type>java.lang.ArithmeticException< /exception-type>
< location>/error.jsp< /location>
< /error-page>
< filter-mapping>
< filter-name>Filter< /filter-name>
< url-pattern>/error.jsp< /url-pattern>
< dispatcher>ERROR< /dispatcher>
< /filter-mapping>
12.禁用浏览器缓存的过滤器
就是在doFilter中加入三行代码(此时用的是HttpFilter自己写的),用来实时的访问最新的页面:
response.setDateHeader("Expries",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
13.字符编码过滤器
通过配置参数encoding指明使用何种字符编码,以处理Html Form请求参数的中文问题。防止每段代码都写setencoding。这样更简便。
web.xml
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
encodingFilter
public class EncodingFilter extends HttpFilter{
private String encoding;
@Override
protected void init() {
encoding = getFilterConfig().getServletContext().getInitParameter("encoding");
}
@Override
public void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
request.setCharacterEncoding(encoding);
filterChain.doFilter(request, response);
}
}
以后只需要改配置文件就可以在访问任何页面的时候自动指定编码集。init用于从xml中获取全局变量encoding的值,然后用于doFilter中。
14.检测用户是否登陆的过滤器
web.xml
<!-- 用户信息放入到 session 中的键的名字 -->
<context-param>
<param-name>userSessionKey</param-name>
<param-value>USERSESSIONKEY</param-value>
</context-param>
<!-- 若未登录, 需重定向的页面 -->
<context-param>
<param-name>rediretPage</param-name>
<param-value>/login/login.jsp</param-value>
</context-param>
<!-- 不需要拦截(或检查)的 URL 列表 -->
<context-param>
<param-name>uncheckedUrls</param-name>
<param-value>/login/a.jsp,/login/list.jsp,/login/login.jsp,/login/doLogin.jsp</param-value>
</context-param>
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.zx.javaweb.login.LoginFilter</filter-class>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/login/*</url-pattern>
</filter-mapping>
Filter
public class LoginFilter extends HttpFilter{
//1. 从 web.xml 文件中获取 sessionKey, redirectUrl, uncheckedUrls
private String sessionKey;
private String redirectUrl;
private String unchekcedUrls;
@Override
protected void init() {
ServletContext servletContext = getFilterConfig().getServletContext();
sessionKey = servletContext.getInitParameter("userSessionKey");
redirectUrl = servletContext.getInitParameter("rediretPage");
///login/a.jsp,/login/list.jsp,/login/login.jsp,/login/doLogin.jsp
unchekcedUrls = servletContext.getInitParameter("uncheckedUrls");
}
@Override
public void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
//1. 获取请求的 servletPath
// /login/b.jsp
String servletPath = request.getServletPath();
//2. 检查 1 获取的 servletPath 是否为不需要检查的 URL 中的一个, 若是, 则直接放行. 方法结束
List<String> urls = Arrays.asList(unchekcedUrls.split(","));
if(urls.contains(servletPath)){
filterChain.doFilter(request, response);
return;
}
//3. 从 session 中获取 sessionKey 对应的值, 若值不存在, 则重定向到 redirectUrl
Object user = request.getSession().getAttribute(sessionKey);
if(user == null){
response.sendRedirect(request.getContextPath() + redirectUrl);
return;
}
//4. 若存在, 则放行, 允许访问.
filterChain.doFilter(request, response);
}
}
通过判断不可访问的页面,以及配置访问的条件来实现用户的登陆检查。
15.Filter实现权限管理
①.可根据配置文件读取权限,来判断可执行的操作。
16.HttpServletRequestWrapper&HttpServletResponseWrapper
通过继承HttpServletRequestWrapper&HttpServletResponseWrapper,来实现对原生request方法的修改与增强,此时在页面调用的request的方法都可以是重写增强的。该方法用来防止原生的方法没有的功能,我们可以去实现新的功能去调用。
注意:除了你自己重写的方法,其他的方法还是自动调用原生的。
举个例子:去除不雅文字(这里也用了Filter)
web.xml
<filter>
<display-name>ContentFilter</display-name>
<filter-name>ContentFilter</filter-name>
<filter-class>com.atguigu.javaweb.ContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ContentFilter</filter-name>
<url-pattern>/bbs.jsp</url-pattern>
</filter-mapping>
Filter类
public class ContentFilter extends HttpFilter{
public void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
//1. 获取请求 content 参数的值
String content = request.getParameter("content");
System.out.println(request);
HttpServletRequest req = new MyHttpServletRequest(request);
//2. 把其中 fuck, shit 等字符串替换换为 ****
//SerletRequest, HttpServletRequest 中并没有提供诸如 setParameter(paramName, paramValue)
//类似于这样的方法.
//目标: 改变 HttpServletRequest 的 getParameter(String) 方法的行为: 若该方法的返回值中
//包含 " fuck ", 则替换为 " **** "
//1. 若对于一个类的方法不满意, 需要进行重写, 最常见的方式是, 继承父类, 重写方法.
//若实现则需要继承 org.apache.catalina.connector.RequestFacade, 而这仅是 Tomcat
//服务器的实现, 若更换服务器, 该方案将无法使用. ×.
//2. 直接写一个 HttpServletRequest 接口的实现类: 无法实现 其中方法. ×
//3. 装饰目前的 HttpServletRequest 对象: 装饰其 getParameter 方法, 而其他方法还和其实现相同.
//创建一个类, 该类实现 HttpServletRequest 接口, 把当前 doFilter 中的 request 传入到该类中, 作为
//其成员变量, 使用该成员变量去实现接口的全部方法.
//3. 转到目标页面
filterChain.doFilter(req, response);
}
}
包装类
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;
}
}
content.jsp
<form action="bbs.jsp" method="post">
content: <textarea rows="5" cols="21" name="content"></textarea>
<input type="submit" value="Submit"/>
</form>
bbs.jsp
content: ${param.content }
上面的这个方法除了getParamter方法,其他的方法还是用原生的。因为只重写了一个。相类似 Servlet API 也提供了一个 HttpServletResponseWrapper 类来包装原始的 response 对象
十一、Servlet监听器
1.介绍
2.监听器分类
①.监听域对象自身的创建和销毁的事件监听器
- ServletContextListener接口
- HttpSessionListener接口
- ServletRequsetListener接口
②.监听域对象中的属性的增加和删减的事件监听器
- ServletContextAttributeListener
- HttpSessionAttributeListener
- ServletRequestAttributeListener
attributeAdded方法
说明这三个接口
1).监听 ServletContext, HttpSession, ServletRequest 中添加属性, 替换属性, 移除属性的事件监听器。
2).以 ServletRequestAttributeListener 为例://实现ServletRequestAttrinbute接口 //添加属性时被调用 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(): 获取属性的值5). 不要忘了配置web.xml
< listener>
< listener-class>com.zx.javaweb.test.TestAttributeListener< /listener-class>
< /listener>
注意:这些接口中的方法都是在相应的属性发生变化时会自动调用相应的方法,其中getName和getValue可以获得改变的属性名和属性值。
③.监听绑定到HttpSession域中的某个对象的状态的事件监听器
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).该监听器较少使用
HttpSessionActivationListener
1). 监听实现了该接口和 Serializable 接口的 Java 类的对象随 session 钝化和活化事件//在活化之后被调用. public void sessionDidActivate(HttpSessionEvent se) //在钝化之前被调用 public void sessionWillPassivate(HttpSessionEvent se)
2).该监听器不需要在 web.xml 文件中进行配置.
3).HttpSessionEvent:
getSession()
4). 该监听器较少被使用.
3.创建Servlet监听器
4.监听器创建例子及对其域对象生命周期的理解
①.以ServletContextListener为例
- 监听 ServletContext 对象被创建或销毁的 Servlet 监听器
怎么用?
1).创建一个实现了 ServletContextListener 的类, 并且实现其中的两个方法public class HelloServletContextListner implements ServletContextListener{}
2).在 web.xml 文件中配置 Listener
< listener>
< listener-class>com.zx.javaweb.test.HelloServletContextListner < /listener-class>
< /listener>
相关的api
// SerlvetContext 对象被创建(即, 当前 WEB 应用被加载时)的时候, Servlet 容器调用该方法.
public void contextInitialized(ServletContextEvent sce)
// SerlvetContext 对象被销毁之前(即, 当前 WEB 应用被卸载时)的时候, Servlet 容器调用该方法.
public void contextDestroyed(ServletContextEvent sce)
ServletContextEvent 中的: getServletContext() 获取 ServletContext
②.监听器的作用
ServletContextListener 是最常用的 Listener, 可以在当前 WEB 应用被加载时对当前 WEB 应用的相关资源进行初始化操作:创建数据库连接池, 创建 Spring 的 IOC 容器, 读取当前 WEB 应用的初始化参数等等
(2)对域对象生命周期的理解
首先说明一下:ServletRequestListener & HttpSessionListener和 ServletContextListener 类似。
利用 ServletRequestListener、HttpSessionListener 以及 ServletContextListener 可以把 request, session
及 application 的生命周期进一步的做一了解.
1.request: 是一个请求, 当一个响应返回时, 即被销毁. 当发送一个请求时被创建. 注意, 请求转发的过程是一个 request 对象.重定向是两个请求。
//jsp转发
<jsp:forward page="/test.jsp"></jsp:forward>
//jsp重定向
String site = new String("/test.jsp");
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site);
//Servlet转发
request.getRequestDispatcher("/test.jsp").forward(request, response);
//Servlet重定向
response.sendRedirect("test.jsp");
2.session: 当第一次访问 WEB 应用的一个 JSP 或 Servlet 时, 且该 JSP 或 Servlet 中还需要创建 session 对象. 此时服务器会创建一个 session 对象。
session 销毁: session 过期、或者直接调用 session 的 invalidate 方法,、或者当前 web 应用被卸载
注意:session 可以被持久化。关闭浏览器, 并不意味着 session 被销毁,。
还可以通过 sessionid 找到服务器中的 session 对象。
JSESSIONID=F4119DE0FC93ED38E8EC83B24CFA3B81
h ttp://localhost:8080/test/session.jsp;jsessionid=F41
19DE0FC93ED38E8EC83B24CFA3B81
3.application: 贯穿于当前的 WEB 应用的生命周期.。当前 WEB 应用被加载时创建 application 对象, 当前 WEB 应用被卸载时销毁 application 对象。
十二、文件的上传与下载
1. 进行文件上传时, 表单需要做的准备
1). 请求方式为 POST:
< 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”属性后,表示表单以二进制传输数据。
5).在编写Servlet代码时
1.不能再使用 request.getParameter() 等方式获取请求信息. 获取不到, 因为请求的编码方式已经改为 multipart/form-data, 以二进制的方式来提交请求信息。
所以此时就需要要到文件的输入输出流来获取。可以这样做但不建议这样做。
2.建议做法:具体使用 commons-fileupload 组件来完成文件的上传操作。
commons-fileupload-x.x.x.jar
commons-io-x.x.x.jar
文件输入输出流的Servlet做法:
InputStream in=request.getInputStream();
Reader reader=new InputStreamReader(in);
BufferedReader buf=new BufferedReader(reader);
String str=null;
while((str=buf.readLine())!=null){
System.out.println(str);
}
2.使用jar包做法
1).导入jar包
commons-fileupload-xxx.jar
commons-io-xxx.jar
2).基本思想
1.commons-fileupload 可以解析请求, 得到一个 FileItem 对象组成的 List
2.commons-fileupload 把所有的请求信息都解析为 FileItem 对象, 无论是一个一般的文本域还是一个文件域.
3.可以调用 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();
}
3).如何得到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);
实现代码
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 得到 FileItem 的集合 items // Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory(); // FileCleaningTracker fileCleaningTracker = // FileCleanerCleanup.getFileCleaningTracker(getServletContext()); // factory.setFileCleaningTracker(fileCleaningTracker); // Set factory constraints factory.setSizeThreshold(1024 * 500); File tempDirectory = new File("d:\\tempDirectory"); factory.setRepository(tempDirectory); // Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory); // Set overall request size constraint upload.setSizeMax(1024 * 1024 * 5); // Parse the request try { List< FileItem> /* FileItem */items = upload.parseRequest(request); // 2. 遍历 items: for (FileItem item : items) { // 若是一个一般的表单域, 打印信息 if (item.isFormField()) { String name = item.getFieldName(); String value = item.getString(); System.out.println(name + ": " + value);
}
// 若是文件域则把文件保存到 d:\\files 目录下.
else {
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
long sizeInBytes = item.getSize();
System.out.println(fieldName);
System.out.println(fileName);
System.out.println(contentType);
System.out.println(sizeInBytes);
InputStream in = item.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
fileName = "d:\\files\\" + fileName;
System.out.println(fileName);
OutputStream out = new FileOutputStream(fileName);
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
in.close();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
3.约束文件上传的可配置性
我们可以在监听器中完成配置属性的初始化,然后在相应的地方调用(不止在这,其他需要配置文件的地方也可以这样)
1).配置文件:upload.properties
exts=pptx,docx,doc
file.max.size=1048576
total.file.max.size=5242880
2).单例模式进行反射对象
public class FileUploadAppProperties {
private Map<String, String> properties = new HashMap<>();
private FileUploadAppProperties(){}
private static FileUploadAppProperties instance = new FileUploadAppProperties();
public static FileUploadAppProperties getInstance() {
return instance;
}
public void addProperty(String propertyName, String propertyValue){
properties.put(propertyName, propertyValue);
}
public String getProperty(String propertyName){
return properties.get(propertyName);
}
}
3).监听器加载配置文件
public class FileUploadAppListener implements ServletContextListener {
public FileUploadAppListener() {
// TODO Auto-generated constructor stub
}
public void contextInitialized(ServletContextEvent arg0) {
InputStream in = getClass().getClassLoader().getResourceAsStream("/upload.properties");
Properties properties = new Properties();
try {
properties.load(in);
for(Map.Entry<Object, Object> prop: properties.entrySet()){
String propertyName = (String) prop.getKey();
String propertyValue = (String) prop.getValue();
//赋值 FileUploadAppProperties.getInstance().addProperty(propertyName, propertyValue);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
}
}
4).实现调用
String exts = FileUploadAppProperties.getInstance().getProperty("exts");
4.文件的下载
1).在Servlet中设置contentType响应头
response.setContentType(“application/x-msdownload”);
它的作用是通知浏览器该操作是文件下载
2).设置 Content-Disposition 响应头
response.setHeader(“Content-Disposition”, “attachment;filename=any”);
通知浏览器不再有浏览器来自行处理(或打开)要下载的文件, 而由用户手工完成
3).具体文件可以调用response.getOutputStream(),以IO流的方式发送给客户端
OutputStream out = response.getOutputStream();
String FileName = "C:\\Users\\test\\1.txt";
InputStream in = new FileInputStream(FileName);
byte [] buffer = new byte[1024];
int len = 0;
while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
//注意这时不关闭out,因为要向用户写
in.close();
4).看个例子
Servlet
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/x-msdownload");
//以什么样的名称下载
String fileName = "文件下载.txt";
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
OutputStream out = response.getOutputStream();
//要下载的文件路径,以文件下载.txt去下1.txt
String FileName = "C:\\Users\\test\\1.txt";
InputStream in = new FileInputStream(FileName);
byte [] buffer = new byte[1024];
int len = 0;
while((len = in.read(buffer)) != -1){
out.write(buffer, 0, len);
}
in.close();
out.close();
}
}
注意:
1.此时的Servlet要用doGet方法
2.里面的文件路径以及下载名我们可以动态的指定
3.下载的位置由于告诉了浏览器这是下载,所以会有另存为的这个选项
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。 是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。 多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
它是一个互联网标准,扩展了电子邮件标准,使其能够支持:非ASCII字符文本;非文本格式附件(二进制、声音、图像等);由多部分(multiple parts)组成的消息体;包含非ASCII字符的头信息(Header information)。
十三、国际化
1.Local类
Locale: 是Java 中表示国家或地区的类.,JDK 中提供了很多常量。也可以通过 Locale(languageCode, countryCode) 的方式来创建 在 WEB 应用中可以通过 request.getLocale() 方法来获取。
(1)示例代码:
//local使用的两种方式
public void testLocale(){
Locale locale = Locale.CHINA;
System.out.println(locale.getDisplayCountry());
System.out.println(locale.getLanguage());
locale = new Locale("en", "US");
System.out.println(locale.getDisplayCountry());
System.out.println(locale.getLanguage());
}
2.DateFormat接口
DateFormat: 格式化日期的工具类。
DateFormat 本身是一个抽象类。
(1)方法介绍
①.若只希望通过 DateFormat 把一个 Date 对象转为一个字符串, 则可以通过 DateFormat 的工厂方法来获取 DateFormat 对象。
②. 可以获取只格式化 Date 的 DateFormat 对象
getDateInstance(int style, Locale aLocale)
③.可以获取只格式化 Time 的 DateFormat 对象
getTimeInstance(int style, Locale aLocale)
④. 可以获取既格式化 Date, 也格式化 Time 的 DateFormat 对象
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
⑤. 其中 style 可以取值为: DateFormat 的常量: SHORT, MEDIUM, LONG, FULL. Locale 则为代表国家地区的 Locale 对象
⑥.通过 DateFormat 的 format 方法来格式化个 Date 对象到字符串
⑦.若有一个字符串, 如何解析为一个 Date 对象呢 ?
- 先创建 DateFormat 对象: 创建 DateFormat 的子类 SimpleDateFormat 对象,SimpleDateFormat(String pattern)。 其中 pattern 为日期, 时间的格式, 例如: yyyy-MM-dd hh:mm:ss
- 调用 DateFormat 的 parse 方法来解析字符串到 Date 对象
(2)代码示例:
public void testDateFormat(){
//设置地点us
Locale locale = Locale.US;
//新建日期对象
Date date = new Date();
System.out.println(date);
//获取 DateFormat 对象
DateFormat dateFormat =
DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, locale);
String str = dateFormat.format(date);
System.out.println(str);
}
//将字符串转化为日期
public void testDateFormat2() throws ParseException{
String str = "1990-12-12 12:12:12";
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = dateFormat.parse(str);
System.out.println(date);
}
3.NumberFormat类
NumberFormat: 格式化数字到数字字符串, 或货币字符串的工具类。
(1)方法介绍
①. 通过工厂方法获取 NumberFormat 对象
1.NumberFormat.getNumberInstance(locale); //仅格式化为数字的字符串
2.NumberFormat.getCurrencyInstance(locale); //格式为货币的字符串
②. 通过 format 方法来进行格式化
③.通过 parse 方法把一个字符串解析为一个 Number 类型
(2)代码示例:
public void testNumberFormat() throws ParseException{
double d = 123456789.123d;
Locale locale = Locale.FRANCE;
//获取想要格式化的国家
NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
String str = numberFormat.format(d);
System.out.println(str);
NumberFormat numberFormat2 = NumberFormat.getCurrencyInstance(locale);
str = numberFormat2.format(d);
System.out.println(str);
str = "123 456 789,123";
d = (Double) numberFormat.parse(str);
System.out.println(d);
str = "123 456 789,12 €";
d = (Double) numberFormat2.parse(str);
System.out.println(d);
}
4.MessageFormat类
MessageFormat: 可以格式化模式字符串。
模式字符串: 带占位符的字符串: “Date: {0}, Salary: {1}”
(1)方法介绍
①.可以通过 format 方法会模式字符串进行格式化 。
MessageFormat.format(pattern, arguements … args)
(2)代码示例:
public void testMessageFormat(){
String str = "Date: {0}, Salary: {1}";
Locale locale = Locale.CHINA;
Date date = new Date();
double sal = 12345.12;
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
String dateStr = dateFormat.format(date);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
String salStr = numberFormat.format(sal);
//方式一
String result = MessageFormat.format(str, dateStr, salStr);
System.out.println(result);
//方式二
String result1 = MessageFormat.format(str, date, sal);
System.out.println(result1);
}
5.ResourceBundle类
ResourceBundle: 资源包类。
(1)方法介绍
①. 在类路径下需要有对应的资源文件: baseName.properties. 其中 baseName 是基名。
②. 可以使用 基名_ 语言代码_ 国家代码.properties 来添加不同国家或地区的资源文件. i18n_ zh_CN.properties。
③.要求所有基名相同的资源文件的 key 必须完全一致。
④. 可以使用 native2ascii 命令来得到 汉字 对一个的 asc 码. Eclipse 内置了工具。
⑤. 可以调用 ResourceBundle 的 getBundle(基名, Locale 实例) 获取获取 ResourceBundle 对象。
⑥.可以调用 ResourceBundle 的 getString(key) 来获取资源文件的 value 字符串的值。
⑦.结合 DateFormat, NumberFormat, MessageFormat 即可实现国际化。
(2)代码示例:
public void testResourceBundle(){
Locale locale = Locale.CHINA;
ResourceBundle resourceBundle = ResourceBundle.getBundle("i18n", locale);
System.out.println(resourceBundle.getString("date"));
System.out.println(resourceBundle.getString("salary"));
String dateLabel = resourceBundle.getString("date");
String salLabel = resourceBundle.getString("salary");
String str = "{0}:{1}, {2}:{3}";
Date date = new Date();
double sal = 12345.12;
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
String dateStr = dateFormat.format(date);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
String salStr = numberFormat.format(sal);
String result = MessageFormat.format(str, dateLabel, dateStr, salLabel, salStr);
System.out.println(result);
}
i18n.properties
date=Date
salary=Salary
i18n _ zh _ CN.properties
date=\u65E5\u671F
salary=\u5DE5\u8D4
i18n _ en _ US.properties
date=Date
salary=Salary
根据不同的需要调用相应的配置文件