什么是Serlvet ?#
全称 server applet 运行在服务端的小程序:
首先来说,这个servlet是java语言编写的出来的应用程序,换句话说servlet拥有java语言全部的优点,比如跨越平台,一次编译到处运行
其次: 相对于CGI(common gateway interface)规范而言,CGI是针对每一个用户的请求创建一个进程处理,而servlet所在的服务器会对每一个请求创建一个线程来处理,虽然线程数量有上限,但是相对于创建进程来说,后者对系统资源的开销更小
然后就是: 现在盛行javaWeb服务器Tomcat也是java语言编写的,毕竟Tomcat有Serlvet容器支持,所以servlet和web服务器之间无缝连接
Servlet其实一个接口,一套规范,不同的厂家对它有不同的实现,tomcat也是如此,
web服务器会把解析http协议信息的逻辑封装进他们的Servlet中,比如将用户发送的请求(request) HttpRequestServlet
,
把响应给用户http报文的逻辑封装进HttpResponseServlet
中, 然后web服务器负责不同组件,不同servlet之间的调度关系,
什么是调度呢? 比如说: 通过某个URL找到指定的Servlet,回调Servlet的service()
方法处理请求
Servlet的体系结构#
servlet接口的实现类如上图
Servlet在java中是一个接口,封装了被浏览器访问到服务器(tomcat)的规则
添加serlvet#
通过web.xml#
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Camel Routes</display-name> <!-- Camel servlet --> <servlet> <servlet-name>app1</servlet-name> <servlet-class>com.changwu.web.MyServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- Camel servlet mapping --> <servlet-mapping> <servlet-name>app1</servlet-name> <url-pattern>/app1</url-pattern> </servlet-mapping> </web-app>
通过注解#
舍弃web.xml是serlet3.0添加全注解技术, 这个注解的属性和需要在xml中配置的对应的
需要Tomcat7及以上才支持
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { /** * servlet-name */ String name() default ""; /** * The URL patterns of the servlet */ String[] value() default {}; /** * servlet的资源路径, 可以为一个servlet配置多个访问路径 */ String[] urlPatterns() default {}; /** * 启动级别默认是-1,同样意味着依然是第一次访问时初始化 */ int loadOnStartup() default -1; /** * The init parameters of the servlet */ WebInitParam [] initParams() default {}; /** * Declares whether the servlet supports asynchronous operation mode. * * @see javax.servlet.ServletRequest#startAsync * @see javax.servlet.ServletRequest#startAsync(ServletRequest, * ServletResponse) */ boolean asyncSupported() default false; /** * The small-icon of the servlet */ String smallIcon() default ""; /** * The large-icon of the servlet */ String largeIcon() default ""; /** * The description of the servlet */ String description() default ""; /** * The display name of the servlet */ String displayName() default ""; }
servlet的路径定义规则#
- /xxx
@WebServlet(urlPatterns = {"/app1","/app2"})
- /xxx/yyy
@WebServlet(urlPatterns = {"/app1/app2"})
- /xxx/*
@WebServlet(urlPatterns = {"/app1/*"})
- *.do
@WebServlet(urlPatterns = {"*.do"})
执行原理:#
- tomcat读取xml配置文件中配置servlet,根据用户配置的加载时机,通过反射技术创建出对象实例
- 用户的请求报文经过tomcat的解析,分发到的Servlet下面,进行不同的回调处理
Servlet接口的方法#
- 初始化方法, 创建servlet时执行一次
- 什么时候被创建: 默认情况下 第一次访问时被创建
- 一般我们都在web.xml配置,让Servlet在启动时完成加载
<load-on-startup>1</load-on-startup>
默认这个值是-1, 表示第一次访问时被创建, 整数表示启动时初始化
- 此外: Servlet的init()方法仅仅被执行一次,说明serlet是单例的,那么在并发的情况的就可能出现线程安全问题 , 解决: 尽量不要在serlvet中定义成员变量,我们最好去成员方法中定义变量,即使定义了, 不要提供set()方法,仅仅提供的get()
@Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("init....."); }
- 获取serlvet config 配置对象
// @Override public ServletConfig getServletConfig() { return null; }
- 提供服务的方法, 每次serlvet被访问都会执行一次
@Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("--------------------service------------"); }
- 获取serlvet 的信息, 版本等
@Override public String getServletInfo() { return null; }
- 服务器正常关闭前, 销毁servlet时 回调
- 服务器非正常关闭,不会执行
@Override public void destroy() { System.out.println("destroy"); }
Servlet3.0新特性#
Servlet3.0中的重大升级是ServletContainerInitializer
,通过这个技术使我们可以为现有的组件写出可插拔的组件,与之相对应的是Servlet的新规范如下:
在执行的路径下面创建指定的文件
/classpath: --META-INF (目录) --services (目录) --javax.servlet.ServletContainerInitializer (文件)
我们可以在上面的文件中配置一个类的全类名,这个类是谁无所谓,但是只要它实现了这个ServletContainnerInitializer
接口,并重写它的onStart()
方法,于是当容器(tomcat)启动的时候就会调用这个类的 onStart()
方法
这个规范带来的革命决定是历史性的,有了它我们的代码就有了可插拔的能力,不信可以回想一下传统的配置文件,如果想给项目进行升级,还不想改动xml文件,那是不可能的,但是现在不同了,只要让我们的类实现这个ServletContainnerInitializer
,重写它的方法,它的onStart()
就会被回调,而其他的功能不受响应,去掉这个类,项目整体也不受响应
示例:
容器启动的时候,会把容器中,被@HandlerTypes(value={Test.class})
中指定的所有Test.class
实现类(子类,子接口)的实例传递进下面的set集合
@HandlesTypes(Test.class) public class ChangWuInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException { System.out.println(set); } }
通过上面onstart()
方法可以看到,第二个参数位置上是 ServletContext, 这个对象是什么?有啥用? 在下文中单独开一个模块说
ServletContext#
tomcat会为每一个web项目创建一个全局唯一的ServeltContext,这个对象里面封装着整个应用的信息,常用的当作域对象,所有的servlet之间共享数据,同时他还可以获取出web.xml文件中的数据
功能:#
- 获取MIME类型
- MIME类型是互联网通信中定义的文件数据类型
- 格式: 大类型/小类型 如: test/html
在tomcat的配置文件目录中存在web.xml ,里面的存在大量的MEMI类型的数据,都可以从
ServletContex
t中获取出来
String getMimeType(String file)
- 域对象(共享数据)
范围: 类似于Session,通过ServletContext对象我们也可以实现数据共享,但值得注意的是,Session是只能在一个客户端中共享数据,而ServletContext中的数据是在所有客户端中都可以实现数据共享的。
方法:
setAttribute(String name,Onject obj); getAttribute(String name); removeAttribute(String name);
- 获取文件真实的文件路径
方法
this.getServletContext().getRealPath("/"); // 现在访问的目录是tomcat中和WEB-INF同级目录
- 实现请求转发
// 方式1: request.getRequestDispatcher("/url").forward(req,res); // 方式2: this.getServletContext().getRequestDispatcher("/url").forward(req,res);
- 获取web应用的初始化参数
我们可以用标签为servlet配置初始化参数,然后使用ServletConfig对象获取这些参数,假如有如下的MyServlet,它的配置为:
<servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.gavin.servlet.MyServlet</servlet-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </servlet>
获取:
String encoding = this.getServletConfig().getInitParameter("encoding");
如何获取:#
ServletContext
在web应用上下文中以单例的形式存在,下面两种获取方式得到的ServletContext
是同一个对象
ServletContext servlet1 = request.getServletContext(); ServletContext servlet2 = this.getServletContext(); this.getServletConfig().getServletContext();
生命周期#
服务器一启动就创建,服务器关闭时才销毁
注册三大web组件(servlet filter listener)#
- Servlet
addServlet、createServlet、getServletRegistration、getServletRegistrations
- Filter
addFilter、createFilter、getFilterRegistration、getFilterRegistrations
- 监听器
addListener、createListener