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定製功能