1.Servlet的运行原理和流程:
1.http协议有多种请求方式,最常见的就是GET请求 和POST
2.GET请求是没有请求正文的,主要用户获取服务器的信息,是浏览器的默认请求方式
3.POST请求是有请求正文的,主要是用于浏览器向服务器提交数据
1.1 执行过程
1客户端发出请求http://localhost:8080/Demo1/abc
2根据web.xml文件的配置,找到<url-pattern>子元素的值“/hello”的<servlet-mapping>元素
3读取<servlet-mapping>元素的<servlet-name>子元素的值,由此确定Servlet的名字为”HelloServlet”
4找到<servlet-name>值为HelloServlet的<servlet>元素
5读取<servlet>元素的<servlet-class>子元素的值,由此确定Servlet的类名为cn.itcast.HelloServlet。
6到Tomcat安装目录/webapps/Demo1/WEB-INF/classes/cn/itcast目录下查找到HelloServlet.class文件
1.2 映射位置
1,同一个Servlet可以被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的设置值可以是同一个Servlet的注册名。
2,在Servlet映射到的URL中也可以使用通配符,但是只能有两种固定的格式:一种格式是“.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。
3.关联关系优先级
对于如下的一些映射关系:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
问题:
当请求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。
4.缺省配置
4.1 如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
4.2 凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。
4.3在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。
4.4 当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。
2.Servlet的生命周期
2.1 基本概念
Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
2.2 Servlet启动配置
如果在<servlet>元素中配置了一个<load-on-startup>元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
举例:
<servlet> <servlet-name>invoker</servlet-name> <servlet-class> org.apache.catalina.servlets.InvokerServlet </servlet-class> <load-on-startup>2</load-on-startup> </servlet>
用途:
为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。
3. ServletConfig对象
3.1 简介
在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。
当srvlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
常用的Api:
String getServletName() -- 获取当前Servlet在web.xml中配置的名字
String getInitParameter(String name) -- 获取当前Servlet指定名称的初始化参数的值
Enumeration getInitParameterNames() -- 获取当前Servlet所有初始化参数的名字组成的枚举
ServletContext getServletContext() -- 获取代表当前web应用的ServletContext对象
在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
这样做的好处是:如果将数据库信息、编码方式等配置信息放在web.xml中,如果以后数据库的用户名、密码改变了,则直接很方便地修改web.xml就行了,避免了直接修改源代码的麻烦。
3.2 基本使用
1.在web.xml中创建servlet的初始化配置
<servlet> <servlet-name>ServletConfigTest</servlet-name> <servlet-class>com.vae.servlet.ServletConfigTest</servlet-class> <init-param> <param-name>name1</param-name> <param-value>value1</param-value> </init-param> <init-param> <param-name>encode</param-name> <param-value>utf-8</param-value> </init-param> </servlet>
2.在servlet代码中获取配置
public void init(ServletConfig config) throws ServletException { String username = config.getInitParameter("name1"); System.out.println(username); }
4.ServletContext
4.1 简介
WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
查看ServletContext API文档,了解ServletContext对象的功能。
4.2 ServletConfig和ServletContext的区别
ServletConfig记录着单个服务员的信息,比如说年龄,性别等
ServletConfig只能获得自己的servlet信息配置信息
比如:以前获取编码参数的问题等
<servlet> <servlet-name>hello7</servlet-name> <servlet-class>com.accp.servlet.Hello7</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>text/html;charset=GB18030</param-value> </init-param> <init-param> <param-name>sex</param-name> <param-value>男</param-value> </init-param> <init-param> <param-name>age</param-name> <param-value>女</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>hello7</servlet-name> <url-pattern>/hello7</url-pattern> </servlet-mapping>
Service()方法中
//ServletConfig使用,拿到Hello7这个servlet单独的信息
//拿到所有servlet所有参数名
Enumeration configs = this.getServletConfig().getInitParameterNames();
while(configs.hasMoreElements()){
String key=(String)configs.nextElement();
//根据参数名拿参数值
String value = this.getServletConfig().getInitParameter(key);
System.out.println(key+"--"+value);
}
*但是我们发现以前每个servlet都要有编码问题的设置,那我们能不能配置一个公有的,让每一个*
*Servlet都享有*
其实可以配置一个公有信息
<context-param> <param-name>encoding</param-name> <param-value>text/html;charset=GBK</param-value> </context-param>
问题是在servlet中怎么获取这些公共的信息?
ServletContext记录着整个这个门店的信息,也就是服务员工作坏境的信息。比如墙壁是刷成什么颜
色等.
ServletContext保存着整个公共的配置信息
Enumeration contexts =this.getServletContext().getInitParameterNames();
while(contexts.hasMoreElements()){
String key=(String)contexts.nextElement();
String value = this.*getServletContext()*.getInitParameter(key);
System.out.println(key+"--"+value);
}
5 .Servlet的线程安全
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。
解决方案:不要定义成员变量;
6.JSP和Servlet对比
out | 转译后对应JspWriter对象,其内部关联一个PringWriter对象 |
---|---|
request | 转译后对应HttpServletRequest/ServletRequest对象 |
response | 转译后对应HttpServletRespons/ServletResponse对象 |
config | 转译后对应ServletConfig对象 |
application | 转译后对应ServletContext对象 |
session | 转译后对应HttpSession对象 |
pageContext | 转译后对应PageContext对象,它提供了JSP页面资源的封装,并可设置页面范围属性 |
exception | 转译后对应Throwable对象,代表由其他JSP页面抛出的异常对象,只会出现于JSP错误页面(isErrorPage设置为true的JSP页面) |
page | 转译后对应this |
7.使用Beanutils来封装参数(需要导入jar包):
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Map<String, String[]> parameterMap = req.getParameterMap(); UserServiceImpl userService = new UserServiceImpl(); User user = new User(); try { System.out.println("封装之前:"+user); //如果需要封装时间则,需要像下面一样,注册一个转换器 ConvertUtils.register(new Converter() { @Override public Object convert(Class clazz, Object value) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date parse = null; try { if (value.equals("") || value == null) {//如果为空给个默认值 value = "1949-10-1"; } parse = format.parse(value.toString()); } catch (ParseException e) { e.printStackTrace(); } return parse; } }, Date.class); BeanUtils.populate(user,parameterMap); System.out.println("封装之后:"+user); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }