什么是Servlet?

  Servlet是运行在Web服务器或应用服务器上的程序,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库

或应用程序之间的中间层。

  servlet架构:

  

Servlet的生命周期:

  Servlet生命周期可被定义为从创建直到毁灭的整个过程。

  1、Servlet通过调用init()方法进行初始化

  2、Servlet调用service()方法来处理客户端的请求,Servlet容器在执行service方法之前加载Servlet。

  3、Servlet通过调用destory()方法终止(结束)

  最后,Servlet是由JVM的垃圾回收器进行垃圾回收的。

init方法只在第一次创建Servlet时调用一次

service方法是执行实际任务的主要方法。Servlet容器(即Web服务器)调用service()方法来处理客户端(浏览器)的请求,并把格式化

  的响应写回给客户端。每次服务器接收到一个Servlet请求时,服务器会产生一个新的线程并调用服务。service()方法由容器调用,service()方法检查HTTP请求类型(GET、POST、PUT、DELETE等),service方法会在适当的时候调用doGet、doPost、doPut、doDelete等方法,所以,我们不用对service()方法做任何动作,您只需要根据来自客户端的请求类型来重写doGet或doPost即可。

destory方法只会被调用一次,在Servlet声明周期结束时被调用。在destory方法中可以关闭数据库连接、停止后台线程、把Cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。

  Servlet是服务HTTP请求并实现javax.servlet.Servlet接口的Java类。Web应用程序开发人员通常编写Servlet来扩展javax.servlet.http.HttpServlet,并实现Servlet接口的抽象类专门用来处理HTTP请求。

  读取HTTP头的方法,如getSession,getParameter(String name)等,这些方法可在Servlet程序中读取HTTP头,通过HttpServlet对象可调用。

过滤器:

  Servlet过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。

  Servlet过滤器是可用于Servlet编程的Java类,可以实现以下目的:

    1):在客户端的请求访问后端资源之前,拦截这些请求

    2):在服务器的响应发送给客户端之前,处理这些响应。

如:身份验证过滤器、加密过滤器、日志记录和审核过滤器等。

过滤器通过Web部署描述符(web.xml)中XML标签来声明,然后映射到应用程序的web.xml中的Servlet名称或URL模式。当Web容器启动Web应用程序时,它会为在web.xml中的每一个过滤器创建一个实例。

Filter的执行顺序与在web.xml配置文件中的配置顺序一致,一般把Filter配置在所有的Servlet之前。

  过滤器是一个实现了javax.servlet.Filter接口的Java类。javax.servlet.Filter接口定义了三个方法:

  1):public void doFilter (ServletRequest, ServletResponse, FilterChain)

    完成实际的过滤操作,当客户端请求的URL和过滤设置的URL匹配时,Servlet先调用过滤器的doFilter方法,FilterChain用于访问后续过滤器

  2):public void init(FilterConfig filterConfig)

    Web应用程序启动时,Web服务器将创建Filter的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能。因为filter对象只

    会创建一次,所以init方法也只会执行一次。在filterConfig中可以获取配置信息

  3):public void destroy()

    Servlet容器在销毁过滤器实例前调用该方法,在该方法中释放Servlet过滤器占用的资源。

Servlet异常处理

  当一个Servlet抛出一个异常时,Web容器在使用了exception元素的web.xml中搜索与抛出的异常类型相匹配的配置。因此必须在web.xml中使用error-page

元素来指定对特定异常或HTTP状态码作出相应的Servlet调用。如:

<servlet>
<servlet-name>ErrorHandler</servlet-name>
<servlet-class>com.runoob.test.ErrorHandler</servlet-class>
</servlet>
<!-- servlet mappings -->
<servlet-mapping>
<servlet-name>ErrorHandler</servlet-name>
<url-pattern>/TomcatTest/ErrorHandler</url-pattern>
</servlet-mapping>
<error-page>
<error-code>404</error-code>
<location>/TomcatTest/ErrorHandler</location>
</error-page>
<!-- 所有的异常的通用的错误处理程序 ,与上面的互斥-->
<error-page>
<exception-type>java.lang.Throwable</exception-type >
<location>/ErrorHandler</location>
</error-page>

Servlet四大域对象

  1)、pageContext:作用域为page,页面执行期,只在当前页面有效

  2)、request:是表示一个请求,只要发出一个请求就会创建一个request,它的作用域仅在当前请求中有效

  3)、session:服务器为每一个会话创建一个Session对象,所以Session中的数据仅可供当前会话中所有的Servlet共享

  4)、Application(ServletContext上下文):所有的用户都可以取得此信息,此信息在整个服务器端被保留。Application属性范围只要设置一次,则

    所有的网页窗口都可以取得数据。ServletContext在服务器启动时创建,在服务器关闭时销毁,一个JavaWeb应用只创建一个ServletContext对象。

  Session

  一个Web服务器可以分配一个唯一的session会话ID(JSESSIONID)作为每个Web客户端的cookie,对于客户端的后续请求可以使用接收到的cookie来识别。(因为会有很多浏览器不支持cookie,所以不建议使用这种方式维持session会话)

  HttpSession对象:Servlet提供了HttpSession接口,该接口提供了一种跨多个页面请求或访问用户以及存储有关用户信息的方式。Servlet 容器使用这个接口来创建一个 HTTP 客户端和 HTTP 服务器之间的 session 会话。会话持续一个指定的时间段,跨多个连接或页面请求。通过调用 HttpServletRequest 的公共方法 getSession() 来获取 HttpSession 对象

  删除Session会话数据:

  1)、移除一个特定的属性:removeAttribute(String name) 来删除与特定的键相关的值

  2)、删除整个session会话:invalidate() 丢弃整个session会话

  3)、设置session会话过期时间:setMaxInactiveInterval(int interval) 单独设置session会话超时(秒为单位),也可在web.xml中配置会话超时(分钟为单位)

Servlet3.0

  Servlet3.0支持使用注解的方式取代之前在web.xml中的servlet、filter、listener的定义配置。

如:注册Servlet:


@WebServlet(name = "MyServlet", urlPatterns = {"/foo", "/bar"}, initParams = {@WebInitParam(name = "contextConfigLocation", value = "classpath*:/spring-mvc.xml"),
@WebInitParam(name = "xxx", value = "xxx")})
public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  super.doGet(req, resp);
}
}

注册Filter:

@WebFilter(value = "/foo")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {}
@Override
public void destroy() {}
}

注册Listener:

@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext sc=servletContextEvent.getServletContext();// 一个web应用一个ServletContext
     // 在这里可以动态往容器中添加Servlet、Filter、Listener等
 } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }

也可以在项目启动时候添加手动Servlet、Filter、Listener等

package com.atguigu.servlet;

import java.util.EnumSet;
import java.util.Set; import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes; import com.atguigu.service.HelloService; //容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer { /**
* 应用启动的时候,会运行onStartup方法;
*
* Set<Class<?>> arg0:感兴趣的类型的所有子类型;
* ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
*
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
* 必须在项目启动的时候来添加;
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*/
@Override
public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
// TODO Auto-generated method stub
System.out.println("感兴趣的类型:");
for (Class<?> claz : arg0) {
System.out.println(claz);
}
    // 使用ServletContext注册Servlet
    ServletRegistration.Dynamic servlet = sc.addServlet("myServlet", "com.yang.spbo.servlet.MyServlet");
    // Servlet映射关系
    servlet.addMapping("/foo");     // 注册Listener
    sc.addListener(MyListener.class);     // 注册Filter
    FilterRegistration.Dynamic filter=sc.addFilter("myFilter",new MyFilter());
    // Filter的映射
    filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
 }
}

如果是SpringBoot项目需要启动类上加上@ServletComponentScan注解;或者在每个Servlet、Filter、Listener上加上@Component注解

Servlet3.0和SpringMVC整合

Web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer,并加载这个文件指定的类

而Spring web包下:

指定的类为:

org.springframework.web.SpringServletContainerInitializer

因此,在Web容器启动的时候会加载SpringServletContainerInitializer

SpringServletContainerInitializer:

package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; @HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
} public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if (webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator(); while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
          // 如果组件不是接口也不是抽象类
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)waiClass.newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
} if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator(); while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
initializer.onStartup(servletContext);
} }
}
}

SpringServletContainerInitializer 实现了ServletContainerInitializer ,并且指定了@HandlesTypes({WebApplicationInitializer.class}),因此在Web容器启动的时候会加载WebApplicationInitializer

接口下的所有组件,如果组件不是接口也不是抽象类,那么则为WebApplicationInitializer组件创建对象,并调用其onStartup方法。

Spring 容器分层结构(父子容器):

WebApplicationInitializer接口的3个抽象子类:

  1)、AbstractContextLoaderInitializer:

      (1)在onStartup方法中创建根容器【createRootApplicationContext();】
  2)、AbstractDispatcherServletInitializer:在onStartup方法中:

      (1)创建一个web的ioc容器【createServletApplicationContext();】
      (2)创建了DispatcherServlet【createDispatcherServlet();】
      (3)将创建的DispatcherServlet添加到ServletContext中【Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);

        registration.setLoadOnStartup(1);
        registration.addMapping(this.getServletMappings());】

      (4)【protected abstract String[] getServletMappings();】此方法由子类来实现
  3)、AbstractAnnotationConfigDispatcherServletInitializer:【AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer】

    继承自2)中的AbstractDispatcherServletInitializer,是注解方式配置的DispatcherServlet初始化器

public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer {
public AbstractAnnotationConfigDispatcherServletInitializer() {
}
  // 创建根容器(只扫描Services和Repositories组件)
protected WebApplicationContext createRootApplicationContext() {
    // 获取根配置文件(Spring配置文件),需要子类提供
Class<?>[] configClasses = this.getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses);
return rootAppContext;
} else {
return null;
}
}
  // 创建Web的IOC容器(只扫描controller,ViewResolver,HandlerMapping组件)
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
    // 获取web配置类(Spring MVC配置文件),需要子类提供
Class<?>[] configClasses = this.getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
servletAppContext.register(configClasses);
} return servletAppContext;
} protected abstract Class<?>[] getRootConfigClasses(); protected abstract Class<?>[] getServletConfigClasses();
}

因此,如果要以注解方式来启动SpringMVC;就自定义一个类继承AbstractAnnotationConfigDispatcherServletInitializer ,然后实现其抽象方法

指定配置文件信息。

若不使用注解的方式,原来的web.xml配置文件为:

<web-app>

    <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext-*.xml</param-value> // 即Spring根配置文件
</context-param> <servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value> // 即Spring MVC配置文件
</init-param>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping> </web-app>

使用注解的方式,省略掉web.xml的自定义web应用启动类

//web容器启动的时候创建对象;调用方法来初始化容器以及前端控制器
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //获取根容器的配置类;(Spring的配置文件) 父容器;
@Override
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[]{RootConfig.class};
} //获取web容器的配置类(SpringMVC配置文件) 子容器;
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[]{AppConfig.class};
} //获取DispatcherServlet的映射信息
// /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
// /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[]{"/"};
}
}

至此,即将Spring容器以及前端控制器创建好了。

原来在使用xml配置方式的时候,会在springmvc.xml中配置以下信息

    <!--加载属性配置文件-->
<context:property-placeholder ignore-unresolvable="false" location="classpath:spmbs.properties"/>
<!-- 注解驱动,可以代替注解的处理器适配器的配置和处理器映射器的配置,默认还会加载很多其他配置比如:json转换器的配置-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 组件扫描,自动注入-->
<context:component-scan base-package="cn.com.yang.modules"></context:component-scan>
<!-- 视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<!--对静态资源的访问,将无法映射到controller方法的path交给default-servlet-handler处理-->
<mvc:default-servlet-handler/>
<!--拦截器配置-->
<mvc:interceptors>
<!--多个拦截器顺序执行-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/sys/login/**"/>
<bean class="cn.com.yang.interceptor.LoginHandlerInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!--/**拦截所有的url及其子url,/* 只拦截根url不能拦截子url-->
<mvc:mapping path="/**"/>
<bean class="cn.com.yang.interceptor.MyHandlerInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.com.yang.interceptor.MyHandlerInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>

那么使用配置类的方式的话怎么配置这些信息呢?

使用@EnableWebMvc注解在mvc配置类上,开启注解驱动,相当于xml中的 <mvc:annotation-driven/>

让配置类实现WebMvcConfigurer接口,实现接口中的方法进行其他配置;也可以让配置类继承WebMvcConfigurerAdapter抽象类,其实现了WebMvcConfigurer接口,这样我们需要

哪些配置,重写抽象类中的方法即可

可参考Spring官方文档:https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/web.html#mvc-config-customize

如:

@Configuration
@EnableWebMvc // 相当于xml中的 <mvc:annotation-driven/>
public class AppConfig extends WebMvcConfigurerAdapter {
/**
* 视图解析器
*
* @param registry
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views", ".jsp");
} /**
* 静态资源访问,相当于<mvc:default-servlet-handler/>
*
* @param configurer
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
} /**
* 拦截器
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
}
}

SpringMVC实现异步处理:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
// Save the deferredResult somewhere..
return deferredResult;
} // From some other thread...
deferredResult.setResult(data);

Controller返回一个DeferredResult,并将deferredResult对象保存到某处,如消息队列中,并将deferredResult返回,然后异步进行处理。此时,请求时阻塞的。当有其他线程处理完成之后

将deferredResult从队列中取出来然后设置result值时,则会响应给客户端。

回顾Servlet及SpringMVC的更多相关文章

  1. 带着新人简单看看servlet到springmvc

    好久都没有写博客了,不是因为自己懒了,而是总感觉自己知道的只是太少了,每次想写博客的时候都不知道怎么下手,不过最近看到一篇博客说的是springmvc,给了我比较大的兴趣,感觉一下子对整个spring ...

  2. struts2,servlet和springmvc的单例多例问题

    struts2,servlet和springmvc的单例多例问题 原创 2017年06月12日 09:59:21 标签: struts2 / servlet / springmvc / 单例 / 多例 ...

  3. 文件上传(Servlet/Struts2/SpringMVC)

    文件下载(Servlet/Struts2)的链接:http://www.cnblogs.com/ghq120/p/8328093.html 文件上传 Servlet实现 要实现文件上传的功能,必须在f ...

  4. servlet到springmvc的演进

    1.简单看看servlet 1.1.servlet继承关系 先看看下面servlet的这个继承关系,有点印象即可(可以暂时忽略ServletConfig,这个接口就是让我们可以从web.xml文件中拿 ...

  5. 1. 回顾Servlet

    回顾Servlet 创建web工程 servlet-api:http://dwz.date/aTGa 编写Servlet import javax.servlet.ServletException; ...

  6. Javaee----重新回顾servlet

    最近面临找工作,不得不回顾一下java servelt . 发现lz的基本功还是很差 1. 每一个servlet都必须实现servlet接口,GenericServlet是个通用的.不特定于任何协议的 ...

  7. 9、Http回顾/Servlet

    1 Http回顾 Http协议: 1)http协议: 对浏览器客户端和服务器端之间数据传输的格式规范. 2)http请求:浏览器->服务器端 格式: 请求行(请求方式(GET/POST) 请求资 ...

  8. Servlet Struts2 SpringMVC 获取参数与导出数据 方法比较

    servlet中通过request.getParameter()从网页获取参数 通过request session servletContext几个域对象的setAttribute(String ,O ...

  9. Servlet和SpringMVC补课

    1.web.xml加载顺序 http://mianhuaman.iteye.com/blog/1105522 关键点:ServletContext -> context-param -> ...

随机推荐

  1. (转)深入理解Linux修改hostname

    当我觉得对Linux系统下修改hostname已经非常熟悉的时候,今天碰到了几个个问题,这几个问题给我好好上了一课,很多知识点,当你觉得你已经掌握的时候,其实你了解的还只是皮毛.技术活,切勿浅尝则止! ...

  2. 基于Apache搭建HTTP HTTPS

    参考资料 <openssl攻略>--第一章 <Apache服务器配置与使用工作笔记>-- 第六章 第十四章 https://juejin.im/post/5a31faf2518 ...

  3. 5.1properties属性

    需求: 将数据库连接参数单独配置在db.properties文件中,只需在SqlMapconfig.xml中加载db.properties的属性值. 在SqlMapconfig.xml中就不需要对数据 ...

  4. 从0 开始手写一个 RPC 框架,轻松搞定!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 来源:juejin.im/post/5c4481a4f265da613438aec3 之前在 RPC框架底层到底什么原理得知 ...

  5. java_第一年_JavaWeb(8)

    前面说到,JSP在运行时会被编译成Servlet源代码,通过_jspServlet方法处理请求,此时该方法会传递和提供9个与web开发相关的对象进行使用,开发人员在JSP页面通过对这些变量即可引用这9 ...

  6. python学习三十八天常用内置函数分类汇总

    python给我们提供丰富的内置函数,不用去写函数体,直接调用就可以运行,很方便快速给我提供开发所需要的函数. 1,查内存地址 id() 变量的内存地址 id() 2,输入输出 input()  pr ...

  7. ADC(简易的DMA传输)的认识

    ADC(简易的DMA传输)的认识 首先看到是ADC的特性 1.ADC的12位分辨率.不能直接测量负电压,然后是最小量程化单位是LSB=Vref+/212 2.单次和转换模式的使用 3. 从通道0到通道 ...

  8. spring boot整合redis多实例

    最近项目中遇到需要连接两个redis实例的情况,于是就在spring boot原先的基础上修改了一点. 首先,添加所需的依赖 <dependency> <groupId>org ...

  9. Can't determine basedir from my_print_defaults mysqld

    我的环境是:centos7 + mysql5.7.26,今天在用 mysqldumpslow 命令查看慢查询日志时出现下面的错误 [root@localhost ~]# mysqldumpslow - ...

  10. 基于mesos 安装 jenkins

    mesos master 机子上安装  jenkins git clone https://github.com/jenkinsci/mesos-plugin.git && cd me ...