Java Web基础教程

Java Web基础教程

1. Servlet基础

1.1 什么是Servlet

Servlet是JavaEE中的标准组件之一,专门用于处理客户端的HTTP请求。并且它必须依赖于Servlet容器(Tomcat就是一个标准的Servlet容器)才能运行。因为Servlet实例的创建和销毁都是由容器负责管理的,我们不能自行通过new关键去创建和使用Servlet。

1.2 编写一个简单的Servlet

  1. 在任意地方创建一个myweb文件夹,这个文件夹相当于一个web项目根目录

  2. 在根目录下创建WEB-INF子目录

  3. 在WEB-INF目录下创建src和classes子目录

  4. 在src目录下编写一个类,继承HttpServlet这个父类

public class HelloServlet extends HttpServlet {
}
  1. 重写父类的service方法,这个就是专门处理客户端请求的方法,web容器会为这个方法传入两个参数HttpServletRequest和HttpServletResponse,并且这个方法还需要抛出ServletException和IOException给容器捕获
public class HelloServlet extends HttpServlet {
	public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{
        //设置响应类型及编码
        response.setContentType("text/html;charset=utf-8");
    	  //获取字符输出流输出html信息
        response.getWriter().println("<h1>Hello Servlet</h1>")
	}
}    
  1. 编译Servlet,需要依赖servlet-api.jar文件,将它添加到classpath中
javac -cp d:\servlet-api.jar; HelloServlet.java
  1. 将编译后的HelloServlet.class文件剪切到classes目录中

  2. 在WEB-INF目录下创建并编辑web.xml文件,为servlet配置请求映射

<?xml version="1.0" encoding="utf-8"?>
<!-- 配置根节点 -->
<web-app>
   <!-- 配置servlet类 -->
   <servlet>
   	  <!-- 指定servlet的别名 -->
   	  <servlet-name>hello</servlet-name>
   	  <!-- 指定Servlet的完整类名-->
   	  <servlet-class>HelloServlet</servlet-class>
   </servlet>
   <!-- 配置请求映射-->
   <servlet-mapping>
   	   <!-- 这里的servlet-name和上面的servlet-name要一一对应 -->
   	  <servlet-name>hello</servlet-name>
   	  <!-- 配置请求映射的url,必须以“/”开头-->
   	  <url-pattern>/hello</url-pattern>
   </servlet-mapping>
</web-app>	
  1. 将myweb项目拷贝到tomcat的webapps目录中,并执行bin目录下的startup.bat启动容器

  2. 打开浏览器,在地址栏输入http://localhost:8080/myweb/hello访问Servlet

1.3 Servlet的请求处理流程

浏览器发起http的请求,这个请求首先会被servlet容器(Tomcat)截获,然后容器会根据web.xml文件中配置servlet的来找到相应的这个别名,然后再根据这个别名找到具体Servlet的类,然后容器会创建这个Servlet类的实例并调用service方法来处理这个请求。通常Servlet是以单实例多线程的方式处理客户端请求。

1.4 Servlet的生命周期

所谓的生命周期,就是从Servlet的创建一直到它销毁的整个过程。并且它的这个生命周期都是由Servlet容器(如:Tomcat)负责管理和维护的。

1.4.1 Servlet对象创建的过程

当第一次请求某个Servlet的时候,容器会先查找之前有没有创建过这个Servlet的实例,如果没有则创建一个并缓存起来。后续相同的请求都由这个缓存的对象来处理。(注意:这里说的是第一次请求时创建。另外一种情况则是在容器启动的时候就创建Servlet的实例,在web.xml中为Servlet指定配置,这个配置的值是一个整型,哪一个Servlet配置的数值越小,创建的优先级别越高)

1.4.2 生命周期方法
方法名描述
init在Servlet对象创建之后立即执行的初始化方法,且只执行一次
service核心的请求处理方法,这个方法可以执行多次
destroy容器准备销毁Servlet实例之前执行这个方法,也是执行一次(容器重启或关闭)

1.5 HTTP报文组成

1.5.1 请求报文

请求行:请求报文的第一行就是请求行。包括请求方法、请求URL地址、HTTP协议版本。

请求头:请求行之后的信息就是请求头,它是以“名称:内容”的格式体现。主要包括服务器主机地址及端口、连接状态、系统信息、编码、语言等等。

请求体:请求头结束之后会有一个空行,空行之后就是请求体的内容。通常使用POST提交的数据信息会存放在请求体当中,然后传递给服务器。

1.5.2 响应报文

状态行:主要包括HTTP协议、响应状态码(例如:200表示OK,成功响应)。

响应头:主要包括服务器信息、响应的类型及编码、内容的长度、响应的时间等。

响应体:服务端可以将信息数据携带到响应体中,带回客户端。

1.6 HTTP请求方法

在HTTP/1.1协议中,请求方法主要包括8个,下面列举常用的请求方法进行说明。

方法说明
GET向服务器请求指定的资源,并返回响应主体。一般来说GET方法应该只用于数据的读取(类似于查询)
POST向指定的服务器提交数据(例如:表单数据的提交、文件上传等),并且提交的数据会放入请求体中(类似于新增)
PUT向服务器提交数据,但是和POST有所区别。如果服务器不存在此资源的时候,则执行新增,如果存在则执行修改(类似于修改)
DELETE根据uri的标识删除服务器上的某个资源(类似于删除)
其他

备注:GET与POST区别:

  1. GET主要用于获取数据,POST用于提交数据。

  2. GET请求所带的参数是放在请求行的url地址后面,而POST这是放在请求体中。

  3. 通常浏览器会对GET请求的url长度有所限制,而POST提交的数据在请求体中,可以提交更多的内容。

  4. 浏览器会对GET请求进行缓存。

1.7 Servlet的请求处理方法

方法说明
service可以处理任何的请求类型
doGet处理对应的GET请求
doPost处理对应的POST请求
doPut处理对应的PUT请求
doDelete处理对应的DELETE请求
其他

说明:通过HttpServlet的源代码得知,默认的所有请求都会先经过service方法,然后service方法根据请求的方法类型判断来决定交给doGet或者是doPost方法来处理请求。如果子类重写了父类的service方法同时还重写了其他的doXxx的方法,那么只有service方法会处理请求,其他方法将失效。

1.8 Request与Response对象

当web容器调用某个Servlet的Service方法时,会创建一个HttpServletRequest和HttpServletRespinse对象作为参数传入到这个方法中,那么我们可以通过HttpServletRequest来获取相关的请求内容等,而响应客户端可以利用HttpServletResponse对象来完成。

1.8.1 HttpServletRequest常用API
方法说明
getParameter(String name)获取请求参数,根据请求参数的name指定
getParameterValues(Spring name)获取相同name的请求参数,返回的是字符串数组
getParameterNames()获取所有的请求参数名称
getParameterMap()获取所有请求参数,包括参数名称和值
getMethod()获取客户端的请求方法
getHeader(String name)根据请求头的名称获取响应的信息
getRemoteAddr()获取远程客户端的IP地址
getServletPath()获取Servlet的请求地址,也就是url-pattern
getRequestURL()获取请求完整的URL地址
getRealPath(String path) 废弃获取项目的绝对路径。(这个方法在request对象中已废弃,建议通过ServletContext对象获取)
其他
1.8.2 HttpServletResponse常用API
方法说明
setContentType(String str)设置响应内容的类型及编码
getWriter()获取响应字符输出流
getOutputStream()获取字节输出流
setHeader(String name, String value)设置响应头信息,如果存在响应头信息,则执行更新
addHeader(String name, String value)设置响应头,不管存不存在都会新加入一个
setStatus(int code)设置响应状态码
其他
1.8.3 常见的响应状态码
状态码说明
200请求已成功
302重定向
401请求被禁止,未授权
404请求的资源不存在
405请求行的方法不被支持
500服务器内部错误
其他

1.9 Servlet之间的通信

1.9.1 转发

所谓转发,就是在多个Servlet之间共享请求和响应对象,所有参与转发过程的Servlet都可以共享同一个请求对象的信息,在Servlet的API中,转发的操作是由HttpServletRequest对象完成的。

转发的特点:

  1. URL地址栏不会发生改变
  2. 转发的过程是在服务端自动完成

示例代码:

public class ServletA extends HttpServlet{
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		//获取页面提交的参数
		String name = request.getParameter("userName");		
		//转发由HttpServletRequest完成
		//第一步先获取一个请求转发器RequestDispatcher
		//获取请求转发器的同时要告诉转发器转发到哪里,转发给谁
		//如果要转发给ServletB,那么就是对应ServletB的url-pattern
		RequestDispatcher rd = request.getRequestDispatcher("servletB");
		//调用转发器的forward方法执行转发,同时将request和response对象一并转发ServletB
		rd.forward(request, response);
	}
}
public class ServletB extends HttpServlet{
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//这里可以获得同一个请求的参数
		String name = request.getParameter("userName");
		System.out.println("ServletB获取请求参数:"+name);
	}
}

请求作用域:

每一个请求对象都有一个独立的空间,这个空间我们称之为请求作用域(RequestScope)。可以为当前这个请求携带额外的一些数据信息,这些信息同样可以在多个Servlet之间进行共享。

示例代码:

public class ServletA extends HttpServlet {
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		//在ServletA中为request对象设置请求作用域
        request.setAttribute("age", 35);
	}
}
public class ServletB extends HttpServlet{
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		//在ServletB中获取统一个请求对象作用域的值
        Integer age  = (Integer)request.getAttribute("age");
	}
}
1.9.2 重定向

重定向的机制和转发不同,首先重定向是通过HttpServletResponse对象来完成。一次重定向的过程中会有两次请求和两次响应,服务器在接收第一次请求后会先做一次302的响应(302表示重定向状态码),告诉客户端浏览器必须发起一个新的请求地址,服务端再次接收这个请求处理,最后再次响应客户端。由于会产生不同的请求对象,因此并不同共享同一个请求中的参数。

重定向的特点:

  1. URL地址栏会发生改变
  2. 重定向的操作是在客户端浏览器完成的

示例代码:

方式一:

public class ServletC extends HttpServlet {

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//执行重定向
		//方式一:设置302响应状态码,并在响应头中添加location属性指定重定向的地址
		response.setStatus(302);
		response.addHeader("location", "http://localhost:8080/ch06/servletD");
	}
}

方式二:

public class ServletC extends HttpServlet {

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//执行重定向
		//方式二:使用response的sendRedirect方法
		response.sendRedirect("servletD");
	}
}

1.10 会话跟踪

由于HTTP协议是无状态的,服务端并不会记录每一个客户端的状态,因此,如果想要实现服务器能记录客户端的状态的话,那么就需要会话跟踪技术。

1.10.1 cookie

cookie是客户端浏览器内部的一个文本文件,用于记录服务器发送过来的一些文本信息。可以在Servlet中创建Cookie对象,在响应时将cookie写回浏览器。在接下来的每次请求中,客户端会都把这个cookie信息带回给服务器,服务器就可以获取cookie的信息,达到会话跟踪的目的。由于cookie是保存在客户端浏览器,也就是会话信息将由客户端维护。

设置cookie:

public class SetCookieServlet extends HttpServlet{

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//创建一个Cookie的实例
		Cookie cookie1 = new Cookie("userId","10001");
    Cookie cookie2 = new Cookie("age","21");
		//将cookie对象设置到响应对象中
		response.addCookie(cookie1);
    response.addCookie(cookie2);
	}
}

获取cookie:

public class GetCookieServlet extends HttpServlet{

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse repsonse) throws ServletException, IOException {
		//cookie是通过request对象来得到的
		//从请求中可以获取多个cookie对象
		Cookie[] cookies = request.getCookies();
		for (Cookie cookie : cookies) {
			//判断cookie,只获取name为userId的cookie对象
			if("userId".equals(cookie.getName())) {
				System.out.println(cookie.getValue());
			}
		}
	}	
}

cookie保存中文:

在保存cookie的时候如果需要保存中文,那么中文信息需要经过编码后才可以写入cookie

示例代码:

编码使用URLEncoder

String str = URLEncoder.encode("张三", "utf-8");
Cookie cookie = new Cookie("userName", str);

解码使用URLDecoder

String str = URLDecoder.decode(cookie.getValue(), "utf-8");
System.out.println(str);

cookie的生命周期:

默认cookie只会保存在浏览器进程的内存中,并不会写入cookie文件,如果关闭了浏览器,那么浏览器的进程也就消失了,那么对应的内存就会释放空间,因此cookie也就销毁。如果想将cookie写入文件,那么就必须设置cookie的生命时长,一旦设置了生命时长,那么就表示这个cookie会在文件中保留多长的时间,到了这个时间之后,浏览器就会自动销毁这个cookie。

设置cookie存活时间:

Cookie cookie = new Cookie("userId","10001");
//设置为0表示立即删除cookie
cookie.setMaxAge(0);
//设置为正数表示cookie在cookie文件的存活时间,单位:秒
cookie.setMaxAge(60*60);
//设置为-1表示cookie只保留在浏览器器的进程中,关闭浏览器之后会销毁cookie
cookie.setMaxAge(-1);
1.10.2 Session

Session是基于服务端来保存用户的信息,这个是和cookie的最大区别。不同的客户端在请求服务器的时候,服务器会为每一个客户端创建一个Session对象并保存在服务器端,Session对象是每个客户端所独有的,相互之间不能访问。服务器为了区别不同的Session属于哪一个客户端,因此Session对象也有一个唯一标识,叫做SessionID。需要注意的是这个SessionID是以cookie的机制保存在客户端浏览器。每次请求的时候,浏览器都会把这个SessionID带回服务端,服务端根据这个SessionID就可以找到对应的Session对象。注意:session的创建是在第一次调用request.getSession()方法时创建的,因为这个方法会判断之前有没有创建过Session,如果有则直接使用,没有则创建一个,不会出现同一个客户端同时创建多个Session的情况。

示例代码:

public class SessionServlet extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//HttpSession对象是在第一次调用request的getSession()方法时才会创建
		//注意:getSession()的方法会先判断之前是否为客户端创建了session实例,
		//如果创建了,则使用之前创建好的Session对象,没有则创建一个新的Session
		HttpSession session = request.getSession();
		//创建HttpSession的同时,会创建一个唯一的标识SessionID
		//这个sessionId会保存在浏览器的cookie中,每次请求会带回这个id找到相应的session对象
		String sessionId = session.getId();
		System.out.println(sessionId);
	}
}

session的生命周期:

SessionId是保存在浏览器的cookie中,但是不会写入cookie文件,这也就表示当关闭浏览器之后,SessionId就会销毁。SesisonId销毁以后,服务端的Session就没有任何作用了。但是,服务器并不会立刻销毁这个Session对象,至于什么时候销毁是由服务器自己决定的。除非我们手动调用了session.invalidate() 方法,服务器就会立即销毁这个session实例。

例如:

HttpSession session = request.getSession();
//立即销毁session
session.invalidate();

Session默认也有存活时间,服务器在创建Session的时候为Session设置默认的存活时间为30分钟,如果在30分钟之内,客户端没有发起任何请求到服务器,那么服务器就会销毁这个Session对象。我们也可以设置Session的存活时间。可以为当前的Session设置,也可以为全局(服务器端的所有Session)的Session设置。

设置当前Session的存活时间:

HttpSession session = request.getSession();
//设置当前Session的存活时间,单位:秒
session.setMaxInactiveInterval(3600);

设置全局的Session的存活时间:

在web.xml中进行设置

<!-- 设置全局Session的存活时间,单位:分钟 -->
<session-config>
  <session-timeout>60</session-timeout>
</session-config>

Session的作用域:

当我们需要将一些数据信息存入Session的时候,就需要操作Session作用域(SessionScope),它和请求作用域类似,也有相应的setAttribute和getAttribute方法,只不过Session作用域的范围要比请求作用域更宽。请求作用域在一次请求响应之后就会消失(因为相应之后请求对象就会销毁)。而Sesison对象只要浏览器不关闭或者Session对象未超时,那么Session对象会一直驻留在服务器端,因此不管重新请求多少次还是转发和重定向,都可以从Session中获取之前保存的数据信息。

示例代码:

User user = new User();
user.setUid("1001");
user.setUserName("wangl");
HttpSession session = request.getSession();
//将数据保存在会话作用域中
session.setAttribute("user", user);

从会话作用域取值

HttpSession session = request.getSession();
//取值
User user = (Object)session.getAttribute("user");

URL重写:

浏览器是可以禁用cookie的,一旦禁用了cookie,那么SessionId将无法写入cookie的缓存中,这样就导致无法实现会话跟踪了,因此解决办法就是使用URL重写。URL重写的目的就是把SessionId在放在请求URL地址的后面提交回服务器(类似GET请求后面带上参数,而这个参数就是一个SessionId),服务器会解析这个URL的地址并得到SessionId。

示例代码:

//重写请求的URL地址,这个地址后面会自动带上SessionId
String url = response.encodeRedirectURL("/getUser");
//重定向URL
response.sendRedirect(url);

浏览器地址演示

http://localhost:8080/ch07/getUser;jsessionid=6F1BA8C92D7E5D7CC479ED8DD30D3ED0

注意:;号后面跟着就是SessionId

1.11 ServletContext

web容器在启动时会为每一个web应用创建唯一的上下文对象,这个对象就是ServletContext,这个上下文对象可以理解为是当前项目的一个共享内存空间,为项目中的所有Servlet提供一个共享的区域。

常用API:

方法说明
getContextPath()获取项目的相对路径
getRealPath(String path)获取项目的绝对路径
getInitParameter(String name)获取上下文的初始化参数(web.xml中配置的)
setAttribute(String name, String value)将数据放入上下文作用域
getAttribute(String name)从上下文作用域中去获取数据

上下文作用域:

上下文作用域是为当前项目所有Servlet提供的一个共享内存区域,可以将需要的数据信息保存在作用域中。这个作用域的的范围是最大的,只要容器没有停止,它就会一直存在。

三种作用域比较:

结合前面所学的作用域共有三个,分别是请求作用域、会话作用域、上下文作用域。

范围从小到大来划分:

请求作用域 < 会话作用域 < 上下文作用域

1.12 过滤器

过滤器可以在请求到达servlet之前和servlet响应客户端之前进行拦截,相当于一个拦截器。主要用于进行一些请求和响应的预处理操作。常用的场景包括用户的认证授权、统一请求字符编码等等。

1.12.1 编写过滤器

要实现一个过滤器,必须实现一个FIlter接口,只有实现了这个接口的类才称之为过滤器。

示例代码:

public class DemoFilter implements Filter {
    ...
}

web.xml配置过滤器:

<filter>
  	<filter-name>demoFilter</filter-name>
  	<filter-class>edu.nf.ch09.filter.DemoFilter</filter-class>
  	<!-- 初始化参数 -->
  	<init-param>
  		<param-name>param</param-name>
  		<param-value>hello</param-value>
  	</init-param>
  </filter>
  <filter-mapping>
  	<filter-name>demoFilter</filter-name>
  	<!-- 什么请求可以经过此过滤器,/*表示所有请求 -->
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
1.12.2 过滤器的生命周期

与Servlet类似,Filter同样也是容器负责创建和销毁,与Servlet的区别在于,容器会在启动的时候最先创建所有的过滤器,并执行init方法进行初始化。

生命周期方法:

方法说明
init初始化方法,容器启动时执行一次
doFilter请求过滤方法,决定请求是否放行
destroy容器销毁过滤器之前执行的方法

代码实例:

public class DemoFilter implements Filter{

	@Override
	public void destroy() {
		
		System.out.println("准备销毁DemoFilter");
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		//FilterChain表示一个过滤链对象,因为过滤器可能会存在多个
		//同时这个对象将决定是否放行当前请求,
		//放行的话则请求会继续到达下一个过滤器或者servlet中
		System.out.println("请求经过DemoFileer..放行");
		chain.doFilter(request, response);
		System.out.println("响应前经过DemoFilter...");
	}

	@Override
	public void init(FilterConfig config) throws ServletException {
		String name = config.getInitParameter("param");
		System.out.println("初始化DemoFilter,获取初始化参数:"+name);
	}
}
1.12.3 过滤链

在一个web项目中可能存在多个过滤器,当有多个过滤器存在的时候就会形成一个过滤链。请求会按照过滤器链的顺序一直传递下去,最终到达某个Servlet来处理请求。(注意:过滤链的顺序是按照web.xml中的先后配置顺序决定的)

配置示例:

<!-- 按先后顺序配置 -->
<!-- 配置第一个过滤器 -->
<filter>
	<filter-name>firstFilter</filter-name>
    <filter-class>edu.nf.ch09.filter.FirstFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>firstFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置第二个过滤器-->
<filter>
	<filter-name>secondFilter</filter-name>
    <filter-class>edu.nf.ch09.filter.SecondFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>secondFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

1.13 监听器

监听器用于监听对象的上的事件发生,在Servlet中监听器主要监听请求对象、会话对象、上下文对象以及监听这些对象的作用域操作。JavaEE为我们提供了一系列的监听器接口,开发时按需实现相应的接口即可。

1.13.1 监听作用域对象的创建与销毁
监听器说明
ServletRequestListener监听请求对象的创建和销毁
HttpSesisonListener监听会话对象的创建和销毁
ServletContextListener监听Servlet上下文对象的创建和销毁

代码示例:

1.请求对象监听器

public class DemoRequestListener implements ServletRequestListener{

	/**
	 * 当请求对象销毁后容器执行此方法
	 * 销毁方法中同样也有一个ServletRequestEvent事件对象
	 */
	@Override
	public void requestDestroyed(ServletRequestEvent event) {
		//通过这个事件对象就可以获取当前的请求对象
		HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
		System.out.println("销毁请求对象..."+request);
	}

	/**
	 * 当请求对象创建之后容器调用此方法
	 * ServletRequestEvent这个参数就是一个事件对象
	 */
	@Override
	public void requestInitialized(ServletRequestEvent event) {
		//通过这个事件对象就可以获取当前的请求对象
		HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
		System.out.println("初始化了请求对象..."+request);
	}

}

web.xml配置

<!-- 配置监听器 -->
  <listener>
    <!-- 指定监听器的完整类名 -->
  <listener-class>edu.nf.ch10.listener.DemoRequestListener</listener-class>
  </listener>

2.会话对象监听器

public class DemoSessionListener implements HttpSessionListener{

	/**
	 * 监听HttpSession对象的创建
	 * HttpSessionEvent参数是一个事件对象
	 * 通过它可以获得当前的HttpSession
	 */
	@Override
	public void sessionCreated(HttpSessionEvent event) {
		HttpSession session = event.getSession();
		System.out.println("创建了Session对象"+session);
	}

	/**
	 * 监听HttpSession对象的销毁
	 */
	@Override
	public void sessionDestroyed(HttpSessionEvent event) {
		HttpSession session = event.getSession();
		System.out.println("销毁了Session对象"+session);
	}
}

注意:当第一次调用了request.getSesison()方法创建Session时,监听器才会起作用。

web.xml配置

<listener>
    <!-- 指定监听器的完整类名 -->
  	<listener-class>edu.nf.ch10.listener.DemoSessionListener</listener-class>
  </listener>

3.Servlet上下文监听器

public class DemoContextListener implements ServletContextListener{

	/**
	 * 监听ServletContext的销毁
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		//通过事件对象获取ServletContext
		ServletContext sc = event.getServletContext();
		System.out.println("销毁了ServletContext对象..."+sc);
	}

	/**
	 * 监听SerlvetContext的创建
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		//通过事件对象获取ServletContext
		ServletContext sc = event.getServletContext();
		System.out.println("创建了ServletContext对象..."+sc);
	}
}

web.xml

<listener>
    <!-- 指定监听器的完整类名 -->
  	<listener-class>edu.nf.ch10.listener.DemoContextListener</listener-class>
  </listener>
1.13.2 监听作用域的操作
监听器说明
ServletRequestAttributeListener监听请求作用域的操作
HttpSessionAttributeListener监听会话作用域的操作
ServletContextAttributeListener监听Servlet上下文作用域的操作

示例代码:

这里以HttpSessionAttributeListener说明,其他作用于监听器用法相似。

public class DemoSessionAttributeListener implements HttpSessionAttributeListener{

	/**
	 * 当有数据添加到会话作用域时,执行此方法
	 */
	@Override
	public void attributeAdded(HttpSessionBindingEvent event) {
		//获取存入作用域的键和值
		System.out.println("存入会话作用域..."+event.getName() + " : " + event.getValue());
		
	}

	/**
	 * 当从会话作用域移除数据时,执行此方法
	 */
	@Override
	public void attributeRemoved(HttpSessionBindingEvent event) {
		System.out.println("移除会话作用域..."+event.getName() + " : " + event.getValue());
		
	}

	/**
	 * 当替换了会话作用域中的某个数据时,执行此方法
	 */
	@Override
	public void attributeReplaced(HttpSessionBindingEvent event) {
		HttpSession session = event.getSession();
        //从session中获取的是替换之后的值
		System.out.println("替换的值: "+session.getAttribute("userName").toString());
		//注意:这里event.getValue()获取到的是替换之前的值
		System.out.println("替换会话作用域..."+event.getName() + " : " + event.getValue());
	}
}

web.xml

<listener>
    <!-- 指定监听器的完整类名 -->
  	<listener-class>edu.nf.ch10.listener.DemoSessionAttributeListener</listener-class>
  </listener>

1.14 注解配置

Servlet3.0开始提供了一系列的注解来配置Servlet、Fiilter、Listener等等。这种方式可以极大的简化在开发中大量的xml的配置。从这个版本开始,web.xml可以不再需要,使用相关的注解同样可以完成相应的配置。

注解说明
@WebServlet这个注解标识在类上,用于配置Servlet。例如:@WebServlet(name=“hello”, urlPatterns=“/hello”) 也可简化配置@WebServlet(“/hello”)。initParams属性设置Servlet的初始化参数。loadOnStartup设置容器启动时初始化Servlet。
@MultipartConfig这个注解标识在类上,标识启用当前Servlet的文件上传功能。location属性设置文件上传的路径。maxFileSize属性限制单个文件上传的大小,默认为-1,表示没有限制。maxRequestSize属性限制一次请求上传总文件的大小,默认为-1,表示没有限制,fileSizeThreshold属性表示当前数据量大于该值时,内容将被写入文件。
@WebFilter这个注解标识在类上,用于配置Filter。例如:@WebFilter(filterName=“encode”,urlPatterns=“/*”) 也可简化配置@WebFilter(“/*”),initParams属性设置Servlet的初始化参数。
@WebListener这个注解标识在类上,用于配置各种监听器

示例:

Servlet配置示例:

@WebServlet(urlPatterns = "/hello",
        initParams = {@WebInitParam(name="name", value="aaa")},
        loadOnStartup = 0)
@MultipartConfig(maxFileSize = 5242880, maxRequestSize = 104857600)
public class TestServlet extends HttpServlet {
	...
}

文件上传配置示例:

@WebServlet(urlPatterns = "/hello")
@MultipartConfig(maxFileSize = 5242880, maxRequestSize = 104857600)
public class TestServlet extends HttpServlet {
	...
}

Filter配置示例:

@WebFilter(urlPatterns = "/*", 
        initParams = {@WebInitParam(name="name", value="aaa")})
public class TestFilter implements Filter {
	...
}

Listener配置示例:

@WebListener
public class ApplicationListener implements ServletContextListener {
  ... 
}

1.15 动态注册

从Servlet3.0开始,支持ServletContext来动态注册相关的Servlet组件。例如可以在自定义的ServletContextListener中,通过在contextInitialized方法中获取ServletContext对象来注册Servlet、Filter、Listener。

动态注册Servlet
public class TestServlet extends HttpServlet { 
  ...
}
@WebListener
public class ApplicationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //获取ServletContext
        ServletContext sc = sce.getServletContext();
        //动态注册Servlet,返回动态注册器实例
        ServletRegistration.Dynamic registration = sc.addServlet("testServlet", TestServlet.class);
        //映射请求url
        registration.addMapping(new String[]{"/test"});
        //初始化参数
        registration.setInitParameter("name", "aaa");
        //启用文件上传
        registration.setMultipartConfig(
                new MultipartConfigElement(sc.getRealPath("/uplaod"), 
                        5242880, 
                        104857600, 
                        104857600)
        );
    }

}
动态注册Filter
public class TestFilter implements Filter {
   ...
}
@WebListener
public class ApplicationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext sc = sce.getServletContext();
        //动态注册Filter
        FilterRegistration.Dynamic registration = sc.addFilter("testFilter", TestFilter.class);
        //设置初始化参数
        registration.setInitParameter("name", "aaa");
        //请求映射配置
        //参数一:设置过滤器拦截的类型FORWARD,INCLUDE,REQUEST,ERROR,ASYNC
        //EnumSet是Set接口的一个特别实现,可用于对枚举类型进行特定的分组
        //参数二:设置该过滤器是否放在当前web应用中已经存在的过滤器之后,true表示之后,false表示之前
        //参数三:设置请求拦截的url
        registration.addMappingForUrlPatterns(
                EnumSet.of(DispatcherType.REQUEST), true, new String[]{"/*"});
    }
}
动态注册Listener
public class UserSessionListener implements HttpSessionListener {
    ...
}
@WebListener
public class ApplicationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext sc = sce.getServletContext();
        //动态注册Listener
        sc.addListener(UserSessionListener.class);
    }
}

1.16 文件上传

从Servlet3.0开始提供了文件上传的功能,操作起来更加的简单和方便。要想使用这个功能,首先必须在web.xml或者使用注解开启Servlet的上传功能,否则无效。

xml配置:

<servlet>
      <servlet-name>upload</servlet-name>
      <servlet-class>edu.nf.ch04.servlet.UploadServlet</servlet-class>
      <!-- 开启上传功能 -->
      <multipart-config/>
</servlet>
<servlet-mapping>
      <servlet-name>upload</servlet-name>
      <url-pattern>/upload</url-pattern>
</servlet-mapping>

注解配置:

@WebServlet("/login")
@MultipartConfig //开启上传功能
public class LoginServlet extends HttpServlet {
    ...
}

参数说明:

参数说明
location指定文件上传的目录
maxFileSize限制单个文件上传的大小
maxRequestSize限制一次请求上传总文件的大小
fileSizeThreshold设置缓存的大小,当达到缓存大小的时候,会将内存的数据写入location指定的目录中(也就是写入磁盘)

文件上传的核心接口是Part,通过HttpServletRequest对象可获得该接口的实现类对象。

//获取单个上传文件的Part
Part part = request.getPart("name");
//获取多个上传文件的Part
Collection<Part> cool = request.getParts();

常用API:

方法说明
getContentType()获取上传的文件类型
getSize()获取上传文件的大小
getSubmittedFileName()获取上文件的文件名
write()将文件上传(写入)到指定位置

当客户端使用form表单来上传文件时,必须将表单的enctype属性设置为"multipart/form-data"。

<form method="post" action="upload" enctype="multipart/form-data">
     <!-- input使用file类型 -->
	<input type="file" name="file"/>
</form>    

案例:

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //指定上传的路径
        String uploadPath = "/Users/wangl/uploads";
        //创建文件夹,如果不存在的情况下
        File dir = new File(uploadPath);
        if(!dir.exists()){
            dir.mkdir();
        }

        //上传单个文件,参数对用input的name属性的值
        //Part part = req.getPart("file");
        //uploadPath = uploadPath + "/" + part.getSubmittedFileName();
        //执行上传
        //part.write(uploadPath);
        //获取文件的类型
        //System.out.println(part.getContentType());
        //获取文件的大小
        //System.out.println(part.getSize());
        //获取上传的文件名
        //System.out.println(part.getSubmittedFileName());

        //上传多个文件
        Collection<Part> parts = req.getParts();
        for (Part p : parts) {
            p.write(uploadPath + "/" + p.getSubmittedFileName());
        }
    }
}

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>文件上传</h2>
 <form method="post" action="upload" enctype="multipart/form-data">
     Username:<input type="text" name="userName"/><br/>
     File1:<input type="file" name="file"/><br/>
     File2:<input type="file" name="file"/><br/>
     <input type="submit" value="submit"/>
 </form>
</body>
</html>

2. JSP基础

2.1 简介

JSP全名为Java Server Pages,中文名叫java服务器页面,是一种动态页面技术,而HTML是属于静态页面。JSP可以在HTML中嵌入Java脚本代码,因为JSP本质上还是一个Servlet,因此JSP也必须依赖于web容器才能运行。JSP的出现并不是为了取代Servlet,而是简化了Servlet的工作,将Servlet中繁琐的视图呈现代码脱离出来,交给JSP来完成,让Servlet专注于请求的处理,所以在开发中通常将JSP和Servlet结合一起使用。

2.2 JSP引擎

JSP引擎主要负责将JSP文件转义成一个Servlet的java源文件,然后再通过javac将这个源文件编译成class字节码文件并装载到JVM中执行。JSP引擎的核心类是JspServlet,位于Jasper.jar文件中,并且在Tomcat的web.xml中也默认就配置好了这个类(JspServlet就是一个Servlet)。因此,凡是以“.jsp”结尾的请求都会先经过JspServlet,那么这个引擎就可以开始工作。通常引擎转义和编译后的文件放在容器的work工作目录中。

注意:如果第一次访问JSP文件的时候,由于work目录中并不存在源文件和字节码文件,JSP引擎就必须完成这两个工作,因此,有可能在第一次访问JSP时会比较缓慢。当字节码编译出来加载后,第二次访问时速度就会很快了。

2.3 JSP三大元素

2.3.1 指令元素

语法:<%@ %>

指令说明
page指令用于设置JSP页面的相关信息以及编码
include指令用于静态包含其他的JSP页面代码,所谓静态包含,就是在编译期,将另外的JSP页面的代码合并到当前的JSP中,最终只会产生一个Java源文件
taglib指令这个指令用于引入标签库
2.3.2 动作元素

语法:jsp:xxx

动作说明
include动态包含其他JSP页面的内容,所谓的动态包含是指在编译期将不同的JSP文件转义成不同的Java源文件,然后在运行时才将目标内容包含到当前的JSP页面中
forward相当于Servlet中的转发,转发到其他的JSP页面或者Servlet
param用于传递参数,通常结合其他的动作一起使用,例如转发时需要提交一些而外的参数
useBean用于在JSP页面中使用JavaBean对象,通常结合setProperty和getProperty来使用,完成bean对象的赋值和取值操作
2.3.3 脚本元素

脚本元素主要就是在JSP中嵌入Java脚本代码,包括声明、表达式、Java脚本

声明语法:<%! %>

表达式::<%= %>

Java脚本: <% %>

示例:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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>
   <%-- 声明变量和方法,这里声明的变量a是实例变量 --%>
   <%! 
   		int a = 10;
   		public void say(){
   			System.out.println("hello");
		}
   	%>
   	
   	<%-- 表达式,注意:表达式后面是不允许有;号结束的 --%>
   	3 + 1 = <%=3+1%><br/>
   	
   	<%-- Java脚本,脚本代码最终会生成在servlet中的service方法中作为代码片段 --%>
   	<table border="1">
   		<tr>
   		   <th>Name</th>
   		   <th>Age</th>
   		</tr>
   		<% for(int i=0;i<5;i++){%>
   		  <tr>
   		  	<td>user<%=i%></td>
   		  	<td><%=10+i%></td>
   		  </tr>
   		<%}%>
   	</table>
</body>
</html>

2.4 JSP内置对象

内置对象,是在容器运行时将创建好的9个对象内嵌在JSP中,在JSP里可以直接拿来使用的对象。

对象说明
out字符流输出对象
config等同于Servlet中的ServletConfig
page表示当前JSP页面(等同于Servlet中的this)
request等同于Servlet中的HttpServletRequest
response等同于Servlet中的HttpServletResponse
session等同于Servlet中的HttpSession
application等同于Servlet中的ServletContext
pageContext表示当前JSP页面的上下文对象
exception表示当前JSP中的异常对象

示例代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!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>
  <%-- 常用内置对象 --%>
  <%
  	 //使用request,API使用同HttpServletRequest一样
     request.getParameter("userName");
     request.setAttribute("user", "user1");
     request.getAttribute("user");
     
     //使用session,API使用等同于HttpSession
     session.setAttribute("user", "user2");
     session.getAttribute("user");
     session.getId();
     
     //使用response
     //response.sendRedirect("demo.jsp");
     
     //out对象,等同于字符输出流对象,在JSP页面输出相关内容
     out.println("hello world");
     //这个是输出在控制中
     System.out.println("hello");
     
     //使用application,等同于ServletContext
     application.setAttribute("userName", "user3");
     
     //pageContext使用
     //从pageContext中获取相关的其他对象
     HttpServletRequest req = (HttpServletRequest)pageContext.getRequest();
     HttpServletResponse res = (HttpServletResponse)pageContext.getResponse();
     HttpSession ses = pageContext.getSession();
     ServletConfig conf = pageContext.getServletConfig();
     ServletContext sc = pageContext.getServletContext();
     //也可以通过pageContext来统一设置不同的作用域
     //第三个参数表示要放入到哪个作用域,是一个int类型的参数
     //1代表page作用域(当前页面有效)
     //2代表请求作用域
     //3代表会话作用域
     //4代表上下文作用域
     pageContext.setAttribute("userName", "zhangsan", 2);
     //也可以指定中哪个作用域取出相应的值
     String name = (String)pageContext.getAttribute("userName", 2);
     out.println(name);
  %>
</body>
</html>

2.5 EL表达式

EL(Expression Language),全称叫做表达式语言,是JSP2.0推出的一种技术。主要简化了在JSP中使用Java脚本表达式。EL表达式的特点在于使用简单,支持四则运算、逻辑运算等,并且还可以对Servlet API中的对象进行数据访问。EL的语法: ${expression}

算数与逻辑运算:

示例结果
${1+1}2
${2*2}4
${1==1}true
${2>5}false
${3>=2}true

数据访问:

1.使用“.”来访问

示例说明
${param.参数名}获取请求参数的值,相当于使用request.getParameter()方法
${requestScope.xxx}从请求作用域中访问数据
${sessionScope.xxx}从会话作用域中访问数据
${applicationScope.xxx}从上下文作用域中访问数据
${xxx}不指定作用域范围时,默认按照作用域范围从小到大的顺序自动查找
其他

2.使用"[]"来访问

示例说明
${requestScope[“userName”]}从请求作用域中取值
${sessionScope[“userName”]}从会话作用域取值
其他…同上

注意:通常使用"[]"来访问数据的时候,主要是访问一些特殊的名称,例如:request.setAttribute(“user.userName”)

这种方式如果使用${requestScope.user.userName}是无效的,应改为${requestScope["user.userName"]}

来访问。

2.6 JSTL

JSTL是JSP中的标准标签库,主要用于取代JSP中大量的Java脚本代码,让页面看起来更趋向于HTML。使用也很简单,通常结合EL表达式一起使用。

核心标签库(core)说明
<c:out>输出标签
<c:set>声明某个变量并存入指定的作用域
<c:redirect>重定向标签
<c:if>条件判断
<c:forEach>循环标签

备注:其他标签库请参阅相关官方文档

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值