回顧Servlet及SpringMVC

什麼是Servlet?html

  Servlet是運行在Web服務器或應用服務器上的程序,它是做爲來自Web瀏覽器或其餘HTTP客戶端的請求和HTTP服務器上的數據庫前端

或應用程序之間的中間層。java

  servlet架構:web

  

Servlet的生命週期:spring

  Servlet生命週期可被定義爲從建立直到毀滅的整個過程。數據庫

  一、Servlet經過調用init()方法進行初始化編程

  二、Servlet調用service()方法來處理客戶端的請求,Servlet容器在執行service方法以前加載Servlet。json

  三、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對象。

 

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值時,則會響應給客戶端。

相關文章
相關標籤/搜索