Servlet

197 阅读9分钟

Servlet学习笔记

用于记录期间Java的Servlet/JSP的学习,因为这两个属于JavaWeb的学习内容,所以首先需要了解什么是JavaWeb,以及如何去学习JavaWeb。


JavaWeb的概念:

Internet上供外界访问的Web资源分为:静态web资源(如html 页面):指web页面中供人们浏览的数据始终是不变。动态web资源:指web页面中供人们浏览的数据是由程序产生的,不同时间点访问web页面看到的内容各不相同。静态web资源开发技术:HTML、CSS。动态web资源开发技术:JavaScript、JSP/Servlet、ASP、PHP等。在Java中,动态web资源开发技术统称为Java Web。

#####这是从百度百科查到的,我的理解是JavaWeb是用于开发动态Web网页的主要技术,Web由于Java的注入得到了强力的发展,至于什么是动态Web也不作多介绍,那么接下来我们该如何学习JavaWeb的技术呢?

image

上面这张图很好的介绍了学习JavaWeb所需要学习的基本内容,这次的笔记也就想写一下自己学习Servlet/JSP的小结,至于Tomcat的具体这里不做介绍,那么从Servlet开始小结.



Servlet是什么?

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。 使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Servlet架构

image 从上图我们可以明显的看出Servlet在Web应用中充当一个中转站(容器),Servlet处理来自客户端(浏览器)的请求数据,Servlet其实是Java的一个接口,但是通常情况下实现了Servlet接口的类都被成为Servlet,并且运行在支持Java的应用服务器中 Servlet的主要功能

Serlvlet的主要功能在于能与用户端(浏览器)交互式的浏览和修改数据,生产动态的Web应用,那么最基本的整个交互流程如下:

  1. 客户端向服务端发送请求
  2. 服务器将具体的请求信息发送给Servlet
  3. Servlet根据自身的业务逻辑生成响应内容传递给服务器,响应动态生产,一般取决于客户端的请求
  4. 服务器将响应返回给客户端

一眼看去好像服务器才是中间站一样,其实不然,由于Servlet是运行在Servlet容器中的,说到这那什么是Servlet容器呢?其实Servlet容器是一个运行在服务器中的Java程序,用于装载并管理Servlet对象的容器.

Servlet容器与Web Server处理请求

image

上图更能形象的体现出Servlet处理请求的步骤


Servlet生命周期

Servlet接口中定义了三个方法:

  1. init:初始化Servlet对象
  2. Service:响应客户端的请求
  3. destroy:销毁Servlet对象,释放资源

后期在写练习时应该注意到启动Servlet时,我们的逻辑代码一般在重写Service() doGet() doPost() 方法中,并没有向往常普通Java练习一样在main()方法中运行逻辑代码,在Servlet中逻辑代码是由Servlet容器负责调用。

Servlet生命周期的四个阶段:

  1. 类加载过程
  2. init()初始化过程,初始化Servlet对象
  3. service()服务过程,选择调用doGet\doPost
  4. destroy()销毁过程,释放资源

1、类加载

Servlet容器(Tomcat)负责加载Servlet,当Servlet容器启动或者第一次使用Servlet的时候Servlet容器会读取web.xml中用户指定的Servlet位置,并创建Servlet实例,成功加载后,Servlet容器会通过反射的方式去对指定的Servlet进行实例化

水平有限留个坑以后补。。。

2、init()初始化

Servlet对象被创建时会调用init()\init(ServletConfig config)用来初始化,ServletConfig对象中包含了该Servlet初始化的配置信息,比如初始化页面编码参数

	@Override
	public void init(ServletConfig config){
		super.init();
		String encoding = config.getInitParameter("encoding");// 读取init-parma中name=encoding的init-value
		System.out.println(encoding);
	}

	@Override
	public void init(){
		String encoding = this.getSerlvetConfig().getInitParameter("encoding");
		System.out.println(encoding);
	}

配置Servlet初始化参数

	<servlet>
		<servlet-name>xxx</servlet-name>
		<servlet-class>com.lyl.Xxx<servlet-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8<param-value>
		</init-param>
	</servlet>

但是init()只会在该Servlet对象初始化的时候调用一次,多次的访问刷新也不会再次触发init方法,通常用来初始化一些Servlet配置参数。

servlet中的配置参数分为两种:<init-parma><context-parma>

相同点:

  1. 都是用于逻辑代码中从web.xml中读取的配置参数标签
  2. 两者的结构都类似于Map,以key-value的形式保存配置参数

区分点:

  1. <init-param>的作用域在自身标签所在的那个Servlet范围中,而<context-parma>的作用域在ServletContext类中(ServletContext是域对象),整个Web应用中只有一个ServletContext,也就是说这个标签不属于任何一个Servlet,但是任何Servlet都能直接访问这个标签中的值(前提是Servlet在这个Servlet容器中)

初始化init()方法的具体行为

  1. Servlet容器启动时,根据web.xml中的配置信息,构造指定的Servlet对象,并将该Servlet中的初始化配置参数(<init-parma>)传入ServletConfig对象中,传递给init()方法调用,而init()方法是在Servlet实例化之后由Servlet容器调用。

3、service()

只要客户端向Servlet发送请求,如果被服务器通过,请求都会被Service()判断是什么类型的请求在对其进行逻辑处理并通过服务器响应给客户端。由于Servlet容器默认是单例设计模式(前提是Servlet没有被部署在分布式环境中),在Web服务器中如果有多个线程对同一个Servlet发送请求,这个时候Servlet容器只会实例化一次该Servlet对象,也就是说该Servlet实例是多线程共享资源,在并发编程时Servlet是线程不安全。

4、destroy()

看过Servlet生命周期的都知道Servlet被销毁时,这个方法必然被调用,但是我在学习Servlet的时候,看到后面发现源码中的destroy()中没有具体的实现方法,之前我也认为destroy()执行之后Servlet会被销毁,于是我在service()中手动的调用了destroy()方法,刷新servlet映射的url发现依然可以对Servlet发送请求,并且destroy也在被重复调用,于是上网查阅资料,发现我之前将destroy()的调用和Servlet的销毁关系弄反了,Servlet的生命周期由Servlet容器全权负责,对应的Servlet销毁的时候Servlet容器会自动的调用destroy(),而手动的调用并不会去销毁Servlet,我目前只能这样去理解,没有去深入了解(水平不到家。。。)这也是我们新手对于Servlet生命周期比较容易误解的一个地方吧!

其实Servlet就是一个接口,是Java的一个规范,单纯的只有5个方法而已,Servlet并不与客户端直接交互,与客户端直接交互的是Servlet容器,比如常用的Tomcat就是其一,而Servlet容器才是负责管理Servlet的,这一点一定要注意,不用将Servlet想象的很难,日常开发中大多数是基于流行框架,而学习Servlet的目的是为了帮助我们日后能更好更快的理解基于Servlet搭建的流行框架


Request/Response

在Servlet中前期练习应该都是用HttpServletRequest对象,点开源码查看该对象实现了ServletRequest接口,那么同理后面的HttpServletRequest也是一样实现了ServletResponse,这里就不讲最基础的一些什么请求头、响应行。。。这些组成和方法了,比竟这里还是自己需要理解的,下面主要讲讲我在前期练习中碰到的一些问题,主要还是讲讲HttpServletRequset

response(响应)

让我在Servlet中用HttpServletResponse对象写响应,我是很不情愿的,并不是很难,写过demo的同学应该都晓得太繁琐了,大量的输出代码携带HTML格式,太繁琐,这就是后面为什么要学习JSP,Servlet用来处理业务逻辑代码,JSP负责面向客户端显示,我在学习前期response时并没有遇到什么难的问题,但是并不代表没有,可能我只是前期学习阶段没有遇到。

request(请求)

通过查看HttpServletRequest的源码得知里面封装了很多方法,getMethod() getHeader() getCookies() getRemoteUser()...这些方法似乎前期学习中用的比较少,也就getMethod()稍微多点,后期学习了会话会学HttpSession,那么我们再进去ServletRequest的源码看看:

  • getParameter() :用于根据name属性获取表单请求的数据
  • setCharacterEncoding() :设置表单请求的数据编码格式,只对post请求有用,get无用

还有很多方法,需要自己查看API结合实际练习理解。

看到源码后面有一个getAttribute()setAttribute()这两个方法,细心的同学应该会注意到这两个方法之前学习Servlet的时候遇到过,在学习ServletContext对象的时候遇到过吧,这里补充一个知识点:Java的域对象

什么是域对象?

简单来说就是该对象本身可以存储一个域(一定范围)内的所有数据,我们就可以通过这个域对象来对对象内的数据进行增删改查,这里就不作详解,因为自己也不是特别的深入了解,可以参考Java四大域对象,一定要多参考API和实践,前期可以不去深入理解但是一定要有一个大概,帮助我们后期学习。

而这里讲的就是ServletRequest域,那么现在我们就可以通过request去携带我们想要的共享的数据给其他Servlet或者JSP,但是同样,域也是分作用范围的,其实简单地说我们共享的其实就是这个携带了我们数据的request对象嘛!那么我们又该如何去携带这个request对象给其他Servlet或者JSP呢?后面要学到Servlet的转发与重定向,最终实现的效果就是跳转。

在其他拿到了该request对象的Servlet\JSP中,我们就可以通过该对象的getAttribute()方法根据key来得到对应的value,实现数据共享,这样在一些特定的代码中就显得非常的方便,例如后台的Servlet处理了前台的数据,现在需要返回一个处理后的结果给JSP,那么通过request域就可以很方便的拿到传来的结果,当然方法多种多样,这里只是举一个例子,具体业务需要具体分析。