Servlet学习笔记
Posted WWWYC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet学习笔记相关的知识,希望对你有一定的参考价值。
时间:2016-11-9 14:49
——JavaWeb三大组件
Servlet Listener Filter
——JavaEE十三种核心技术
JavaEE是一个开放的平台,它包括的技术很多,主要包括十三种核心的技术。
1、JDBC
JDBC API是一个以统一的方式访问各种数据库,与ODBC类似,JDBC将开发者和私有数据库之间的问题隔离开来,由于它建立在Java语言之上,因此JDBC可以提供与平台无关的数据库访问。
ODBC:
开放数据库连接(Open Database Connectivity,ODBC)是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API(应用程序编程接口)。这些API利用SQL来完成其大部分任务。ODBC本身也提供了对SQL语言的支持,用户可以直接将SQL语句送给ODBC。开放数据库互连(ODBC)是Microsoft提出的数据库访问接口标准。开放数据库互连定义了访问数据库API的一个规范,这些API独立于不同厂商的DBMS,也独立于具体的编程语言(但是Microsoft的ODBC文档是用C语言描述的,许多实际的ODBC驱动程序也是用C语言写的。)ODBC规范后来被X/OPEN和ISO/IEC采纳,作为SQL标准的一部分。
JDBC定义了4种不同的驱动,具体来说包括:
1)JDBC-ODBC桥
在JDBC刚出现时,JDBC-ODBC桥是非常有用的,通过它,开发者可以使用JDBC来访问一个ODBC数据源。缺点是,它需要在客户机器上安装一个ODBC驱动,该驱动通常是运行在Windows操作系统的,使用这一类的驱动器,就失去了JDBC与平台无关的优势。此外,ODBC驱动器需要客户端的管理。
2)JDBC-native驱动桥
JDBC-native驱动桥提供了一个建立在本地数据库驱动上的JDBC接口,没有使用ODBC。JDBC驱动将标准的JDBC调用转变为对数据库API的本地调用,使用该驱动也会失去JDBC跨平台的优势,并且需要安装客户端的本地代码。
3)JDBC-network桥
JDBC-network桥不需要客户端的数据库驱动,它们使用网络-服务器中层来访问一个数据库,这会引出诸如负载均衡、连接池等技术,数据缓冲也是可能的。由于该驱动通常可能带来相对较小的下载时间,并且它是跨平台的,不需要客户端的安装和管理,因此很适合用作Internet的应用程序。
4)纯Java驱动
该驱动使用纯Java数据库驱动来提供直接的数据库访问。由于该驱动运行在客户端,并且直接访问数据库,因此运行在这个模式上要使用一个两层的体系。要在一个N层的体系中使用该驱动,可以通过一个包含有数据访问代码的EJB,并且让该EJB为它的客户提供一个数据库无关的服务。
桥:
JDBC和ODBC,充当的就是桥的角色。
2、Java命名和目录接口(Java Naming and Directory Interface, JNDI)
JNDI是Java Naming and Directory Interface的简写,意思是:Java命名及目录接口,它是为了对高级网络应用开发中使用的目录基础结构的访问。实际上这个目录是一个特殊的数据库,提供了对存储数据的快速访问,不像传统的目录服务访问方式,必须提供不同的API接口去访问不同的目录服务,如LDAP,NIS,ADS等,而它提供了一种标准的API来访问类型不同的目录。据说,使用完整的SDK可以开发哪些JNDI还不支持的目录服务提供者。
JNDI是JavaEE的一个API,提供了一套标准的接口,以定位用户、机器、网络、对象以及服务。
例如:可以使用JNDI来定位内部网络中的一台打印机,也可以使用它来定位Java对象或连接到一个数据库。
JNDI可以用于EJB、RMI-IIOP、JDBC中。它是网络查找定位的标准方法。
JNDI API被用来访问命名和目录服务,它提供了一个相容的模式来访问和操作企业范围大的资源,例如一个应用服务器中的DNS、LDAP、本地文件系统或者对象。
在JDNI中,一个目录结构中的每一个节点被称为context,每一个JNDI的名字都是一个与context相对的,没有一个绝对名字的概念,一个应用可以使用InitialContext类来得到它的第一个context。
Context ctx = new InitialContext();
通过这个初始的context,应用就可以经过目录树定位到需要的资源或者对象。例如,已经在WebLogic Server中配置了一个EJB,并且在myApp.myEJB中绑定了home接口,EJB的客户端在得到这样一个初始的context后,就可以使用以下的代码来定位到home接口了:
MyEJBHome home = ctx.lookup("myApp.myEJB");
一旦得到所需对象的引用(在这个例子中,就是EJB的home接口),然后就可以调用它的方法了,为了一个在context中查找到一个对象,JNDI还提供了一个方法:
插入或者绑定一个对象到一个context中,在配置一个EJB时,这是非常有效的方法:
从context中移除一个对象。
列出context中的所有对象。
创建和删除subcontexts。
3、企业 Java Beans(Enterprise Java Beans, EJB)
随着Hibernate的兴起,EJB的地位受到了挑战。
EJB3.0是一个轻量级的开发工具。
JavaEE中一个引人注目的技术是EJB,它提供了一个架构来开发和配置到客户端的分布式商业逻辑,因此可以明显减少开发的扩展性、高度复杂企业应用的难度。EJB规范定义了EJB组件应该如何及何时与它们的容器交互。由容器来负责提供普通的服务,例如目录服务、事务管理、安全、资源池和容错。
EJB规范定义了三类基本的bean:
1)会话beans(session beans):
会话beans为业务流程建模,由于它们通常表示执行某个动作,因此可以把它们当做是动词,这个执行的动作可以是任何事情,例如增加数量,访问数据库,调用其它系统,调用其他企业的Bean。我们可以举出很多例子,包括一个计价引擎,一个工作流引擎,一个目录引擎,一个信用卡认证中心或者一个网上证券交易引擎。
2)实体beans(Entity beans):
这是持久保存数据的代表,数据对象存储在数据库中,因此在服务器崩溃后数据仍然存在,多个客户端可以使用EJB来表示同样的数据。实体beans为企业数据建模,由于它们表示数据对象(就是缓存数据信息的Java对象),因此可以把它们当做名词。
实体beans的例子包括一种产品、一项订单、一张信用卡或一只股票。会话beans典型的方式是通过实体beans来实现业务目标的。例如一个证券交易引擎(会话beans)处理股票(实体beans)。
3)Message-Driven beans:
消息bean为了支持异步开发模式而产生的。
Message-Driven beans也表示动作,这一点上它类似于会话beans,它们之间的不同点在于你只能够发送消息给Message-Driven beans的方式来调用它们。Message-Driven beans的例子包括了接收股票交易信息的beans,信用认证消息或工作流消息。这些Message-Driven beans也可以调用其他的企业beans。
无状态和有状态:
无状态的beans(Stateless beans)是一个单一使用的服务,不维护任何的状态,在服务器崩溃时也会消失,而且生存周期也相对较短,例如一个无状态的 session beans 可能用作执行温度转换。
有状态的beans提供了一个传统的与客户端交互的方法,存储客户端的状态。在线购物车就是这样一个有状态的session beans 的典型例子。有状态的session beans在服务器崩溃时也会消失,而且生命周期也相对较短,并且每个实例只可以用在一个单一的线程中。
4、Java Server Pages(JSP)
或许你已经对微软的Active Server Pages(ASP)非常熟悉,JSP也是类似的技术,不过JSP是跨平台的,他们都是来帮助web开发者使用相对较少的代码就可以开发动态网页。即时web设计者不懂得编程,也可以使用JSP来开发动态的网页。JavaServerPages是html代码和Java代码的混合,在客户请求页面的时候,服务器就会处理Java代码,然后返回HTML页面给浏览器。
你可能也听过JHTML,它是一个旧的标准,现在已经被JSP取代了。
WebLogic Server不但支持JSP,还支持JHTML,不过,在默认设置下,WebLogic Server是不支持JSP的(对于5.1版本),必须编辑weblogic.properties来激活web服务器,对于JSPServlet来说,也是这样。
5、Servlet
Servlet是JSP的基础。
Java --> Servlet --> JSP
Servlet以Java为基础,JSP以Servlet为基础。
Java本身并不支持Web开发,Servlet的出现使得Java能够对Web进行开发,又因为Servlet做界面比较困难,所以出现了JSP。
Servlet提供的功能大部分与JSP相同,不同的是:JSP中大部分是HTML代码,其中只有少量的Java代码,而Servlet则相反,它完全使用Java代码编写,并且生成HTML代码。
Servlet是一个在服务器上运行的Java程序,它可以扩展Web服务器的功能。这些服务器端的应用可以在被请求时动态执行,与传统Web服务器上的CGI、Perl脚本差不多,CGI脚本和Servlet的一个主要区别是:CGI脚本对于每次请求都启动一个全新的进程,需要额外的系统开销;而Servlet的执行只要在Servlet引擎内启动一个独立的线程就可以了,因此Servlet的扩展性也更好。
在开发Servlet时,通常都要扩展javax.servlet.http.HttpServlet类,并且覆盖它的一些方法,主要包括:
servlce():作为command-specific方法的一个调度程序。
doGet():处理一个来自客户的HTTP GET请求。
doPost():处理一个来自客户的HTTP POST请求。
还有一些其他的方法来处理不同的HTTP请求——可以参考HttpServlet API的文档来得到更多相关的信息。
6、Java IDL / CORBA
通过Java的IDL支持,开发者可以将Java与CORBA集成。它们可以创建能配置在一个CORBA ORB中的Java对象,也可创建作为配置在其他ORB内的CORBA对象客户端的Java类。对于通过Java将你的新应用和以前的系统集成,后者提供了一个另外的方法。
7、Java事务体系(JTA) / Java事务服务(JTS)
JTA定义了一个标准的API,应用可以通过它来访问事务监控器。
JTS是CORBA OTS事务监控器的一个基本实现。JTS指定了一个事务管理器的实现(Transaction Manager),这个管理器在一个高级别上支持Java事务API(JTA)规范,并且在一个低级别上实现了OMG OTS规范的Java映射。一个JTS事务管理器为应用服务器、资源管理器、Standalone应用和通信资源管理器提供事务服务。
8、JavaMail和JavaBeans激活架构(JavaBeans ACtivation Framework, JAF)
JavaMail是一个用来访问邮件服务器的API。JavaMail API提供了一套抽象类来模型化一个邮件系统,支持SMTP和IMAP服务器。
JavaMail通过使用JavaBeans Activation Framework(JAF)来处理MIME加密的邮件附件。MIME字节楼和Java对象间可以互相转化。大多数的应用无需直接使用JAF。
9、Java信使服务(Java Messaging Service, JMS)
JMS往往配合EJB中的消息bean使用。
JMS是一个用来和面向信息的中层通信的API,它不但支持点对点的域,也支持发布 / 订阅域,并且提供对担保信息传送、事务信息传送、持久信息和durable subscribers的支持,对于将应用和以前的backend系统集成,JMS提供了另外一个方法。
10、扩展标记语言(Extensible Markup Language,XML)
XML是一个用来定义其他标记语言的语言,它可被用于商业之间的数据共享。XML的发展是与Java分开的;不过,它的目标和Java类似,都是为了解决跨平台性,通过将Java与XML结合,可以得到一个完全跨平台的解决方案。多个公司都为了在Java和XML之间开发一个紧密的集成而工作,具体的信息,可以浏览Sun公司网站的Java-XML部分。
——JavaWeb开发体系
中级开发:
Java(基础语言)
JDBC(数据库编程)
mysql(数据库)
HTML CSS javascript(静态Web开发)
XML
Servlet JSP(动态Web开发)
高级开发:
jQuery
Ajax
Struts2
Spring
Hibernate
——Servlet概述
一、什么是Servlet
Servlet是JavaWeb的三大组件之一,它属于动态资源,Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
接受请求数据
处理请求数据
完成响应
例如客户端发出登录请求,这些请求都应该由Servlet来完成处理,Servlet需要我们自己来编写,每个Servlet必须实现javax.servlet.Servlet接口。
二、实现Servlet的方式
实现Servlet有三种方式:
1、实现javax.servlet.Servlet接口
2、继承javax.servlet.GenericServlet类
3、继承javax.servlet.http.HttpServlet类
通常我们会去继承HttpServlet类来完成我们的Servlet,但学习还要从javax.servlet.Servlet接口开始学习。
GenericServlet实现Servlet接口,HttpServlet继承GenericServlet抽象类。
Servlet接口源代码:
public interface Servlet
{
//得到ServletConfig对象
public ServletConfig getServletConfig();
//该方法是服务方法,我们的业务逻辑代码写在该方法内
//调用该方法时得到Servlet的配置信息
public String getServletInfo();
}
1、如何让浏览器访问Servlet
1)给Servlet指定一个Servlet路径(让Servlet与一个路径绑定在一起)
<url-pattern>
2)浏览器访问Servlet路径。
2、如何给Servlet配置Servlet路径
web.xml
<servlet>
<servlet-name>XXX</servlet-name> //name可以随便写,只要上下对应即可。
<servlet-class>com.wyc.servlet.XXServlet</servlet-class> //不能加.java扩展名,不能加斜线,因为该路径会被ClassLoader调用,ClassLoader的路径不能加斜线。
</servlet>
<servlet-mapping>
<servlet-name>XXX</servlet-name>
<url-pattern>/aa/bb/XXServlet</url-pattern> //路径可以随便写,并且必须写 /
<!--servlet-name给该Servlet取名,名字可以自己定义,默认就使用该Servlet的名字-->
<!--servlet-class要指明该Servlet放在哪个包下,形式是包/包/...类/ (无.java)-->
<!--两个servlet-name的名称要相同-->
<!--Web服务器先看url-pattern的名称,也就是访问该Servlet的资源名部分-->
分析可能出现的错误
①<servlet-name>hahahaha</servlet-name>,名字不一样,会在启动Tomcat的时候报错。
②<servlet-class>com.wyc.hahaha</servlet>,如果加上.java扩展名,会出现500错误。
③写错资源名:http://localhost:8080/wycweb/资源名 也就是<url-pattern>,会出现404错误。
3、Servlet特性:
Servlet是单例的,一个类只有一个对象。
是线程不安全的,效率高。
Servlet类由我们来写,但对象由服务器来创建,并且由服务器通过getMethod()方法来调用对应的方法(doGet() doPost())。
定义所有servlet都必须实现的方法。
此接口定义了初始化servlet的方法,为请求提供服务的方法和从服务器移除servlet的方法。
这些方法称为生命周期方法,它们是按照以下顺序调用的:
1、构造servlet,然后使用init方法将其初始化。
2、处理来自客户端的对service方法的所有调用。
3、从服务中移除servlet,然后使用destory方法销毁它,最后进行垃圾回收并终止它。
除生命周期方法外,此接口还提供了getServletConfig方法和getServletInfo方法,Servlet可以使用getServletConfig()方法获得任何配置信息,而getServletInfo()方法允许Servlet返回有关其自身的基本信息,比如作者、版本等。
方法摘要:
public void destory()
由Servlet容器调用,指示将从服务器中移除该servlet。
此方法仅在Servlet的service方法已经退出或者在超过了时期之后调用一次,在此方法调用之后,Servlet容器不会再对此Servlet调用service方法。此方法为Servlet提供了一个清除持有的所有资源(比如内存、文件句柄和线程)的机制,并确保任何持久状态都与内存中该servlet的当前状态保持同步。
该方法不会销毁Servlet,而是释放所有资源,然后通过JVM的垃圾回收机制来销毁Servlet。
public void init(ServletConfig config) throw ServletException
由servlet容器调用,指示将该Servlet放入服务。
Servlet容器仅在实例化Servlet之后调用init方法一次。
在Servlet可以接收任何请求之前,init方法必须成功完成,否则无对象,无法完成请求响应。
出现以下情况时,Servlet容器无法将Servlet放入服务:
1)抛出ServletException。
2)未在Web服务器定义的时间段内返回。(超时)
config:包含Servlet的配置和初始化参数的ServletConfig对象。
public void service(ServletRequest req,ServletResponse res) throws ServletException,java.io.IOException
由Servlet容器调用,以允许Servlet响应某个请求。
此方法仅在servlet的init()方法成功完成后调用。(也就是说,首先要保证内存中存在Servlet对象)
应该为抛出或发送错误的Servlet设置相应的状态代码。
Servlet通常运行在可同时处理多个请求的多线程Servlet容器中,开发人员必须知道要同步对所有共享资源(比如文件、网络连接以及servlet的类和实例变量)的访问。
req:包含客户端请求的ServletRequest对象。
res:包含Servlet的响应的ServletResponse对象。
public ServletConfig getServletConfig()
该方法会在init()方法之后被调用。
返回ServletConfiguration对象,该对象包含此Servlet的配置信息和启动参数,返回的ServletConfig对象是传递给init方法的对象。此接口的实现负责存储ServletConfig对象,以便此方法可以返回对象,实现此接口的GenericServlet类已经这样做了。
return:初始化此Servlet的ServletConfig对象。
public String getServletInfo()
返回有关Servlet的信息,比如作者、版本等。
此方法返回的字符串应该是纯文本,不应该是任何种类的标签(比如HTML、XML等)。
return:包含servlet的信息。
生命周期方法全部由Servlet容器进行调用。
四、Servlet生命周期
1、发送http请求。
2、服务器解析主机。
3、服务器解析Web应用。
4、服务器解析资源名(HTML文件)。
5、定位Servlet文件所在包。
6、在定位到.servlet文件所在包后,使用反射机制,创建实例。
7、init()方法将该实例装载到内存(该方法只调用一次)。实际上Servlet就是单例。
8、Web服务器把接收到的请求封装成Request对象,作为service方法的参数传入service方法,该方法会被调用多次,每访问一次(也就是每发送一次请求)Servlet,它的service就会被调用一次。
9、在service方法中获取response对象(该对象有各种信息)。
10、分解response对象信息,形成http响应格式信息。
11、返回请求。
12、在某些情况下(三种),Web服务器回去调用该Servlet的destroy方法,将该Servlet销毁。
关闭服务器,重新加载Web应用,关闭电脑。
请简述Servlet的生命周期(工作流程)
Servlet程序由Web服务器调用,Web服务器收到客户端的Servlet访问请求后:
1、Web服务器首先检查是否已经装载并创建了该Servlet的实例对象,如果已经创建,则直接执行第4步,否则执行第2步。
2、装载并创建该Servlet的一个实例对象。
3、调用Servlet实例对象的init()方法。
4、创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象。然后调用Servlet的service()方法并将请求和相应对象作为参数传递进去。
5、Web应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法销毁该Servlet。
以下情况会导致调用destroy()方法:重启Tomcat、reload该Web应用、重启电脑。
——ServletConfig接口
ServletConfig中包含的是Servlet的配置信息。
当Servlet执行时,对应的web.xml中的信息会被加载到内存。
也就是说,ServletConfig对象对应的是Servlet的web.xml文件信息。
方法摘要:
String getServletName()
返回的就是<servlet-name>XXX</servlet-name>的值。
并不提供获取Servlet类的方法,因为JDK认为这样是不安全的。
ServletContext getServletContext()
获取Servlet上下文对象,可以理解为当前项目对象。
String getInitParameter(String name)
通过名称获取指定初始化参数的值。
Enumeration getInitParameterNames()
获取所有初始化参数的名称。
因为ServletConfig中的信息都来自于web.xml,所以可以直接获取xml文件中的信息。
<servlet>
<servlet-name>XXServlet</servlet-name>
<servlet-class>com.wyc.servlet.XXServlet</servlet-class>
<init-param>
<param-name>p1</param-name>
<param-value>v1</param-value>
</init-param>
<init-param>
<param-name>p2</param-name>
<param-value>v2</param-value>
</init-param>
</servlet>
如果想要获取v1的值,可以使用getInitParameter(String name)来获取。
如果想要获取v1和v2的值,可以使用getInitParameterNames()来获取所有参数的名称。
获取初始化参数:
——GenericServlet概述(抽象类)
这个类的存在使得编写Servlet更加方便。它提供了一个简单的方案,这个方案用来执行有关Servlet生命周期的方法以及在初始化时对ServletConfig对象和ServletContext对象进行说明。
GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。
该类提供了一个无参init()方法,用来执行子类特有的一些代码,不能覆盖有参init(ServletConfig servletConfig)方法,因为有参init方法中有如下代码:this.config = servletConfig;
无参init()方法会由有参init()方法调用。
——HttpServlet
方法摘要:
doGet(HttpServletRequest request,HttpServletResponse response)
接受get请求。
doPost(HttpServletRequest request HttpServletResponse response)
接受post请求。
void service(ServletRequest request, ServletResponse response)
这是一个生命周期方法,当接收到请求后,首先会调用该方法。
将两个参数强转为两个支持http相关协议的参数类型。
再调用本类的service(HttpServletRequest request,HttpServletResponse response)方法。
protected service(HttpServletRequest request,HttpServletResponse response)
接收强转后的支持http协议的参数。
它会通过request.getMethod()得到当前请求的请求方式,例如get请求或post请求。
再根据请求方式调用doGet()或者doPost()
doGet()和doPost()方法需要我们自己重写。
ServletConfig源代码
//该方法会在init()方法之后被调用
public String getServletInfo()
public void init(ServletConfig config) throws ServletException
//每次处理请求都会被调用。
——通过继承HttpServlet开发Servlet
1、通过继承HttpServlet开发Servlet需要重写doGet()和doPost()方法。
2、表单提交数据get请求和post请求的区别:
1)从安全性看get请求的安全性小于post的安全性,get请求提交的数据会在浏览器的地址栏显示。
2)从提交的内容大小看,get请求提交的内容数量小于post请求,get提交的数据不能大于2KB,而post提交的数据理论上不受限制,但是实际开发中建议不要大于64KB。
3)从请求响应速度来看,get请求响应速度大于post请求,get要求服务器立即处理请求,而post请求可能形成一个请求队列。
——在MyEclipse中开发Servlet
MyEclipse可以开发JSP、Servlet、Struts2、Hibernate、Spring
一、在MyEclipse中新建一个Web Project工程,MyEclipse会自动创建下图所示目录结构:
HaHa:
Web工程的名称,该工程部署时,在webapps目录下就会创建一个HaHa的Web应用。
src:
Java程序的开发目录,该目录下编写的所有程序在部署时,会自动部署到HaHa/WEB-INF/classes目录下。
WebRoot:
WebRoot对应Web应用的根目录,该目录下的所有子目录和子文件在部署时,会原封不动的发布到Web应用目录下。
开发步骤:
1、建立一个Web工程。
2、在src目录下建立一个包。
3、右键单击包创建一个Servlet文件。
选中doGet()和doPost(),然后点击Next。
选中:Generate/Map web.xml file(创建XML配置文件),单击Finish。
4、配置Tomcat
5、发布(部署):实际上就是将Web资源拷贝到webapps中。
MyEclipse基本配置:
6、window——Preferences——General——Editors——File Association——JSP——MyEclipse JSP Editor(设置为default)
7、搜索jsp,将编码设置为UTF-8
8、右键Tomcat,选择Configure Server Connector,选择对应服务器的Launch,选择Run mode。
9、删除MyEclipse Derby
General——Startup and Shutdown——MyEclipse Derby——重启MyEclipse
10、如果是Windows7,还需要修改字体:
C:\Windows\Fonts,找到Courier New,鼠标右键——显示
Ceneral——Appearance——Colors and Fonts——Basic——Text Font——Edit——选择Courier New字体
——Servlet接口实现类
一、对于Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet类。
二、HttpServlet指能够处理HTTP请求的Servlet,它在原有Servlet接口上添加了一些与HTTP协议处理相关的方法,它比Servlet接口的功能更为强大,因此开发人员在编写Servlet时,通常应该继承这个类,而避免直接实现Servlet接口。
三、HttpServlet在实现Servlet接口时,复写了service方法,该方法体内的代码会自动判断用户的请求方式,调用对应的doGet
或doPost方法,因此开发人员在编写Servlet时,通常只需要复写doGet或doPost方法,而不需要复写service方法。
——Servlet的一些细节
一、Servlet与线程安全
因为一个类型的Servlet只有一个实例对象,那么就有可能会出现一个Servlet同时处理多个请求的情况,那么Servlet是否为线程安全的呢?答案是:不是线程安全的。这说明Servlet的工作效率很高,但是同时也存在线程安全问题。
所以我们不应该在Servlet中轻易创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,而另一个线程对这个成员变量进行读操作。
解决方法:
1、不要在Servlet中创建成员,创建局部变量即可,这样每个线程独有一份数据,互不影响。
2、可以创建无状态成员,例如仅仅用来输出或者是调用一下的方法等。
3、可以创建有状态的成员,但是状态必须为只读的,例如只提供get方法,不提供set方法。
因为Servlet是单例,因此会出现线程安全问题,比如:
售票系统,如果不加同步机制,则会出现问题。
同步原则:
1)如果一个变量需要多个用户共享(例如网站访问量或卖票等),则应当在访问该变量的时候加上同步代码块:
synchronized(this){ }
2)如果一个变量不需要共享,则直接在doGet()或者doPost()方法中定义,这样不会存在线程安全问题,因为是局部变量。
二、让服务器在启动时就创建Servlet
默认情况下,服务器会在某个Servlet第一次收到请求时创建它,也可以在web.xml文件中对Servlet进行配置,使服务器启动时就创建Servlet。
因为Servlet在第一次访问时会比较慢,所以可以在服务器启动时就完成Servlet的初始化。
需求:当网站启动的时候,可能会要求初始化一些数据(比如创建某些数据库临时表),或者完成一些定时任务(比如定时写日志、备份数据、发送邮件等)。
解决方法:可以通过配置<load-on-startup>和多线程来解决。
<load-on-startup>:通过配置<load-on-startup>,我们可以指定某个Servlet自动创建。
模拟一个定时发送邮件的功能:
设置该Servlet随着服务器的启动而加载,加载顺序为第一位,可以在该Servlet中开启一个线程,专门用于监控发送邮件。
参数从0开始,也可以不从0开始,执行顺序是按照从小到大执行。
当参数为-1或者不写时,默认不加载。
代码如下:
<servlet>
<servlet-name>XXServlet01</servlet-name>
<servlet-class>com.wyc.servlet.XXServlet01</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>XXServlet01</servlet-name>
<url-pattern>/XXServlet01</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>XXServlet02</servlet-name>
<servlet-class>com.wyc.servlet.XXServlet02</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>XXServlet02</servlet-name>
<url-pattern>/XXServlet02</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>XXServlet03</servlet-name>
<servlet-class>com.wyc.servlet.XXServlet03</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>XXServlet03</servlet-name>
<url-pattern>/XXServlet03</url-pattern>
</servlet-mapping>
三、<url-pattern>
<utl-pattern>是<servlet-mapping>的子元素,用来指定Servlet的访问路径,即URL,它必须是以"/"开头。
1、由于客户端是通过URL地址访问Web服务器中的资源,所以Servlet程序若想被外界访问,必须把Servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>标签和<servlet-mapping>标签完成。
2、<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称(Servlet的文件名)和Servlet的完整类名(包名+类名)。
一个已经注册的Servlet可以被多次映射,也就是说可以与多个url-pattern关联,代码如下:
<servlet>
也可以写成:
此时在地址栏中输入:http://localhost:8080/HaHaHa也可以访问LoginServlet了。
当映射一个Servlet的时候,可以映射多层,比如:
<url-pattern>/servlet/index.html</url-pattern>
还可以看出,后缀名是html的文件不一定就是HTML,可能是假象。
3、还可以在<url-pattern>中使用通配符,所谓通配符就是星号" * ",星号可以匹配任意URL前缀或者后缀,使用通配符可
以命名一个Servlet绑定一组URL,例如:
<url-pattern>/servlet/*</url-pattern>:/servlet/a 、 /servlet/b,都匹配/servlet/*(匹配路径)
<url-pattern>*.do</url-pattern>:/abc/def/ghi.do、/a.do,都匹配*.do(匹配扩展名)
<url-pattern>/*<url-pattern>:匹配所有URL。
需要注意得是,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符,例如:/*.do就是错误的,因为*出现在URL中间位置上,*.*也不对,因为一个URL中最多只能出现一个通配符。
注意,通配符是一种模糊匹配URL的方式,如果存在更具体的<url-pattern>,那么访问路径会去匹配更具体的<url-pattern>
通配符优先级:谁匹配的越多(*.do),谁的优先级越高,谁匹配的越少(/*),谁的优先级越低。
“ / ”:什么都能匹配,所以它的优先级最低。
4、同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子标签的设置值可以是同一个servlet的注册名。
5、在Servlet映射到URL中的时候也可以使用通配符(* ),但是只能有两种固定的格式,一种格式是“ * . 扩展名 ”:
<url-pattern>/*</url-pattern> 或 <url-pattern>*.html</url-pattern>
另一种格式是以斜线(/)开头,以(/*)结尾。代码如下
/* /news/*
6、对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题:
判断原则:
谁的匹配度高,谁就被调用。
*.do的优先级最低。
Servlet引擎就是Web服务器。
当请求URL为 " /abc/a.html "," /abc/* " 和 " /* " 都匹配,哪个Servlet响应?
Servlet引擎将调用Servlet1。
当请求URL为 " /abc " 时," /abc/* " 和 " /abc " 都匹配,哪个Servlet响应?
Servlet引擎将调用Servlet3。
当请求URL为 " /abc/a.do " 时," /abc/* " 和 " *.do "都匹配,哪个Servlet响应?
Servlet引擎将调用Servlet1。
当请求URL为 " a.do " 时," /* " 和 " *.do " 都匹配,哪个Servlet响应?
Servlet引擎将调用Servlet2。
当请求URL为 " /xxx/yyy/a.do " 时," /* " 和 " *.do " 都匹配,哪个Servlet响应?
Servlet引擎将调用Servlet2。
四、web.xml文件的“继承”
CATALINA_HOME\conf\web.xml文件中的内容,相当于写到了每个项目的web.xml文件中,它是所有web.xml文件的父文件。
<servlet-name>default</servlet-name>
它的优先级最低,如果一个请求没有Servlet来处理,那么它来处理,它显示404。
也就是说,当访问路径不存在时,会执行该Servlet。
在web.xml文件中有全部的MIME类型,可以通过扩展名来查询。
——Servlet与反射
当服务器获取到URL时,会截取Servlet路径与<url-pattern>进行匹配,如果匹配成功,找到对应的<servlet-class>,可以通过
Class c = Class.forName("完整类名");
来获取当前Servlet类的对象,然后再通过:
Method method = c.getMethod("service",ServletRequest.class,ServletResponse.class);
来获取到一个method方法对象,然后可以通过method.invoke()来完成对其调用。
通常情况下,只要和配置相关,就有反射机制。
——ServletContext
一个项目只有一个ServletContext对象。
可以在多个Servlet中获取这个唯一的对象,可以使用它来进行多个Servlet之间的数据传递。
一、ServletContext概述
服务器会为每个应用创建一个ServletContext对象。
1)ServletContext对象的创建是在服务器启动时完成的。
2)ServletContext对象的销毁是在服务器关闭时完成的。
也就是说ServletContext生命周期最长。
ServletContext对象的作用是在整个Web应用的动态资源之间共享数据,例如在AServlet中向ServletContext对象中保存一个值,然后在BServlet中就可以获取这个值了,这就是共享数据。
二、获取ServletContext
1、在ServletConfig中获取ServletContext对象:
在void init(ServletConfig config)中:
ServletContext context = config.getServletContext();
ServletConfig类的getServletContext()方法可以用来获取ServletContext对象。
2、在GenericServlet或HttpServlet中获取ServletContext对象:
GenericServlet类实现了ServletConfig接口,所以也具备getServletContext方法,所以可以直接使用this.getServletContext()来获取ServletContext对象。
3、在HttpSession中获取ServletContext对象:
ServletContext getServletContext();
4、在ServletContextEvent中获取ServletContext对象:
ServletContext getServletContext();
三、域对象的功能
域对象就是用来在多个Servlet中传递数据的,所以说所有的域对象都有存、取数据的功能。
ServletContext是JavaWeb的四大域对象之一:
PageContext
ServletRequest
HttpSession
ServletContext
所有的域对象都有存取数据的功能,因为域对象内部都有一个Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:
void setAttribute(String name,Object value)
用来存储一个对象,也可以称之为存储一个域属性。
需要注意的是,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的value,这一特性与Map集合相同。
Object getAttribute(String name)
通过name来获取ServletContext中的value,在获取之前需要先存储。
因为存入的是Object,所以取出时需要进行强制类型转换。
void removeAttribute(String name)
用来移除ServletContext中的域属性,如果参数name不存在,那么该方法什么都不做。
Enumeration getAttributeNames()
获取所有域属性的名称。
四、获取应用初始化参数
可以使用ServletContext来获取在web.xml文件中配置的应用初始化参数。
注意:应用初始化参数与Servlet初始化参数不同。
Servlet也可以获取初始化参数,但它是局部的参数,也就是说,一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个Servlet准备。
可以配置公共的初始化参数,也就是整个Web应用的参数,为所有Servlet而使用,这需要使用ServletContext才能获取。
<web-app ...>
<context-param>
<param-name>paramName01</param-name>
<prarm-value>paramValue01</param-value>
</context-param>
<context-param>
<param-name>paramName02</param-name>
<param-value>paramValue02</param-value>
</context-param>
</web-app>
获取参数方法:
当前Servlet获取value方法:this.getInitParameter("name");
ServletContext获取value方法:getServletContext().getInitParameter("name");
当指定name的value不存在时,返回null。
五、获取资源的相关方法
1、获取真实路径
可以使用ServletContext对象来获取Web应用下的资源,例如在HelloWeb应用的根目录下创建a.txt文件,现在想
在Servlet中获取这个资源,就可以使用ServletContext来获取。
1)获取a.txt的真实路径:String realPath = ServletContext.getRealPath("/a.txt");
realPath的值为:F:\tomcat7\webapps\HelloWeb\a.txt
2)获取b.txt的真实路径:String realPath = ServletContext.getRealPath("/WEB-INF/b.txt");
realPath的值为:F:\tomcat7\webapps\HelloWeb\WEB-INF/b.txt
getRealPath()方法可以获得带盘符的真实路径。
默认会获取WebRoot目录下的文件路径。
2、获取资源流
不止可以获取资源的路径,还可以通过ServletContext获取资源流,即把资源以输入流的方式获取:
获取a.txt资源流:InputStream in = ServletContext.getResourceAsStream("/a.txt");
获取b.txt资源流:InputStream in = ServletContext.getResourceAsStream("/WEB-INF/b.txt");
也可以通过realPath自己创建流。
3、获取指定目录下所有资源路径
还可以使用ServletContext获取指定目录下所有资源路径,例如获取/WEB-INF目录下所有资源的路径:
Set st = context.getResourcePaths("/WEB-INF");
System.out.println(st);
输出结果为:[/WEB-INF/lib/ , /WEB-INF/classes/ , /WEB-INF/b.txt , /WEB-INF/web.xml ]
注意:本方法必须以“ / ”开头。
只获得子目录,不获取孙目录。
六、访问量统计
当访问一个网站的时候,无论是哪个用户访问指定页面,都会累加访问量,所以这个访问量统计应该是整个项目共享的,很明显,这需要使用ServletContext来保存访问量。
逻辑分析:
最初访问页面时ServletContext中没有保存访问相关的属性。
当第一次被访问时,创建一个变量,设置其值为1,并保存到ServletContext中。
当以后访问时,就可以从ServletContext中获取这个变量,然后在其基础之上+1。
ServletContext application = this.getServletContext(); //获取ServletContext对象
Integer count = (Integer)application.getAttribute("count"); //获取ServletContext对象中的count属性
if(count == null)
count = 1; //如果ServletContext中不存在count属性,那么count的值为1,表示第一次访问。
else
count++; //如果ServletContext中存在count属性,说明以前被访问过,那么让count的值在原来基础上+1。
response.serContentType("text/htmllcharset=utf-8");
response.getWriter().print(count); //向客户端响应本页面被访问的次数。
application.setAttribute("count",count); //将count的值保存到ServletContext对象中。
只要服务器不关闭,则ServletContext一直存在。
七、获取类路径下的资源
Class
ClassLoader
这里所指的类路径下的资源,对于JavaWeb应用而言,就是获取classes目录下的资源:
/WEB-INF/classes和/WEB-INF/lib/下的jar包。
当项目发布到服务器后,src就相当于classes。
代码如下:
1、先得到Class,再得到ClassLoader
ClassLoader cl = this.getClass().getClassLoader();
2、调用其getResourceAsStream(),得到一个InputStream
InputStream input = cl.getResourceAsStream();(ClassLoader获取资源时,不能以斜线开头)
如果不使用ClassLoader,使用Class来获取路径,代码如下:
1、相对当前.class文件所在目录(不加" / ")相当于:G:\apache-tomcat-7.0.72\webapps\ServletDemo\WEB-INF\classes\www\wyc\ServletDemo.class
InputStream input = c.getResourceAsStream("a.txt");
2、相对于classes目录(加" / ")相当于:G:\apache-tomcat-7.0.72\webapps\ServletDemo\WEB-INF\classes
InputStream input = c.getResourceAsStream("/a.txt");
此时的路径是相对于class所在文件夹。
打印输出:
不能使用绝对路径,也就是不能带盘符。
ServletContext的getContextPath()可以获取项目根目录。
ServletContext的getRealPath()方法可以获取项目的真实目录。
——request(封装了客户端所有的请求数据)
服务器处理请求的流程:
服务器每次收到请求时,都会为这个请求创建一个新的线程。
服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体。
服务器还会创建response对象,这个对象与客户端连接在一起,它负责给客户端发送响应。
请求响应流程图
请求行
请求头
空行
请求体(GET请求无请求体,参数在URL中)
协议中的数据都可以通过request对象来获取。
1、获取常用信息
1)获取客户端IP地址
案例:封IP地址。
String getRemoteAddr()
String addr = request.getRemoteAddr();
2)获取HTTP请求方式
request.getMethod()
可能是POST也可能是GET
2、获取HTTP请求头
1)String getHeader(String name):适用于单值头
2)int getIntHeader(String name):适用于单值int类型的请求头
3)long getDateHeader(String name):适用于单值毫秒类型的请求头
4)Enumeration<String> getHeaders(String name):适用于多值请求头
需要使用链接才会出现防盗链。
案例:
1)通过User-Agent识别用户浏览器类型
String address = request.getRemoteAddr();//获取客户端的IP地址
String referer = request.getHeader("Referer");
System.out.println(referer);
if(referer == null || !referer.contains("localhost"))
response.sendRedirect("http://www.baidu.com");
else
System.out.println("hello world");
3、获取请求URL
1)String getScheme()
获取协议:http
2)String getServerName()
获取服务器名(主机名称):localhost
3)String getServerPort()
获取服务器端口:8080
4)String getContextPath()
获取项目名:/HelloWeb(带斜线)
使用它获取项目名,例如:<a href="项目名+servlet路径"></a>
5)String getServletPath()
获取Servlet路径:/AServlet(带斜线)
6)String getQueryString()
获取参数部分,即问号后面的部分:username=xxx&password=yyy
7)String getRequestURI()
获取请求URI(项目名+Servlet路径就是URI):/HelloWeb/AServlet
8)String getRequestURL()
获取请求URL(不包含参数的整个请求路径):http://localhost:8080/HelloWeb/AServlet
4、获取请求参数
请求参数是由客户端发送给服务器的,有可能是在请求体中(POST),也有可能是在URL之后(GET),获取方式完全相同。
请求参数:有一个参数一个值的,也有一个参数多个值的。
1)String getParameter(String name):获取指定名称的请求参数,适用于单值请求参数。
2)String[] getParameterValues(String name):获取指定名称的请求参数值,适用于多值请求参数。
3)Enumeration<String> getParameterNames():获取所有请求参数名称。
4)Map<String,String[]> getParameterMap():获取所有请求参数。
以上两种方法获取参数方式相同,但是处理编码问题的方式不同。
案例:
1)超链接参数
2)表单数据
5、请求转发和请求包含
重定向:多个请求,请求多个Servlet
请求转发:一次请求,包含多个Servlet
RequestDispatcher rd = request.getRequestDispatcher("/MyServlet");
MyServlet:被包含、被转发的Servlet路径。
使用request获取RequestDispatcher对象,方法的参数是被转发或包含的Servlet的路径。
请求转发:rd.forward(request, response);
请求包含:rd.include(request, response);
请求转发:URL路径不变。
重定向:URL发生变化。
有时一个请求需要多个Servlet协作才能完成,所以需要从一个Servlet跳到另一个Servlet来执行。
* 一个请求跨多个Servlet,需要使用转发和包含。
* 请求转发:由下一个Servlet完成响应体,当前Servlet可以设置响应头(保留请求头,不保留请求体,因为请求需要被转发)
* 请求包含:由两个Servlet共同完成响应体(保留请求头并且保留请求体)
* 无论是请求转发还是请求包含,都在一个请求范围内,使用同一个request和response。
forward请求转发:
------->>>>>转发
System.out.println("TwoSerlet...");
请求转发调用的是doGet方法还是doPost方法呢?
调用的是doGet方法
6、request域
Servlet中四大域对象:pageContext、request、session、application
只要是域对象,都有以下三个方法:
void setAttribute(String name, Object value)
Object getAttribute(String name)
void removeAttribute(String name)
同一请求范围内使用request、setAttribute()、request.getAttribute()来传值。
前一个Servlet调用setAttribute()来保存值,后一个Servlet调用getAttribute()来获取值。
请求参数不是域属性。
7、请求转发和重定向的区别
* 请求转发时一个请求一次响应,而重定向是两次请求两次响应。
* 请求转发地址栏不变化,而重定向会显示后一个请求的地址。(因为重定向是客户端发起的请求)
* 请求转发只能转发到本项目的Servlet,而重定向不止能重定向到本地项目的其他Servlet,还能定向到其他项目的Servlet,可以重定向到网络中任意URL。(重定向是浏览器执行)
请求可以只转发给同一应用程序上下文根中的组件,而不在应用程序之间转发。
转发是Servlet具体实现的,是服务器内部的转发。
重定向是浏览器发出的请求,包含requestURI,即包含项目名,可以访问其他服务器。
* 请求转发是服务器端的行为,只需给出转发的Servlet路径即可,而重定向需要给出requestURI,即包含项目名。
* 请求转发和重定向的效率谁高
请求转发效率高,因为只有一个请求。
如果需要地址栏发生变化,则必须使用重定向。
如果需要在下一个Servlet中获取request域的属性,必须使用请求转发。
——response(其类型是HttpServletResponse)
ServletResponse:与HTTP协议无关的类型。
HttpServletResponse:与HTTP协议相关的类型。
1)sendError(int sc):发送错误状态码,例如404,500
2)sendError(int sc,String msg):发送错误状态码,可以发送一个错误信息
3)setStatus(int sc) :发送正确的状态码,可以用来发送302
response.setHeader("aaa","AAA");
如果name相同,则设置第二个值时,会覆盖第一个值,此特性与Map集合相同,如果想要设置第二个值,需要使用addHeader()方法。
response.add("aaa","A");
response.add("aaa","AA");
执行此方法之前不需要执行setHeader方法。
response.setIntHeader("Content-Length",100);
response.setDateHeader("expires",1000*60);
long:表示时间的毫秒数
expires:过期时间,-1表示不缓存。
即使重定向代码被执行,后面的代码依然会被执行。
/**
// URL带IP(主机名),URI不带IP(主机名)
/**
//设置禁用缓存
<meta>标签可以代替响应头:
3、响应体
在ServletResponse中有两个方法
PrintWriter getWriter() throws IOException
获取字符流:PrintWriter writer = responsr.getWriter();
用来发送字符相关的数据。
异常:
IllegalStateException
非法状态异常,在非法或不适当的时间调用该方法时抛出的异常,换句话说,即Java环境或Java应用程序没有处于请求操作所要求的适当状态下而调用该方法,会抛出该异常。
异常条件:
当已经使用字节流时,再使用字符流,会抛出该异常。也就是说,字节流和字符流只能使用其一。
getOutputStream条件相同。
其父类是RuntimeException。
ServletOutputStream getOutputStream() throw IOException
获取Servlet输出字节流:ServletOutputStream out = response.getOutputStream();
使用这个字节流可以发送字节相关的数据。
4、重定向
方法一:设置302,设置Location,其中变化的只有Location,所以Java将该方法封装,完成重定向。
方法二:sendRedirect(String location);
response.sendRedirect("/day10_1/CServlet");
——编码
Tomcat8.0默认编码改为:UTF-8
常见字符编码:ISO-8859-1(不支持中文)、GB2312、GBK(系统默认编码,中国国标码)、utf-8(万国码,支持全世界的编码)
一、响应编码
1、在使用response.getWriter()来向客户端发送字符数据时,如果在之前没有设置编码,那么默认使用ISO-8859-1,因为ISO-8859-1不支持中文,所以一定会乱码。
2、在使用response.getWriter()之前可以使用response.setCharacter()来设置相应编码,当设置响应编码后,发送的全部字符都是通过指定编码进行编码的字符。
3、在使用response.getWriter()之前可以使用response.setHeader("Content-Type","text/html;charset=utf-8")来设置响应头,来告诉浏览器使用什么编码进行解码。相当于同时设置了:setCharacterEncoding("utf-8");
4、setHeader("Content-Type","text/html;charset=utf-8")的快捷方法是:setContentType("text/html;charset=utf-8");
二、请求编码
1、客户端发送给服务器的请求参数是什么编码
客户端首先要打开一个页面,然后在页面中提交表单或点击超链接,在请求这个页面时,服务器响应的编码是什么,那么客户端发送时请求的编码就是什么。
也就是说,当前页面的编码,是由上一个页面响应的编码来指定的。
所以,响应编码必须一致,才能保证不出现乱码的情况。
2、服务器端默认使用什么编码来解码参数
服务器端默认使用iso-8859-1来解码,所以一定会出现乱码,因为iso-8859-1不支持中文。
解码:字节 → 字符串
编码:字符串 → 字节
3、请求编码处理分为两种:GET和POST
GET请求参数不在请求体中,而POST请求参数在请求体中,所以它们的处理方式是不同的。
4、GET请求编码处理:
1)String username = new String(Request.getParameter("username").getBytes("iso-8859-1"),"utf-8");
因为服务器默认使用ISO-8859-1编码,所以使用ISO-8859-1编码进行解码。
2)在server.xml文件中配置UTIEncoding=utf-8
当设置编码之后,GET请求参数的编码就不需要手动设置了,默认为UTF-8。
但是不推荐使用,因为不灵活。
推荐使用如下代码:
String name = request.getParameter("name");
byte[] bytes = name.getBytes("ISO-8859-1"); //先使用iso-8859-1解码
name = new String(bytes,"utf-8"); //再使用utf-8编码
5、POST请求编码处理:
1)String username = new String(request.getParameter("iso-8859-1"),"utf-8"); //先得到参数,然后回退为字节数组,然后重新编码。
2)在获取参数之前调用request.setCharacterEncoding("utf-8");
代码如下:
//1、在获取参数之前,需要先调用request.setCharacterEncoding("utf-8");
三、URL编码
表单的编码类型:Content-Type:application/x-www-form-urlencoded,就是把中文转换成%+两位的16进制数。
为什么要用它呢?
在客户端和服务器之间传递中文时,需要把它转换成适合网络传输的方式。
如果直接传输中文,可能会出现丢失数据的情况,为了避免这种情况,所以先将中文转换成16进制数,再发送给服务器。
服务器可以自动识别URL编码。
1、它不是字符编码
2、它是用来在客户端与服务器之间传递参数用的一种方式。
3、URI编码需要先指定一种字符编码,把字符串解码后,得到byte[]数组,然后把小于0的字节+256,然后转换成16进制,再在前面加上一个%。
4、POST请求默认使用URL编码,Tomcat会自动使用URL解码
5、URL编码:String username = URIEncoder.encode(username,"utf-8");
字节 → URL编码(%A5....)
代码如下:
String name = "啊啊";
byte[ ] bytes = name.getBytes("utf-8");
System.out.println(Array.toString(bytes)): //该语句会将已经转换为字节数组的"啊啊"打印到控制台
String s = URLEncoder.encode(name,"utf-8"); //将字节数组按照utf-8的编码转换为URL编码
页面超链接中的中文必须URL编码,服务器不会自动编码,但是服务器会自动解码。
6、URL解码:String username = URLDecoder.decode(username,"utf-8");
URL编码(A5....)→ 字符串
代码如下:
s = URLDecoder.decode(s,"utf-8");
//该语句将URL编码转换为utf-8编码(也就是从%E2%C3%A0转到中文汉字)
如果使用的是GET请求,并且链接中有中文参数,需要使用URL进行编码。
——路径
建议使用绝对路径,绝对路径不受界面影响。
一、web.xml中<url-pattern>路径(Servlet路径)
要么以" * "开头,要么以" / "开头。
一般Servlet路径中不适用通配符,在使用过滤器的时候才会使用通配符。
二、转发和包含路径
以" / "开头:相对当前项目路径,例如:
http://localhost:8080/项目名/AServlet
request.getRequestDispatcher("/AServlet").forward(request,response);
如果不以" / "开头:相对当前Servlet路径名,例如:
访问同目录下的Servlet:
request.getRequestDispatcher("BServlet").forward(request,response);
三、重定向路径(客户端路径)
以" / "开头:相对当前主机,例如http://localhost:8080/,所以需要自己手动添加项目名。
例如:
response.sendRedirect("/HelloWeb/AServlet");
四、页面中超链接和表单路径
与重定向相同,都是客户端路径,需要添加项目名。
<form action="/HelloWeb/AServlet" ></form>
<a href="/HelloWeb/AServlet">
<a href="AServlet">:如果不以" / "开头,那么就是相对路径。
五、ServletContext获取资源路径
相对于当前项目目录,即WebRoot目录下。
getContextPath(),获取项目名。
六、ClassLoader获取资源路径
相对classes目录。(类路径)
当项目发布到服务器,src就相当于classes。
负责加载类,同时可以获取类路径下其他资源。
//得到类加载器,让类加载器去类路径下获取资源(ClassLoader获取资源时,不能以斜线开头)
s(new String(b, 0, length, "utf8"));
七、Class获取资源路径
以" / "开头:相对classes目录。
不以" / "开头相对当前.class文件所在目录。
//如果不写/,则会到当前Class文件所在的目录去加载
——BaseServlet
1、我们希望在一个Setvlet中可以有多个请求处理方法,而不是一个请求对应一个Setvlet。
2、客户端发送请求时,必须多给出一个参数,用来说明要调用的方法。
请求处理方法的签名必须与service相同,即返回值、参数以及声明的异常都相同。
3、客户端必须传递名为method()的参数
domain: User
dao: UserDao
setvice: UseService
servlet: UserServlet
生命周期方法:
void init(ServletConfig config)
void destory()
void service(ServletRequest, ServletResponse) throws IOException, ServletException {
在这里让同一个请求来调用其他方法。
要求:
用户发出请求时,必须给出一个参数,用来说明要调用哪一个方法。
// 获取参数,通过参数来确定要调用的方法
}
===============================================================================
4、TestServlet
* 2、判断参数指定的是哪一个方法,并调用
* 所以: 得到方法名称,然后通过反射来调用方法
* * 需要得到Class对象,然后调用该对象的方法得到Method对象
------------------------------------------------------------------------------------------------------------------------------
5、BaseServlet
如果想让所有的Setvlet都可以使用这种方法,可以创建一个抽象类:BaseSetvlet,使用BaseServlet中的service(HttpRequestServlet, HttpResponseSevlet)进行方法调用,即其他Servlet继承BaseServlet即可。
===============================================================================
6、BaseSetvlet进阶
Servlet常用语转发或重定向,可以使用BaseSetvlet来完成,只需要在指定方法调用结束后返回字符串即可,字符串格式:
"前缀:后缀";
前缀:确定转发还是重定向
后缀:确定路径
------------------------------------------------------------------------------------------------------------------------------
以上是关于Servlet学习笔记的主要内容,如果未能解决你的问题,请参考以下文章