0%

最全的javaweb知识全集

最全的javaweb知识全集

Servlet是java定义的Servlet标准接口

servlet容器负责Servlet和客户的通信以及调用Servlet的方法

1
2
3
4
5
6
7
8
9
10
11
public interface Servlet {
void init(ServletConfig var1) throws ServletException;

ServletConfig getServletConfig();

void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

String getServletInfo();

void destroy();
}

servlet生命周期

生命周期相关方法,servlet生命周期中的方法全是由servlet容器来调用的

  • 构造器
  • init方法
  • service方法
  • destory方法

init方法

init方法在第一次创建servlet时被调用,在后续每次请求时都不会被调用。

当用户调用servlet的时候,该servlet的一个实例就会被创建,并且为每一个用户产生一个新的线程,init()用于进行一些初始化数据的加载和处理,这些数据会被用于servlet的整个生命周期

1
void init(ServletConfig var1) throws ServletException;

该方法是由servlet容器调用的

重写init方法

GenericServlet实现了Servlet和ServletConfig,是一个抽象类,并对init(ServletConfig var1)方法进行了一层封装,有一个ServletConfig成员变量,在init()方法中进行了初始化,使得可以直接在GenericServlet中直接使用ServletConfig方法

而我们平时写Servlet大多是继承于HttpServlet类的,在对init方法进行重写时,重写不带参的init()方法即可

1
2
3
4
5
6
7
8
9
//GenericServlet类

public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}

public void init() throws ServletException {
}

该方法由GenericServlet的调用,如果需要使用到ServletConfig则调用getServletConfig()方法来获取

service方法

service方法是实际处理请求的方法,servlet容器调用service方法来处理请求,并将响应写会到客户端,每次服务器接收到一个新的servlet请求时,服务器会产生一个新的线程来调用服务

1
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
处理请求逻辑

HttpServlet继承了GenericServlet,重写了service方法,将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse,并根据不同的请求方式进行分发,doGet/doPost/doHead等

servlet可以在任何协议下访问 ,写的Servlet必须实现Servlet接口,在http协议下可以使用HttpServlet ,用来给Web访问的

在HttpServlet类中对于service()方法进行了处理,根据请求方式的不同,将请求分发到了不同的方法,而我们一般情况下写Servlet也是继承自HttpServlet的,所以在写请求处理逻辑时,只需要重写doGet()和doPost()方法即可

1
2
3
4
5
6
7
8
9
10
11
// HttpServlet类

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
GET方法

GET方法时浏览器向web服务器传递信息的默认方法,会在地址栏上产生很长的字符串,且GET方法有大小限制,请求字符串中最多只能有1024个字符

POST方法

POST方法不将请求信息放到地址中

destroy方法

destory()方法只在servlet生命周期结束时被调用一次。可以在destory()方法中进行一些资源的清理,如关闭数据库连接、停止后台线程等

ServletContext接口

  • 该对象代表当前WEB应用,可以获取到web应用的信息
  • 可以使用ServletConfig的getServletContext()获取到ServletContext
  • 可以获取web应用的初始化参数,这是全局的方法,在web.xml中配置
  • 获取web应用某个文件的绝对路径(在服务器上的路径,不是部署前的方法) getRealPath
  • 获取当前应用的名称 getContextPath
  • 获取当前web应用的某一个文件对应的输入流 getResourceAsStream() path是相对于当前web应用的根目录

servlet容器

在上面介绍servlet生命周期时,多次提到了servlet容器,其实在设计Servlet时,J2EE jdk只是提供了一个标准,在javax.servlet包以及子包下,而真正的实现是由servlet容器来进行实现的,如tomcat是在servlet-api.jar中实现的

servlet容器
1、可以创建servlet,并调用servlet的相关生命周期方法
2、JSP、Filter、Listener

下面将以tomcat作为servlet容器为例介绍web应用

web应用

WEB-INF

静态页面不要放在WEB-INF下,WEB-INF是给tomcat用的

WEB-INF 对于web应用的描述

  • web.xml 必须符合J2EE标准
  • lib 放jar包

tomcat配置项目位置

tomcat映射配置任意目录的项目
在conf下新建catalina文件夹,新建localhost文件夹,
在其中新建一个xml文件,文件名是url根路径
path当前没用 docBase为项目路径(编译之后的项目)

1
<Context path="" docBase="" reloadable="true">

web.xml配置servlet

load-on-startup参数指定servlet创建的时机 若为负数,则在第一次请求时被创建,若为0或者正数,在web应用被Servlet容器加载时创建实例,值越小越早被启动

init方法有入参 ServletConfig
配置ServletConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 注意servlet和servlet-mapping都是成对出现的 -->
<servlet>
<servlet-name>HW</servlet-name>
<servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
<!-- 配置servlet初始化init时中ServletConfig参数-->
<init-param>
<param-name>name</param-name>
<param-value>john</param-value>
</init-param>
<!-- servlet加载时机 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 对应servlet标签中的servlet-name值 -->
<servlet-name>HW</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

​ 获取ServletConfig值

1
2
3
4
// 获取指定的属性
config.getInitParameter("name")
// 获取所有的属性
config.getInitParameters()

请求转发和请求重定向

1
2
3
4
// 请求转发
request.getRequestDispatcher(url).forward(req,resp)
// 请求重定向
response.sendRedirect(url)

本质区别:请求转发只向服务器发起一次请求,重定向发起两次请求
1、
请求转发:地址是初次发出请求的地址
重定向:地址栏是最后响应的地址

2、
请求转发:在最终的Servlet中,request对象和中转的那个request是同一个对象
重定向:在最终的Servlet中,request对象和中转的那个request不是同一个对象

3、
请求转发:只能转发到当前web应用
请求重定向:可以重定向到任何资源

4、
请求转发:/代表当前web应用的根目录
请求重定向:/代表当前web站点的根目录,要使用request.getContextPath()再加上路径

会话管理

HTTP是无状态的协议,每次客户端访问web页面时,都会打开一个单独的连接到web服务器,服务器不会自动保存客户端请求的任何记录,需要使用cookie和session来将一系列的请求和响应关联起来,维持客户端和服务器之间的会话

Cookie是存储在计算机上的文本文件,用于追踪各种信息,记录在客户端,浏览器可以禁用cookie,可以删除cookie,
在服务器产生,作为响应头的一部分返回给客户端,浏览器收到cookie后,把cookie的键值写入到文本中,发送请求时浏览器会把cookie信息与请求发送给服务器

cookie原理

底层原理:WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie信息发送给浏览器,浏览器则通过在HTTP请求信息中增加Cookie请求头字段将Cookie回传给WEB服务器

操作cookie
创建cookie
1
2
3
// 第一个参数是cookie的键,第二个参数是cookie的值
Cookie cookie = new Cookie("name","value")
resp.addCookie(cookie)
获取cookie
1
Cookie[] cookies = req.getCookies();
设置cookie的一些方法
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
// 描述cookie的注释
public void setComment(String purpose) {
this.comment = purpose;
}
// 设置cookie适用的域名
public void setDomain(String pattern) {
this.domain = pattern.toLowerCase(Locale.ENGLISH);
}
// 设置过期时间(单位是秒),如果不设置,cookie在当前session中有效
// 如果设置生命周期会写在文件里
// 如果不设置生命周期会写在浏览器内存里,窗口关闭,cookie就没了
public void setMaxAge(int expiry) {
this.maxAge = expiry;
}

// 设置cookie适用的路径,如果不指定,在当前目录及其子目录的URL下会返回cookie
public void setPath(String uri) {
this.path = uri;
}

// 是否只在加密的连接上(SSL)发送
public void setSecure(boolean flag) {
this.secure = flag;
}

// 设置cookie值
public void setValue(String newValue) {
this.value = newValue;
}


public void setVersion(int v) {
this.version = v;
}
获取cookie属性的方法
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
// 获取cookie的注释,如果没有为null
public String getComment() {
return this.comment;
}
// 获取cookie适用的域名
public String getDomain() {
return this.domain;
}
// 获取cookie过期时间,如果为-1,cookie表示持续到浏览器关闭
public int getMaxAge() {
return this.maxAge;
}
// 获取cookie适用的路径
public String getPath() {
return this.path;
}
// 获取是否只在加密的连接上发送
public boolean getSecure() {
return this.secure;
}
// cookie的名称,创建后不可修改
public String getName() {
return this.name;
}
// 获取cookie值
public String getValue() {
return this.value;
}

public int getVersion() {
return this.version;
}
删除cookie

设置生命周期 cookie.setMaxAge()方法设置,秒为单位,若为0,表示立即删除该cookie,将该cookie放到响应中返回

注意:一个servlet设置的cookie可以被同一个路径下或者子路径下的servlet读到,其他访问不到

​ 路径是指url可以通过cookie.setPath()方法设置cookie的作用范围

cookie适用场景
  • 自动登录,不需要填写用户名和密码
  • 浏览记录

session

session是记录在服务器端,获取session需要把sessionId传递给服务端,通过sessionId来取到对应的session,关闭浏览器,session不会被销毁,还可以通过sessionId找到该session,在同一个application下的servlet/jsp可以共享一个session,前提是同一个客户端窗口

操作session
创建或获取session
1
2
// 若为false,如果当前没有关联的session,如返回null;若为true,如果没有则会创建 默认是true
HttpSeesion session = request.getSession(true);
session的相关方法
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
// 返回session的创建时间(单位毫秒)
long getCreationTime();
// 获取sessionId
String getId();
// 返回客户端最后一次发送与该session会话相关的请求的时间(单位毫秒)
long getLastAccessedTime();

ServletContext getServletContext();
// session的过期时间,单位秒
// 也可以在web.xml中设置过期时间,单位为分钟,tomcat默认是30分钟
// <session-config>
// <session-timeout15></session-timeout>
// </session-config>
void setMaxInactiveInterval(int var1);
// 返回servlet容器在客户端访问时保持session会话打开的最大时间间隔,单位秒
int getMaxInactiveInterval();
// 返回seesion会话中该名称的对象,没有返回null
Object getAttribute(String var1);
// 返回该session会话中所有的名称
Enumeration<String> getAttributeNames();
// 将对象绑定到该session会话中
void setAttribute(String var1, Object var2);
// 移除指定名称的对象
void removeAttribute(String var1);
// 使该session无效
void invalidate();
// 是否为新创建的session(客户端还不知道该session)
boolean isNew();
1
2
3
4
5
6
// 判断当前请求的session是否合法
req.isRequestedSessionIdValid();
// 判断当前请求是不是从URL发出的
req.isRequestedSessionIdFromURL();
// 判断当前请求是不是从cookie发出的
req.isRequestedSessionIdFromCookie();
session的实现方式

session有两种实现方式
通过cookie来实现 第一次请求时,响应在响应头set-Cookie中 有sessionId,把sessionId放到cookie中,如果浏览器支持cookie,会把sessionId放到cookie中
默认是存储在内存中的,没有存储在磁盘上,关闭浏览器就会失效
可以进行持久化,使用cookie.setMaxAge

通过URL重写来实现
response.encodeURL两个作用

  • 转码
  • URL后加上sessionID

过滤器Filter

可以对请求和响应进行拦截,在访问后端资源之前,拦截这些来自客户端的请求,在发送回客户端之前,处理这些响应

过滤器的类型

  • 身份验证过滤器
  • 数据压缩过滤器
  • 加密过滤器
  • 触发访问事件资源的过滤器
  • 图像转换过滤器
  • 日志记录和审核过滤器
  • MIME-类型链过滤器
  • Tokenizing过滤器
  • 转换XML内容的XSL/T过滤器

过滤器的使用

需要实现Filter接口

1
2
3
4
5
6
7
8
public interface Filter {
// 由servlet容器调用,指示一个过滤器被放入服务
void init(FilterConfig var1) throws ServletException;
// 在每次一个请求或响应在所对应的资源下时通过链传递,由容器调用
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
// 由servlet容器调用,指示一个过滤器从服务去除
void destroy();
}

​ 在web.xml中配置写好的Filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<filter>
<filter-name>security</filter-name>
<filter-class>com.zhanghe.study.webstudy.filter.SecurityFilter</filter-class>
<!--用户名-->
<init-param>
<param-name>userName</param-name>
<param-value>john</param-value>
</init-param>
</filter>

<!-- 拦截的顺序与在web.xml中 filter-mapping的配置顺序有关,靠前的先被调用 -->
<filter-mapping>
<filter-name>security</filter-name>
<url-pattern>/*</url-pattern>
<!-- 在filter-mapping中有一个dispatcher标签,指定过滤器所拦截的资源被Servlet容器的调用方式
可以是REQUEST,INCLUDE,FORWARD和ERROR,默认是REQUEST
可以指定多个dispatcher来指定Filter对资源的多种调用方式进行拦截 -->
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

异常处理

当servlet出现异常时,servlet容器使用exception-type元素来找到与抛出的异常类型相匹配的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ExceptionHandler extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
Integer code = (Integer) req.getAttribute("javax.servlet.error.status_code");
String message = (String) req.getAttribute("javax.servlet.error.message");
System.out.println("=========");
System.out.println(throwable);
System.out.println("=========");
System.out.println(code);
System.out.println("=========");
System.out.println(message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 配置异常处理的servlet -->
<servlet>
<servlet-name>ExceptionHandler</servlet-name>
<servlet-class>com.zhanghe.study.servlet.ExceptionHandler</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ExceptionHandler</servlet-name>
<url-pattern>/ExceptionHandler</url-pattern>
</servlet-mapping>
<!-- 配置哪些错误码会调用该异常处理类 -->
<error-page>
<error-code>404</error-code>
<location>/ExceptionHandler</location>
</error-page>
<!-- 配置哪些异常类型会调用该异常处理类 -->
<error-page>
<exception-type>java.lang.ArithmeticException</exception-type>
<location>/ExceptionHandler</location>
</error-page>

如果出现异常,会在请求域中设置相应的属性

可以使用request.getAttribute(“”)取出

1
2
3
4
5
6
javax.servlet.error.status_code //错误码,Integer类型
javax.servlet.error.exception_type // 异常类型,Class类型
javax.servlet.error.message //异常信息,String类型
javax.servlet.error.request_uri //出现异常的uri地址,String类型
javax.servlet.error.exception //异常,Throwable类型
javax.servlet.error.servlet_name //servlet名称,String类型

上传文件

1
2
3
4
5
6
<!-- 上传文件一定要使用post请求
enctype需要设置为multipart/form-data
input标签的type类型设为file
-->
<form method="post" enctype="multipart/form-data">
<input type="file" name="file">

form表单的编码格式
①application/x-www-form-urlencoded 默认 在发送前编码所有字符
②multipart/form-data 不对字符编码,二进制
③text/plain 空格转换为+号,但不对特殊字符编码

使用读取流的方式来读取太过于麻烦 request.getInputStream() 获取到的是整个请求体,需要解析各个字段和分隔
解析multipart/form-data比较复杂
可以使用外部依赖包
commons-fileupload.jar 依赖于 commons-io.jar

1
2
3
4
5
6
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>

commons-fileupload解析请求,解析成FileItem集合,无论是文本域还是文件域
使用FileItem.isFormField()来判断是不是一个表单域
表单域 item.getFieldName item.getString

​ 非表单域 item.getFieldName item.getName item.getContentType item.isImemory item.getSize item.getInputStream

转发和重定向

国际化

  • 国际化(i18n): i18n internationalization,网站能够提供翻译成访问者的语言或国籍的不同版本的内容
  • 本地化(i10n): 向网站添加资源,使其适应特定的地理或文化区域,例如将网站翻译为中文
  • 区域设置:通常为语言符号后跟一个由下划线分隔的国家符号。例如”en_US”

获取区域

1
2
//获取当前国家和语言
Locale locale = request.getLocale();

获取区域设置

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
// 获取该区域设置的国家
public String getCountry() {
return baseLocale.getRegion();
}
// 获取该区域设置的国家名称
public final String getDisplayCountry() {
return getDisplayCountry(getDefault(Category.DISPLAY));
}
// 获取该区域设置的语言代码
public String getLanguage() {
return baseLocale.getLanguage();
}
// 获取该区域设置的语言名称
public final String getDisplayLanguage() {
return getDisplayLanguage(getDefault(Category.DISPLAY));
}
// 返回该区域设置的国家的三个字母缩写
public String getISO3Country() throws MissingResourceException {
String country3 = getISO3Code(baseLocale.getRegion(), LocaleISOData.isoCountryTable);
if (country3 == null) {
throw new MissingResourceException("Couldn't find 3-letter country code for "
+ baseLocale.getRegion(), "FormatData_" + toString(), "ShortCountry");
}
return country3;
}
// 返回该区域设置的语言的三个字母缩写
public String getISO3Language() throws MissingResourceException {
String lang = baseLocale.getLanguage();
if (lang.length() == 3) {
return lang;
}

String language3 = getISO3Code(lang, LocaleISOData.isoLanguageTable);
if (language3 == null) {
throw new MissingResourceException("Couldn't find 3-letter language code for "
+ lang, "FormatData_" + toString(), "ShortLanguage");
}
return language3;
}

时间国际化

时间格式化 DateFormat

货币国际化

数字、货币格式化 NumberFormat

字符串国际化

字符串格式化 MessageFormat

1
2
3
4
5
String str = "Date: {0},Salary: {1}";
Locale locale = Locale.CHINA;
Date date = new Date();
double sa1 = 12345.12;
MessageFormat.format(str,date,sa1);

国际化配置

native2ascii命令在jdk下 可以查看ascii码

1
2
Locale locale = Locale.CHINA;
ResourceBundle bundle = ResourceBundle.getBundle("i18n",locale);

在配置文件中i18n.properties i18n_en_US.properties i18n_zh_CN.properties
根据locale来找不同的配置文件

中文乱码问题
GET请求 ①tomcat的server.xml文件中,在Connector 节点中添加useBodyEncodingForURI=”true” 属性 使用请求体的编码,然后在获取请求内容之前使用request.setCharacterEncoding(“UTF-8”)
②tomcat的server.xml文件中,在Connector 节点中添加URIEncoding=”UTF-8”属性
③tomcat的get请求默认使用ISO-8859-1来编码,可以在获取的时候进行转码,new String(request.getParameter(“name”).getBytes(“ISO-8859-1”),”UTF-8”)

多个请求使用同一个Servlet
第一种方案:在url加上入参method
根据method进行分发

​ 第二种方案:
​ web.xml使用*.do来匹配Servlet
​ 根据 request.getServletPath()然后反射调用方法

表单的重复提交
重复提交的情况
①在表单提交到一个Servlet,Servlet又通过请求转发的方式响应了一个页面,此时地址栏还保留着Servlet的那个路径,在响应页面点击刷新
②在响应页面没有到达时重复点击提交按钮
③点击返回,再点击提交

不是重复提交的情况
①点击返回之后,刷新页面,再点击提交

如何避免表单的重复提交
使用session,生成属性,移除属性

HttpServletRequestWrapper类包装原始的request对象,实现了HttpServletRequest接口的所有方法,内部调用了所包装的
request对象的对应方法
相应的也有HttpServletResponseWrapper类来包装原始的response对象
继承HttpServletRequestWrapper,重写

Servlet监听器
用于监听ServletContext、HttpSession、ServletRequest等对象的创建和销毁,以及属性修改

①监听ServletContext、HttpSession、ServletRequest等对象的创建和销毁
ServletContextListener 创建contextInitialized 销毁contextDestroyed
HttpSessionListener 创建sessionCreated 销毁sessionDestroyed
ServletRequestListener 创建requestInitialized 销毁requestDestroyed

实现相应的接口,监听不同的域对象

1
2
3
<!-- web.xml -->
<listener>
<listener-class>

场景:
ServletContextListener最常用,在当前WEB应用加载的时候对当前WEB应用的相关资源进行初始化操作:创建数据库连接池,创建Spring的IOC容器,读取当前WEB应用的初始化参数

②监听域对象 ServletContext、HttpSession、ServletRequest 属性变更的监听器
ServletContextAttributeListener attributeAdded attributeRemoved attributeReplaced
HttpSessionAttributeListener attributeAdded attributeRemoved attributeReplaced
ServletRequestAttributeListener attributeAdded attributeRemoved attributeReplaced
ServletRequestAttributeEvent 可以getName、getValue或者值

③感知session绑定的监听器
保存到Session域中的对象可以有多种状态:绑定到Session中,从Session中解除绑定;随Session对象持久到到一个存储设备中;随Session对象从一个存储设备中恢复
HttpSessionBindingListener和HttpSessionActivationListener接口,实现这两个接口不需要在web.xml文件中注册

放到session中的对象实现HttpSessionBindingListener
会触发两个方法 绑定valueBound 解除valueUnBanding

实现了HttpSessionActivationListener接口的对象可以感知自己被钝化和被活化的事件
sessionWillPassivate 从内存写到磁盘 sessionDisActivate 从磁盘中读取出来

session会被存储在tomcat当前项目下 .cer文件