上篇文章走讀了springboot中消息處理進(jìn)入到servlet里了,這次,具體走讀了請(qǐng)求消息在servlet里是怎么處理的。這里主要補(bǔ)充servlet和springmvc相關(guān)的知識(shí)。
servlet相關(guān)知識(shí)
servlet生命周期
servlet生命周期在servlet的代碼里能很清楚的體現(xiàn)出來(lái)。代碼為:
public interface Servlet {
//servlet 初始化,這里摘錄了部分注釋
/**
* The servlet container calls the <code>init</code> method exactly once
* after instantiating the servlet. The <code>init</code> method must
* complete successfully before the servlet can receive any requests.
*/
public void init(ServletConfig config) throws ServletException;
/**
*
* Returns a {@link ServletConfig} object, which contains initialization and
* startup parameters for this servlet. The <code>ServletConfig</code>
* object returned is the one passed to the <code>init</code> method.
*
* <p>
* Implementations of this interface are responsible for storing the
* <code>ServletConfig</code> object so that this method can return it. The
* {@link GenericServlet} class, which implements this interface, already
* does this.
*
* @return the <code>ServletConfig</code> object that initializes this
* servlet
*
* @see #init
*/
public ServletConfig getServletConfig();
/**
* Called by the servlet container to allow the servlet to respond to a
* request.
*
* <p>
* This method is only called after the servlet's <code>init()</code> method
* has completed successfully.
*
* <p>
* The status code of the response always should be set for a servlet that
* throws or sends an error.
*/
//真正的處理請(qǐng)求
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/**
* Returns information about the servlet, such as author, version, and
* copyright.
*
* <p>
* The string that this method returns should be plain text and not markup
* of any kind (such as HTML, XML, etc.).
*
* @return a <code>String</code> containing servlet information
*/
public String getServletInfo();
/**
* Called by the servlet container to indicate to a servlet that the servlet
* is being taken out of service. This method is only called once all
* threads within the servlet's <code>service</code> method have exited or
* after a timeout period has passed. After the servlet container calls this
* method, it will not call the <code>service</code> method again on this
* servlet.
*
* <p>
* This method gives the servlet an opportunity to clean up any resources
* that are being held (for example, memory, file handles, threads) and make
* sure that any persistent state is synchronized with the servlet's current
* state in memory.
*/
public void destroy();
}
從代碼中可以看出,servlet生命周期是init-->service-->destory;
具體過程
1、加載與實(shí)例化(new)
servlet在啟動(dòng)時(shí)或第一次接收到請(qǐng)求時(shí),會(huì)到內(nèi)存中去查詢一下是否有servlet實(shí)例,有則取出來(lái),沒有則new一個(gè)出來(lái)。
2、初始化(init)
在創(chuàng)建servlet之后,會(huì)調(diào)用init方法,進(jìn)行初始化,該步主要是為接收處理請(qǐng)求做一些必要的準(zhǔn)備,只會(huì)執(zhí)行一次。
3、提供服務(wù)(service)
servlet實(shí)例接收客戶端的ServletRequest信息,通過request的信息,調(diào)用相應(yīng)的doXXX()方法,并返回response。
4、銷毀(destroy)
servlet容器在關(guān)閉時(shí),銷毀servlet實(shí)例。只會(huì)執(zhí)行一次
后面以SpringMVC為例來(lái)具體說(shuō)明servlet的生命周期。
SpringMVC相關(guān)知識(shí)
這里只簡(jiǎn)單的羅列一下SpringMVC中M,V,C 的關(guān)系。如圖:
1、客戶端發(fā)送request請(qǐng)求進(jìn)入到分發(fā)器中,DispatcherServlet中。
2、分發(fā)器通過uri到控制器映射中去查詢相應(yīng)的處理器(HandlerMapping)。
3、分發(fā)器拿到對(duì)應(yīng)的處理器之后,調(diào)用處理器響應(yīng)的接口,返回?cái)?shù)據(jù)及對(duì)應(yīng)的視圖。
4、分發(fā)器拿到處理器返回的modelAndView后,查找相應(yīng)的視同解析器進(jìn)行渲染。
5、視圖將渲染好的結(jié)果返回給終端用戶進(jìn)行展示。
下面通過源碼的方式看springboot中關(guān)于springMVC和servlet相關(guān)的實(shí)現(xiàn)。
請(qǐng)求消息在SpringMVC中的流向
servlet的創(chuàng)建過程(new)
源碼閱讀接上篇文章,現(xiàn)在到了servlet的處理了。在這之前,看servlet在springboot中是如何完成實(shí)例化及初始化的。
首先看servlet是如何new出來(lái)的。springboot中,使用的是dispatcherServlet,DispatcherServlet的類的繼承關(guān)系圖如下
該類使用自動(dòng)裝配的方式初始化一個(gè)beanName為
dispatcherServlet
的bean。類名為DispatcherServletAutoConfiguration
,具體代碼如下:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
這里就是直接new出來(lái)一個(gè)servlet,并初始化相關(guān)的配置。
這個(gè)servlet對(duì)象是如何注冊(cè)到tomcat容器里呢,這里就有下面一個(gè)名稱為dispatcherServletRegistration
的bean來(lái)做的事情了,具體代碼為:
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
這里需要看一下DispatcherServletRegistrationBean
類的繼承關(guān)系圖了。
從圖中看出,
DispatcherServletRegistrationBean
實(shí)現(xiàn)了接口ServletContextInitializer
,SpringBoot內(nèi)嵌tomcat代碼走讀(一)中,有對(duì)這個(gè)接口的調(diào)用代碼。this.webServer = factory.getWebServer(getSelfInitializer());
在獲取webServer
時(shí),傳入想改接口的調(diào)用。接下來(lái)我們看DispatcherServletRegistrationBean的
OnStartup
的實(shí)現(xiàn),該方法實(shí)現(xiàn)在其父類RegistrationBean
中。
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
主要方法register
的實(shí)現(xiàn)在其子類DynamicRegistrationBean
中。
@Override
protected final void register(String description, ServletContext servletContext) {
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}
其主要方法addRegistration
在其子類ServletRegistrationBean
中實(shí)現(xiàn)。
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
return servletContext.addServlet(name, this.servlet);
}
servletContext
為ApplicationContextFacade
,addServlet
代碼為:
@Override
public ServletRegistration.Dynamic addServlet(String servletName,
Servlet servlet) {
if (SecurityUtil.isPackageProtectionEnabled()) {
return (ServletRegistration.Dynamic) doPrivileged("addServlet",
new Class[]{String.class, Servlet.class},
new Object[]{servletName, servlet});
} else {
return context.addServlet(servletName, servlet);
}
}
Tomcat的ApplicationContext里加入servlet。代碼為:
private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
Servlet servlet, Map<String,String> initParams) throws IllegalStateException {
if (servletName == null || servletName.equals("")) {
throw new IllegalArgumentException(sm.getString(
"applicationContext.invalidServletName", servletName));
}
if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
//TODO Spec breaking enhancement to ignore this restriction
throw new IllegalStateException(
sm.getString("applicationContext.addServlet.ise",
getContextPath()));
}
Wrapper wrapper = (Wrapper) context.findChild(servletName);
// Assume a 'complete' ServletRegistration is one that has a class and
// a name
if (wrapper == null) {
wrapper = context.createWrapper();
wrapper.setName(servletName);
context.addChild(wrapper);
} else {
if (wrapper.getName() != null &&
wrapper.getServletClass() != null) {
if (wrapper.isOverridable()) {
wrapper.setOverridable(false);
} else {
return null;
}
}
}
ServletSecurity annotation = null;
if (servlet == null) {
wrapper.setServletClass(servletClass);
Class<?> clazz = Introspection.loadClass(context, servletClass);
if (clazz != null) {
annotation = clazz.getAnnotation(ServletSecurity.class);
}
} else {
wrapper.setServletClass(servlet.getClass().getName());
wrapper.setServlet(servlet);
if (context.wasCreatedDynamicServlet(servlet)) {
annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
}
}
if (initParams != null) {
for (Map.Entry<String, String> initParam: initParams.entrySet()) {
wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
}
}
ServletRegistration.Dynamic registration =
new ApplicationServletRegistration(wrapper, context);
if (annotation != null) {
registration.setServletSecurity(new ServletSecurityElement(annotation));
}
return registration;
}
這里主要邏輯是向tomcat里添加servlet。前面我們知道tomcat的體系結(jié)構(gòu),為了提高擴(kuò)展性,tomcat里分層定義了很多組件,在最內(nèi)層組件為Wrapper,這里就是想warapper中添加servlet。這里用默認(rèn)的wrapper,StandardWrapper
,具體代碼為:
@Override
public void setServlet(Servlet servlet) {
instance = servlet;
}
至此,tomcat的基本功能就有了,servlet也有了。但是,當(dāng)前的servlet還不能用,因?yàn)閺纳厦娴膕ervlet生命周期看,這只完成了第一步,還有init這一步?jīng)]有完成。那么,init在什么時(shí)候呢。繼續(xù)看代碼。
從代碼中可以知道,當(dāng)?shù)谝粋€(gè)請(qǐng)求過來(lái)時(shí),servlet才進(jìn)行init操作。接上篇文章的請(qǐng)求流程圖,這里看請(qǐng)求在servlet中的流向。這里流程圖從StandardWrapperValue的invoke方法開始進(jìn)行。
調(diào)用流程圖如下:
按照流程圖,我們走讀一下代碼,具體看看相關(guān)的處理邏輯。
首先看invoke
方法里的代碼邏輯。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
requestCount.incrementAndGet();
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
//去掉一些與主流程關(guān)系不太大的代碼
.......
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
。。。。。。
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
//獲取一個(gè)ApplicationFilterChain
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
。。。。。。
} else {
// 開始處理請(qǐng)求
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// 開始處理請(qǐng)求
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
} catch (ClientAbortException | CloseNowException e) {
。。。。。。
}
}
上面代碼比較長(zhǎng),做了一下刪減。主要的功能是初始化servlet(init)和具體調(diào)用servlet的service
方法。
servlet
的init
方法在allocate()
中調(diào)用。service
方法在doFilter
中調(diào)用。
servlet初始化過程(init)
具體看下allocate()
的實(shí)現(xiàn)。
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
//該處并不知道singleThreadModel的值,使用默認(rèn)值,在loadServlet中會(huì)有確定的賦值,但SingleThreadedModel已經(jīng)被廢棄,這里也走不到該分支中,所以,springboot中的servlet是一個(gè)實(shí)例。
//當(dāng)為SingleThreadedModel類型時(shí),容器中會(huì)有多個(gè)servlet實(shí)例,默認(rèn)最多20個(gè)。
if (!singleThreadModel) {
// Load and initialize our instance if necessary
//前文提到了instance在bean初始化時(shí)已經(jīng)將instance賦值了,這里instance!=null,但instanceInitialized=false
if (instance == null || !instanceInitialized) {
synchronized (this) {
//該分支在該處不會(huì)走到。
if (instance == null) {
。。。。。。
}
if (!instanceInitialized) {
//init servlet
initServlet(instance);
}
}
}
if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (log.isTraceEnabled()) {
log.trace(" Returning non-STM instance");
}
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
}
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
allocate方法中,instance !=null,singleThreadModel 一直都為false,因?yàn)椴粫?huì)走到loadServlet中,singleThreadModel的值一直為false。容器中servlet實(shí)例只有一個(gè)。提一句:如果singleThreadModel類型為SingleThreadModel時(shí),容器中可以有多個(gè)servlet實(shí)例,放在一個(gè)大小為20的棧中。SingleThreadModel已經(jīng)被廢棄掉了
@deprecated As of Java Servlet API 2.4, with no direct replacement.
。
下面看initServlet()
方法的處理邏輯。
private synchronized void initServlet(Servlet servlet)
throws ServletException {
if (instanceInitialized && !singleThreadModel) return;
// Call the initialization method of this servlet
try {
if( Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init",
servlet,
classType,
args);
success = true;
} finally {
if (!success) {
// destroy() will not be called, thus clear the reference now
SecurityUtil.remove(servlet);
}
}
} else {
servlet.init(facade);
}
instanceInitialized = true;
} catch (UnavailableException f) {
unavailable(f);
throw f;
} catch (ServletException f) {
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw f;
} catch (Throwable f) {
ExceptionUtils.handleThrowable(f);
getServletContext().log(sm.getString("standardWrapper.initException", getName()), f);
// If the servlet wanted to be unavailable it would have
// said so, so do not call unavailable(null).
throw new ServletException
(sm.getString("standardWrapper.initException", getName()), f);
}
}
這段代碼很簡(jiǎn)單,就是調(diào)用servlet的init方法。
具體的init方法實(shí)現(xiàn)在其父類GenericServlet
中。
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
這里的init方法在子類HttpServletBean
中。
// Let subclasses do whatever initialization they like.
initServletBean();
}
initServletBean()
方法在子類FrameworkServlet
中。
@Override
protected final void initServletBean() throws ServletException {
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
}
initFrameworkServlet()
是留給子類實(shí)現(xiàn)的一個(gè)擴(kuò)展接口,默認(rèn)為空實(shí)現(xiàn),主要的邏輯在initWebApplicationContext()
中,該方法主要是初始化一些策略,如文件解析器,國(guó)際化解析器,主題解析器,處理器映射器,處理器適配器,異常處理器,視圖解析器等。具體的實(shí)現(xiàn)在子類DispatcherServlet
中。
代碼如下:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
//文件解析器,沒有,返回為空。
initMultipartResolver(context);
//國(guó)際化解析,沒有,返回默認(rèn)值,AcceptHeaderLocaleResolver
initLocaleResolver(context);
//主題解析,沒有返回默認(rèn)值FixedThemeResolver
initThemeResolver(context);
//處理器映射,若沒有,返回默認(rèn)值BeanNameUrlHandlerMapping,RequestMappingHandlerMapping,RouterFunctionMapping
initHandlerMappings(context);
//處理器適配器,若沒有,返回默認(rèn)值HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter,HandlerFunctionAdapter
initHandlerAdapters(context);
//異常解析,若沒有,返回默認(rèn)值ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver,DefaultHandlerExceptionResolver
initHandlerExceptionResolvers(context);
//異常后,沒有返回視圖時(shí)的默認(rèn)處理DefaultRequestToViewNameTranslator
initRequestToViewNameTranslator(context);
//視圖解析,沒有返回InternalResourceViewResolver
initViewResolvers(context);
//默認(rèn)值SessionFlashMapManager
initFlashMapManager(context);
}
后面通過看具體的請(qǐng)求處理來(lái)看上面的處理器如何工作的。
到此,servlet的init方法正式處理完成,就可以提供服務(wù)了。
servlet提供服務(wù)過程(service)
下面,看service
如何處理。
從前文中的代碼中可以知道,在調(diào)用service
之前,需要獲取一個(gè)ApplicationFilterChain
,先來(lái)看看ApplicationFilterFactory
中createFilterChain
里做了哪些事情。從類名中可以猜到,這里主要是添加一些Filter
相關(guān)的信息。具體代碼為:
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
//獲取Filter列表
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return filterChain;
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMap, requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMap, servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}
其中比較重要的一行代碼FilterMap filterMaps[] = context.findFilterMaps();
,獲取過濾器列表。
@Override
public FilterMap[] findFilterMaps() {
return filterMaps.asArray();
}
這里filterMaps
是在什么時(shí)候進(jìn)行賦值的呢?此處又會(huì)回到ServletContextInitializer
這個(gè)接口的onStartup
方法里了。
前文里提到過,在createWebServer
時(shí),有個(gè)方法引用getSelfInitializer
。這里同樣走到這段代碼里。
Filter相關(guān)的準(zhǔn)備工作
我們?cè)谕ǔT趕pringboot里定義過濾器時(shí),可以使用如下代碼:
@Bean
public FilterRegistrationBean xxxFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
XxxFilter xxxFilter = new XxxFilter();
registrationBean.addUrlPatterns("/*");
registrationBean.setFilter(xxxFilter);
return registrationBean;
}
看下FilterRegistrationBean
的類圖:
FilterRegistrationBean同樣實(shí)現(xiàn)了ServletContextInitializer接口。
同時(shí),我們還可以進(jìn)行如下方式定義過濾器。
@Service
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
這種方式?jīng)]有顯式的定義成一個(gè)FilterRegistrationBean
,在代碼中事實(shí)上也封裝成一個(gè)FilterRegistrationBean
。接下來(lái)看具體的代碼實(shí)現(xiàn)。
首先看ServletContextInitializer
的onStartup
的調(diào)用處代碼。
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
getServletContextInitializerBeans()
里從bean工廠里獲取ServletContextInitializer
類型的bean,調(diào)用onStartup
方法。
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
//顯式定義為ServletContextInitializer的bean
addServletContextInitializerBeans(beanFactory);
//未顯式定義為ServletContextInitializer的bean
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
.collect(Collectors.toList());
this.sortedList = Collections.unmodifiableList(sortedInitializers);
logMappings(this.initializers);
}
顯式定義為ServletContextInitializer的bean的處理方法:
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
initializer);
}
}
private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory, Object source) {
this.initializers.add(type, initializer);
if (source != null) {
// Mark the underlying source as seen in case it wraps an existing bean
this.seen.add(source);
}
if (logger.isTraceEnabled()) {
String resourceDescription = getResourceDescription(beanName, beanFactory);
int order = getOrder(initializer);
logger.trace("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order="
+ order + ", resource=" + resourceDescription);
}
}
未顯式定義為ServletContextInitializer的bean
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
for (Entry<String, B> entry : entries) {
String beanName = entry.getKey();
B bean = entry.getValue();
if (this.seen.add(bean)) {
// One that we haven't already seen
//通過適配器模式,進(jìn)行處理。
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
int order = getOrder(bean);
registration.setOrder(order);
this.initializers.add(type, registration);
if (logger.isTraceEnabled()) {
logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
+ order + ", resource=" + getResourceDescription(beanName, beanFactory));
}
}
}
}
private static class FilterRegistrationBeanAdapter implements RegistrationBeanAdapter<Filter> {
@Override
public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(source);
bean.setName(name);
return bean;
}
}
代碼比較簡(jiǎn)單,這里只做代碼的羅列,不做詳細(xì)說(shuō)明。
接下來(lái)看FilterRegistrationBean
里onStartup
方法的處理邏輯,該方法的實(shí)現(xiàn)在其父類RegistrationBean
中。
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
register
方法在RegistrationBean
子類DynamicRegistrationBean
中。
@Override
protected final void register(String description, ServletContext servletContext) {
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}
addRegistration
在其子類AbstractFilterRegistrationBean
中實(shí)現(xiàn)。
@Override
protected Dynamic addRegistration(String description, ServletContext servletContext) {
Filter filter = getFilter();
return servletContext.addFilter(getOrDeduceName(filter), filter);
}
addFilter
代碼走到ApplicationContextFacade
中。
@Override
public FilterRegistration.Dynamic addFilter(String filterName,
Filter filter) {
if (SecurityUtil.isPackageProtectionEnabled()) {
return (FilterRegistration.Dynamic) doPrivileged("addFilter",
new Class[]{String.class, Filter.class},
new Object[]{filterName, filter});
} else {
return context.addFilter(filterName, filter);
}
}
最后走到ApplicationContext
中。
private FilterRegistration.Dynamic addFilter(String filterName,
String filterClass, Filter filter) throws IllegalStateException {
FilterDef filterDef = context.findFilterDef(filterName);
if (filterDef == null) {
filterDef = new FilterDef();
filterDef.setFilterName(filterName);
context.addFilterDef(filterDef);
} else {
if (filterDef.getFilterName() != null &&
filterDef.getFilterClass() != null) {
return null;
}
}
......
return new ApplicationFilterRegistration(filterDef, context);
}
最后將FilterDef加入到StandradContext
的filterDefs
中。
回過頭來(lái),看DynamicRegistrationBean中register
的第二個(gè)主要調(diào)用的方法configure()
對(duì)于過濾器來(lái)說(shuō),實(shí)現(xiàn)方法在AbstractFilterRegistrationBean
中。
@Override
protected void configure(FilterRegistration.Dynamic registration) {
super.configure(registration);
EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
if (dispatcherTypes == null) {
T filter = getFilter();
if (ClassUtils.isPresent("org.springframework.web.filter.OncePerRequestFilter",
filter.getClass().getClassLoader()) && filter instanceof OncePerRequestFilter) {
dispatcherTypes = EnumSet.allOf(DispatcherType.class);
}
else {
dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
}
}
Set<String> servletNames = new LinkedHashSet<>();
for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
servletNames.add(servletRegistrationBean.getServletName());
}
servletNames.addAll(this.servletNames);
if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);
}
else {
if (!servletNames.isEmpty()) {
registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
StringUtils.toStringArray(servletNames));
}
if (!this.urlPatterns.isEmpty()) {
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
StringUtils.toStringArray(this.urlPatterns));
}
}
}
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);
這里的方法將每個(gè)filter通過StandardContext的addFilterMapBefore
方式,塞到filterMap中的arrays中。通過findFilterMaps
就能得到所有的filter了。
到此,F(xiàn)ilter相關(guān)的準(zhǔn)備工作完成。
需要補(bǔ)充上面流程的流程圖。
回到service
回到前文的ApplicationFilterChain
的獲取上。ApplicationFilterFactory
里通過context.findFilterMaps();
獲取所有的過濾器,放入filterChain中。
StandardWrapperValue
中代碼繼續(xù)往下走,調(diào)用了ApplicationFilterChain的doFilter
方法。
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 遍歷所有的filter,這里就是為啥我們?cè)诙xFilter時(shí),為了讓chain能順利的走完需要調(diào)用chain.doFilter()的原因。
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
。。。。。。
} else {
filter.doFilter(request, response, this);
}
}
。。。。。。
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
。。。。。。
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} 。。。。。。
。。。。。。
}
}
以上代碼就進(jìn)入到了servlet的另外一個(gè)生命周期了,提供service服務(wù)了。
下篇文章,我們具體看下service接口里的邏輯。