Spring註解驅動開發(三)servlet3.0以及整合SpringMVC

1、Servlet3.0

       在Servlet3.0之前,創建web工程都是需要web.xml文件的,三大組件:Servlet、Filter和Listener以及SpringMVC的前端控制DispatchServlet等都是需要在web.xml文件中進行配置的。

       但是這次我們要做的是基於Spring的註解配置,不想使用web.xml來進行Spring的Listener和SpringMVC的DispatchServlet的配置,怎麼實現呢?首先我們先看看Servlet3.0提供的新特性。

       在Servlet3.0之後,提供了相關的註解來添加組件到web容器中,web.xml文件就不再是必須的了。

1.1 Servlet3.0原生版的註解開發

       Servlet3.0的開發需要tomcat7及以上版本的支持,是JSR315系列的規範。

       創建普通的動態web工程,選擇3.0及以上的版本,不選擇創建web.xml文件;

1.1.1 @WebServlet和@WebInitParam

       @WebServlet註解可以向web容器註冊一個Servlet,配置攔截的路徑等等。這樣的話,我們就不用配置web.xml也可以很容易的實現Servlet的裝配。

      @WebInitParam註解用在Servlet和Filter中,配置一些初始化屬性的

1、index.jsp中添加超鏈接

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="hello">hello</a>
  </body>
</html>

2、創建HelloServlet

package com.servlet3.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(value = "/hello",initParams = {@WebInitParam(name = "password",value = "123456")})
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("------------------HelloServlet執行了------------------");
        ServletConfig servletConfig = getServletConfig();
        String password = servletConfig.getInitParameter("password");
        System.out.println("初始化參數password的值是:"+password);
        response.getWriter().write("Hello servlet3.0");
    }
}

啓動項目,點擊首頁的hello超鏈接,就能返回Hello servlet3.0到瀏覽器。說明註冊Servlet是成功的。

1.1.2 @WebFilter

       @WebFilter 用於將一個類聲明爲過濾器,該註解將會在部署時被容器處理,容器將根據具體的屬性配置將相應的類部署爲過濾器。該註解具有下表給出的一些常用屬性 (所有屬性均爲可選屬性,但是 value、urlPatterns、servletNames 三者必需至少包含一個,且 value 和 urlPatterns 不能共存,如果同時指定,通常忽略 value 的取值 )

package com.servlet3.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "helloFilter",urlPatterns = "/hello")
public class HelloFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("----HelloFilter過濾器初始化----");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        // 對request和response進行一些預處理
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        System.out.println("HelloFilter執行前!!!");
        filterChain.doFilter(request, response); // 讓目標資源執行,放行
        System.out.println("HelloFilter執行後!!!");
    }

    @Override
    public void destroy() {
        System.out.println("----HelloFilter過濾器銷燬----");
    }
}

Filter的生命週期:

       Filter的創建:Filter的創建和銷燬由WEB服務器負責。 web 應用程序啓動時,web 服務器將創建Filter 的實例對象,並調用其init方法,完成對象的初始化功能,從而爲後續的用戶請求作好攔截的準備工作,filter對象只會創建一次,init方法也只會執行一次。通過init方法的參數,可獲得代表當前filter配置信息的FilterConfig對象。

      Filter的銷燬:Web容器調用destroy方法銷燬Filter。destroy方法在Filter的生命週期中僅執行一次。在destroy方法中,可以釋放過濾器使用的資源。

      FilterConfig接口:用戶在配置filter時,可以使用爲filter配置一些初始化參數,當web容器實例化Filter對象,調用其init方法時,會把封裝了filter初始化參數的filterConfig對象傳遞進來。因此開發人員在編寫filter時,通過filterConfig對象的方法,就可獲得:       

  •        String getFilterName():得到filter的名稱。
  •   String getInitParameter(String name): 返回在部署描述中指定名稱的初始化參數的值。如果不存在返回null.
  •   Enumeration getInitParameterNames():返回過濾器的所有初始化參數的名字的枚舉集合。
  •   public ServletContext getServletContext():返回Servlet上下文對象的引用。

1.1.3 @WebListener

       @WebListener註解將一個實現了特定監聽器接口的類定義爲監聽器,這樣我們在web應用中使用監聽器時,也不再需要在web.xml文件中配置監聽器的相關描述信息了。

package com.servlet3.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 * 使用@WebListener註解將實現了ServletContextListener接口的MHelloListener標註爲監聽器
 */
@WebListener
public class HelloListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("============ServletContext 初始化============");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("============ServletContext 銷燬============");
    }
}

1.2 Shared libraries(共享庫)/ runtimes pluggability(運行時插件能力)

Servlet3.0的Shared libraries(共享庫)/ runtimes pluggability(運行時插件能力):

1、Servlet容器(web容器比如tomcat)啓動會掃描 當前應用裏面每一個jar包的 ServletContainerInitializer的實現
2、提供ServletContainerInitializer的實現類;該實現類必須綁定在,META-INF/services/javax.servlet.ServletContainerInitializer
文件中,文件的內容就是ServletContainerInitializer實現類的全類名;

總結:容器在啓動應用的時候,會掃描當前應用每一個jar包裏面META-INF/services/javax.servlet.ServletContainerInitializer
指定的實現類,啓動並運行這個實現類的方法;傳入感興趣的類型;

1.2.1 創建測試的幾個類

package com.servlet3.service;

public interface HelloService {
}

package com.servlet3.service;

public interface HelloServiceExt extends HelloService {
}

package com.servlet3.service;

public class AbstractHelloService implements HelloService {
}


package com.servlet3.service;

public class HelloServiceImpl implements HelloService {
}

1.2.2 創建HelloServletContainerInitializer

package com.servlet3.initializer;

import com.servlet3.service.HelloService;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.util.Set;

//容器啓動的時候會將@HandlesTypes指定的這個類型下面的子類(實現類,子接口等)傳遞過來;
//傳入感興趣的類型;
@HandlesTypes(value={HelloService.class})
public class HelloServletContainerInitializer implements ServletContainerInitializer {
    /**
     * 應用啓動的時候,會運行onStartup方法;
     * Set<Class<?>> set:感興趣的類型的所有子類型;
     * ServletContext servletContext:代表當前Web應用的ServletContext;一個Web應用一個ServletContext;
     * 1)、使用ServletContext註冊Web組件(Servlet、Filter、Listener)
     * 2)、使用編碼的方式,在項目啓動的時候給ServletContext裏面添加組件;
     * 		必須在項目啓動的時候來添加;
     * 		1)、ServletContainerInitializer得到的ServletContext;
     * 		2)、ServletContextListener得到的ServletContext;
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感興趣的類型:");
        for (Class<?> clazz : set) {
            System.out.println(clazz);
        }
    }
}

 

1.2.3 創建javax.servlet.ServletContainerInitializer文件

在src根目錄下創建 META-INF/services目錄,裏面創建文件:javax.servlet.ServletContainerInitializer(該文件沒有後綴名)

注意:在IDEA中的src下面創建普通的文件夾,需要這樣:File——》Project Structure——》選擇Moudles——》選擇sources

文件裏面配置:

com.servlet3.initializer.HelloServletContainerInitializer

啓動可以看到:

感興趣的類型:
interface com.servlet3.service.HelloServiceExt
class com.servlet3.service.AbstractHelloService
class com.servlet3.service.HelloServiceImpl

1.2.3 使用ServletContext註冊web容器的三大組件

       之前我們了可以使用註解@WebServlet等註解來向容器中註冊組件,但是當我們是導入的第三方jar包的時候,就不好使用註解來進行註冊了。以前有web.xml時候,是可以在web.xml中進行配置的。那麼現在沒有了web.xml,要怎麼把組件註冊到容器中呢?

必須在項目啓動的時候來添加,在運行是不允許註冊的:
*     1)、ServletContainerInitializer得到的ServletContext;
*     2)、ServletContextListener得到的ServletContext;
package com.servlet3.initializer;

import com.servlet3.filter.HelloFilter;
import com.servlet3.listener.HelloListener;
import com.servlet3.service.HelloService;
import com.servlet3.servlet.HelloServlet;

import javax.servlet.*;
import javax.servlet.annotation.HandlesTypes;
import java.util.EnumSet;
import java.util.Set;

//容器啓動的時候會將@HandlesTypes指定的這個類型下面的子類(實現類,子接口等)傳遞過來;
//傳入感興趣的類型;
@HandlesTypes(value={HelloService.class})
public class HelloServletContainerInitializer implements ServletContainerInitializer {
    /**
     * 應用啓動的時候,會運行onStartup方法;
     * Set<Class<?>> set:感興趣的類型的所有子類型;
     * ServletContext servletContext:代表當前Web應用的ServletContext;一個Web應用一個ServletContext;
     * 1)、使用ServletContext註冊Web組件(Servlet、Filter、Listener)
     * 2)、使用編碼的方式,在項目啓動的時候給ServletContext裏面添加組件;
     * 		必須在項目啓動的時候來添加;
     * 		1)、ServletContainerInitializer得到的ServletContext;
     * 		2)、ServletContextListener得到的ServletContext;
     */
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        System.out.println("感興趣的類型:");
        for (Class<?> clazz : set) {
            System.out.println(clazz);
        }
        //註冊HelloServlet組件
        ServletRegistration.Dynamic helloServlet = servletContext.addServlet("helloServlet", HelloServlet.class);
        //配置HelloServlet的映射
        helloServlet.addMapping("/hello");
        
        //註冊HelloFilter組件
        FilterRegistration.Dynamic helloFilter = servletContext.addFilter("helloFilter", HelloFilter.class);
        //配置HelloFilter的攔截路徑
        helloFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/hello");

        //註冊HelloListener組件
        servletContext.addListener(HelloListener.class);
    }
}

2、整合SpringMVC

2.1 思路分析

1、web容器在啓動的時候,會掃描每個jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
2、在spring-web的jar包下,META-INF/services下面的javax.servlet.ServletContainerInitializer文件中配置的類是:org.springframework.web.SpringServletContainerInitializer

3、在類SpringServletContainerInitializer上使用了註解@HandlesTypes,配置了WebApplicationInitializer,所以spring的應用一啓動會加載感興趣的WebApplicationInitializer接口的下的所有組件;

@HandlesTypes({WebApplicationInitializer.class})


4、並且爲WebApplicationInitializer組件創建對象(組件不是接口,不是抽象類),他主要有以下三個實現類:


    1)、AbstractContextLoaderInitializer:創建根容器;createRootApplicationContext();
    2)、AbstractDispatcherServletInitializer:
            創建一個web的ioc容器;createServletApplicationContext();
            創建了DispatcherServlet;createDispatcherServlet();
            將創建的DispatcherServlet添加到ServletContext中;
                   getServletMappings();
    3)、AbstractAnnotationConfigDispatcherServletInitializer:註解方式配置的DispatcherServlet初始化器
            創建根容器(父容器):createRootApplicationContext()
                                  getRootConfigClasses();傳入一個配置類
            創建web的ioc容器(子容器): createServletApplicationContext();
                                  獲取配置類;getServletConfigClasses();

    
總結:
    以註解方式來啓動SpringMVC;繼承AbstractAnnotationConfigDispatcherServletInitializer;實現抽象方法指定DispatcherServlet的配置信息;

2.2 整合SpringMVC

2.2.1 創建maven web工程,導入依賴

參照博客(不使用骨架)創建maven web工程:https://blog.csdn.net/weixin_42396239/article/details/92830544

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.springmvc.annotation</groupId>
    <artifactId>springmvc-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>3.0-alpha-1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

2.2.2 自定義WebAppInitializer

        定義MyWebAppInitializer,繼承:AbstractAnnotationConfigDispatcherServletInitializer,實現在web容器啓動的時候創建對象;調用方法來初始化容器以前前端控制器

package com.springmvc;

import com.springmvc.config.SpringConfig;
import com.springmvc.config.SpringMvcConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

//web容器啓動的時候創建對象;調用方法來初始化容器以前前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //獲取根容器的配置類;(Spring的配置文件)   父容器
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    //獲取web容器的配置類(SpringMVC配置文件)  子容器;
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    //獲取DispatcherServlet的映射信息
    //  /:攔截所有請求(包括靜態資源(xx.js,xx.png)),但是不包括*.jsp;
    //  /*:攔截所有請求;連*.jsp頁面都攔截;jsp頁面是tomcat的jsp引擎解析的;
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

2.2.3 創建在MyWebAppInitializer中對應的主配置類和子容器配置類

1、SpringConfig

package com.springmvc.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

//Spring的容器不掃描controller;父容器
@ComponentScan(value = "com.springmvc",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
        })
public class SpringConfig {
}

2、SpringMvcConfig

package com.springmvc.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;

//SpringMVC只掃描Controller,ControllerAdvice(全局異常處理器註解);子容器
//useDefaultFilters=false 禁用默認的過濾規則;
@ComponentScan(value = "com.springmvc",
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, ControllerAdvice.class})
},useDefaultFilters = false)
public class SpringMvcConfig {
}

2.2.4 在webapp下創建index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="hello">hello</a>
</body>
</html>

啓動工程,首頁點擊hello,可以看到瀏覽器上的正確返回。說明整合是成功的。

 

2.3 定製SpringMVC

在進行xml配置的時候,針對SpringMVC的配置有相關的,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!--將SpringMVC處理不了的請求交給tomcat;靜態資源 就可以訪問 -->
	<mvc:default-servlet-handler/>
	<!-- SpringMVC的高級功能開啓 -->
	<mvc:annotation-driven />
	
	<!-- <mvc:interceptors>
	
	</mvc:interceptors>
	<mvc:view-controller path=""/> -->

</beans>

那麼現在沒有了xml,怎麼通過註解來進行定製化的配置呢?

1、在一個SpringMVC配置類(這裏就用上面創建的SpringMvcConfig,也可以另建一個)上,添加註解:@EnableWebMvc:開啓SpringMVC定製配置功能;相當於以前的配置文件的:<mvc:annotation-driven/>

2、配置組件(視圖解析器、視圖映射、靜態資源映射、攔截器。。。),需要實現:WebMvcConfigurer接口(以前的版本是繼承:WebMvcConfigurerAdapter類,但是已經過時了),可以根據需要,重寫對應的方法,可以配置視圖解析器等組件。

2.3.1 自定義一個攔截器

package com.springmvc;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyFirstInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("目標方法之前執行---------------");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("目標方法執行正確之後執行===============================");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("頁面響應以後執行############################");
    }
}

2.3.2 首頁

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="hello">hello</a>
    <a href="success">success</a>

    <br/>
    <img src="img/datupian.png"/>
</body>
</html>

2.3.3 在webapp下面創建WEB-INF/views文件夾,裏面創建success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功頁面</title>
</head>
<body>
SUCCESS PAGE
</body>
</html>

2.3.4 在SpringMVC的配置類中定製SpringMVC

package com.springmvc.config;

import com.springmvc.MyFirstInterceptor;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.config.annotation.*;

//SpringMVC只掃描Controller,ControllerAdvice(全局異常處理器註解);子容器
//useDefaultFilters=false 禁用默認的過濾規則;
@ComponentScan(value = "com.springmvc",
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, ControllerAdvice.class})
},useDefaultFilters = false)
@EnableWebMvc  //開啓SpringMVC定製功能
public class SpringMvcConfig implements WebMvcConfigurer {

    //定製視圖解析器
    public void configureViewResolvers(ViewResolverRegistry registry) {
        //默認所有的頁面都從 /WEB-INF/ xxx .jsp
        //registry.jsp();
        registry.jsp("/WEB-INF/views/",".jsp");
    }

    //靜態資源訪問
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        //開啓靜態資源訪問,
        //相當於配置了:<mvc:default-servlet-handler/> 將SpringMVC處理不了的請求交給tomcat;靜態資源 就可以訪問
        configurer.enable();
    }

    //攔截器
    public void addInterceptors(InterceptorRegistry registry) {
        // /** 攔截任意多層路徑的任意請求
        registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
    }
}

@EnableWebMvc  //開啓SpringMVC定製功能

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章