1.什么是Servlet
Servlet是一个JavaEE的组件,也是JavaEE中的一个规范.
Servlet是JavaEE中的一个规范:服务器其实是对Servlet的实现.
Servlet是一个JavaEE的组件:Servlet是一个程序(类),只不过必须要遵循Servlet的规范.
要求类:必须实现javax.servlet.Servlet接口.
2.如何搭建JavaWeb项目
搭建标准的JavaWeb的项目结构::
1.创建一个Java项目:HelloServletWeb;
2.在HelloServletWeb中创建一个文件夹webapp,表示Web项目的根;
3.在webapp中创建WEB-INF文件夹,
4.在WEB-IN中创建文件夹:lib,classes
5.在WEB-IN中去Tomcat根/conf拷贝web.xml文件,只需要保留根元素.
6.把当前项目的classpath路径改成webapp/WEB-IN下的classes中.
|-HelloServletWeb
|--webapp(这里可以写任何文件夹名,以后只需要将这个文件夹拷贝到服务器即可)
|---WEB-INF
|-----classes
|-----lib
|-----web.xml
3.开发第一个Servlet
Servlet属于JavaEE的范畴,得依赖JavaEE的jar.
Servlet的第一个程序编写步骤:
1):拷贝Tomcat根/lib/servlet-api.jar到项目的WEB-INF/lib目录中,并做build path.
2):编写Servlet程序,使之实现javax.servlet.Servlet接口,并覆盖接口中的方法.
public class HelloServlet implements javax.servlet.Servlet{...实现方法...}
3):发现方法中参数出现arg0,arg1的情况是因为没有关联Servlet的源代码.
是否关联源代码和程序最终的运行没有关系,只是在开发阶段参数美观,可阅读源代码.
Tomcat的源代码程序:apache-tomcat-7.0.57-src.zip
4):在service(ServletRequest req, ServletResponse res)方法,打印一句话.
5):此时HelloServlet类,和Tomcat一点关系都没有:我们需要告诉Tomcat来帮我们管理HelloServlet类.在server.xml中部署项目:
<Context docBase="D:\JavaApps\Servlet\webapp" path="day3"/>
6):为资源配置路径
访问:
http://ip:port/contextPath/资源名
http://localhost:80/day3/hello
4.Servlet的生命周期
- 生命周期:从出生—>死亡,中间的过程.
- Servlet的生命周期:创建对象,初始化操作,运行操作,销毁操作.
Tomcat管理了Servlet对象的生命周期,Servlet对象整个过程都是由Tomcat来管理的.Servlet的创建,初始化,运行,销毁的行为都是Tomcat来负责调用的.
javax.servlet.Servlet接口中的方法:
String getServletInfo():获取Servlet的信息(Servlet的作者,版本,版权相关). ServletConfig getServletConfig():获取Servlet的配置信息对象. void init(ServletConfig config):初始化Servlet对象方法 void service(ServletRequest req, ServletResponse resp):服务方法,Web动态网页的操作就编写在该方法. void destroy():销毁Servlet对象方法.
在Web的生命周期中(Tomcat启动->Tomcat关闭),Servlet是单例的.
构造器:在服务端程序第一次被请求的时候,调用,只被调用一次. void init(ServletConfig config):在构造器执行完毕之后,调用init方法,也只会执行一次. void service(ServletRequest req, ServletResponse resp):每一次请求都会执行该方法. void destroy():正常关闭Tomcat才会执行(该方法不一定会被执行,我们没必要在其中编写扫尾的操作).
总结:
构造器--->init方法---->[ service方法 ]循环 ---->destory方法
5.Servlet的请求流程
1:浏览器先发送请求:http://localhost:80/day3/hello.
2:DNS解析域名(忽略)
3:Tomcat解析请求:/day3/hello.
上下文路径:/day3
资源的名称:/hello
4:解析Tomcat根/conf/server.xml文件,获取其中所有的元素,并找到path属性为/day3的元素.
<Context docBase="D:\JavaApps\Servlet\webapp" path="day3"/>
再读取该元素,再获取docBase属性值,该属性值就是当前访问的WEB项目的根路径.
5:从该web的根路径/WEB-INF下找到web.xml文件.
6:读取web.xml文件,获取所有的元素,并判断哪一个的文本内容为:/hello.
找不到: 报404错误.
找 到: GOTO 7.
7:通过/hello,找到当前Servlet的全限定名.
com._520it._01_hello.HelloServlet.
8:从Servlet的实例缓冲池中去获取com._520it._01_hello.HelloServlet对应的对象.
Map<String,Servlet> cache = .....;
Servlet obj = cache.get("com._520it._01_hello.HelloServlet");
if(obj == null{
//第一次请求:GOTO 9.
}else{
//非第一次请求:GOTO 12;
}
9:使用反射创建Servlet对象.
Servlet obj = Class.forName("com._520it._01_hello.HelloServlet").newInstance();
10.把创建的Servlet对象,存储到Servlet实例缓存池中,供下一次请求使用.
cache.put("com._520it._01_hello.HelloServlet",obj);
11:容器创建ServletConfig对象,并调用init方法,来完成初始化操作.
obj.init(config);
12:容器创建ServletRequest和ServletResponse对象,并调用service方法,处理请求.
obj.service(req,resp);
13:在service方法中,对当前请求的客户端做相应.
6.Servlet初始化参数
ServletConfig接口:表示Servlet的信息配置对象(web.xml中,当前Servlet的配置信息).
其中的方法:
String getServletName():获取当前Servlet的名字,<servlet-name>元素的文本内容.
ServletContext getServletContext():获取当前的Servlet上下文,其实表示当前应用对象.
String getInitParameter(String paramName):根据当前Servlet的指定的参数名获取初始化参数值.
Enumeration<String> getInitParameterNames():获取当前Servlet的所有初始化参数的名字.
配置信息如下:
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>com._520it.servlet.ServletDemo2</servlet-class>
<!-- 单个servlet的初始化数据 -->
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
<init-param>
<param-name>person</param-name>
<param-value>lean</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo2</servlet-name>
<url-pattern>/servletdemo2</url-pattern>
</servlet-mapping>
获取初始化参数:
7.Servlet继承体系
以下模拟Servlet继承体系,首先Servlet是一个接口规范,Tomcat对于实现了2个子类GenericSevlet和HttpSevlet。
MyGenericSevlet.java
/**
* 该类主要封装了ServletConfig对象的业务逻辑
*/
public abstract class MyGenericSevlet
implements Servlet,ServletConfig,Serializable{
private static final long serialVersionUID = 1L;
protected ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config=config;
init();
}
public void init() {
//NO OP
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void destroy() {
}
//---------如下就是ServletConfig 接口对应的方法--------
@Override
public String getServletName() {
return config.getServletName();
}
@Override
public ServletContext getServletContext() {
return config.getServletContext();
}
@Override
public String getInitParameter(String name) {
return config.getInitParameter(name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return config.getInitParameterNames();
}
}
MyHttpSevlet.java:
/**
* 该类主要封装了Http对象相关的请求方法
*/
public abstract class MyHttpSevlet extends MyGenericSevlet{
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request=(HttpServletRequest) req;
HttpServletResponse response=(HttpServletResponse) res;
service(request, response);
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals("GET")) {
doGet(req, res);
}else {
doPost(req, res);
}
}
private void doPost(HttpServletRequest req, HttpServletResponse res) {
//DEFAULT IMPL
}
private void doGet(HttpServletRequest req, HttpServletResponse res) {
//DEFAULT IMPL
}
}
总结:
8.HttpServletRequest常用方法
ServletRequest接口: 请求对象,封装了获取所有请求信息(请求行,请求头,请求实体)的方法.
HttpServletRequest接口:是ServletRequest的子接口,处理HTTP协议请求的方法.
常用方法:
01.String getMethod():返回请求方式:如GET/POST
02.String getRequestURI():返回请求行中的资源名字部分:如/test/index.html
03.StringBuffer getRequestURL():返回浏览器地址栏中所有的信息
04.String getContextPath():返回当前项目的上下文路径(<Context/>元素的path属性值.)
05.String getRemoteAddr():返回发出请求的客户机的IP地址
06.String getHeader(String name):返回指定名称的请求头的值。
获取请求参数的方法:
01.String getParameter(String name):返回指定名字参数的值。
02.String[] getParameterValues(String name):返回指定名字参数的多个参数值。
03.Enumeration<String> getParameterNames():返回所有参数名的Enumeration对象。
04.Map<String,String[]> getParameterMap():返回所有的参数和值所组成的Map对象。
9.HttpServletResponse常用方法
ServletResponse接口: 响应对象.封装了获取响应信息的方法.
HttpServletResponse接口:ServletResponse的子接口,可以处理HTTP响应的方法.
常用方法:
OutputStream getOutputStream():获取字节输出流对象. 文件下载.
PrintWriter getWriter():获取字符输出流对象
注意:上述方法,不能共存,否则报错.
设置输出的MIME类型(内容的类型):
response.setContentType(“text/html”);//不能写错
设置输出数据的编码方式:
response.setCharacterEncoding(“UTF-8”);
可以将上述两行代码合并成一行代码:
response.setContentType(“text/html;charset=utf-8”);
注意:必须先设置MIME类型和编码,再获取输出流,否则没有效果.
10.Servlet映射细节
1):一个Servlet程序(Web组件),可以配置多个,表示一个Servlet有多个资源名称.
2):一个Servlet程序,可以配置多个.
3):必须保证唯一性,而且必须使用/打头.
4):Servlet的映射支持通配符映射(*:表示任意字符):
第一种写法: /*,任意的资源都可以访问该Servlet. /system/*:请求的资源必须以/system/打头才可以访问.
Servlet中,权限控制:
第二种写法: *.拓展名, 比如: *.do,请求的资源必须以.do作为结尾才可以访问该Servlet.
5):在映射Servlet的时候,元素的文本内容不能是default.因为,在Tomcat中存在一个叫做default的Servlet,专门用于处理请求静态资源(html,css,js,图片等).
在Tomcat根/conf/web.xml文件:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
...
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
11.Servlet3.0新特性-注解配置
Servlet3.0对应着JavaEE6的规范,Tomcat7.*.
问题:传统的使用XML做Servlet配置,如果Servlet有N个,就得配置 10*N 行代码,web.xml文件,臃肿,不利于维护,开发效率低.
从Tomcat7开始,可以使用注解(WebServlet)来取代XML配置.
使用注意:在web.xml文件的根元素中,存在属性,表示是否忽略扫描Web组件注解:
metadata-complete=”true” : 要忽略
metadata-complete=”false”: 不忽略
不要改属性: 缺省情况等价于metadata-complete=”false”.
开发步骤:
1.修改web.xml
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="false">
...
</web-app>
2.在Servlet中,对其进行配置:
@WebServlet(value="/mapping",loadOnStartup=1,
initParams= {@WebInitParam(name="encoding",value="GBK"),
@WebInitParam(name="username",value="lean")})
public class MappingServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("mapping service !");
String encoding = getInitParameter("encoding");
String username = getInitParameter("username");
System.out.println(encoding+" "+username);
}
}
12.启动初始化Servlet
回顾Servlet生命周期方法的执行流程:
在启动Tomcat服务器的时候,没有对Servlet创建和初始化操作.
在第一次服务端请求的时候:
1):创建Servlet对象.
2):调用init方法做初始化.
3):调用service方法,处理请求.
如果某一天,某一个Servlet(核心Servlet:初始化全局的信息)需要在启动服务器的时候就创建出来,我们可以使用loadOnStartup配置。
配置后启动Tomcat,会发现init被调用了,但是不代表service被调用,因为我们只是创建了该对象,并没有调用该对象的组件。
13.Servlet线程安全问题
Servlet的线程不安全问题:
造成的根本原因是:Servlet是单例的,Servlet中的非static的成员变量只有一份,多个客户端好比是多个线程,都访问的是同一个空间.
解决方案:
1:让当前Servlet实现javax.servlet.SingleThreadModel接口.
包装只有一个线程放Servlet,如果有多个线程就排队,如此的话,性能超低(已过时).
2:在Servlet中不要使用成员变量,使用局部变量.
每一个用户,每一个请求都会调用service方法,而局部变量在service方法中,每一次都是新的空间.
Struts1,Spring MVC都是线程不安全的,都是单例的和Servlet类似.
Struts2是线程安全的,因为每一个线程(请求)都是一个新的Action对象.