上篇文章走讀了springboot中消息處理進入到servlet里了,這次娜搂,具體走讀了請求消息在servlet里是怎么處理的。這里主要補充servlet和springmvc相關的知識。
servlet相關知識
servlet生命周期
servlet生命周期在servlet的代碼里能很清楚的體現(xiàn)出來风范。代碼為:
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.
*/
//真正的處理請求
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硼婿、加載與實例化(new)
servlet在啟動時或第一次接收到請求時,會到內(nèi)存中去查詢一下是否有servlet實例禽车,有則取出來寇漫,沒有則new一個出來刊殉。
2、初始化(init)
在創(chuàng)建servlet之后州胳,會調(diào)用init方法记焊,進行初始化,該步主要是為接收處理請求做一些必要的準備栓撞,只會執(zhí)行一次遍膜。
3、提供服務(service)
servlet實例接收客戶端的ServletRequest信息瓤湘,通過request的信息瓢颅,調(diào)用相應的doXXX()方法,并返回response弛说。
4挽懦、銷毀(destroy)
servlet容器在關閉時,銷毀servlet實例木人。只會執(zhí)行一次
后面以SpringMVC為例來具體說明servlet的生命周期信柿。
SpringMVC相關知識
這里只簡單的羅列一下SpringMVC中M,V虎囚,C 的關系角塑。如圖:
1、客戶端發(fā)送request請求進入到分發(fā)器中淘讥,DispatcherServlet中圃伶。
2、分發(fā)器通過uri到控制器映射中去查詢相應的處理器(HandlerMapping)蒲列。
3窒朋、分發(fā)器拿到對應的處理器之后,調(diào)用處理器響應的接口蝗岖,返回數(shù)據(jù)及對應的視圖侥猩。
4、分發(fā)器拿到處理器返回的modelAndView后抵赢,查找相應的視同解析器進行渲染欺劳。
5、視圖將渲染好的結果返回給終端用戶進行展示铅鲤。
下面通過源碼的方式看springboot中關于springMVC和servlet相關的實現(xiàn)划提。
請求消息在SpringMVC中的流向
servlet的創(chuàng)建過程(new)
源碼閱讀接上篇文章,現(xiàn)在到了servlet的處理了邢享。在這之前鹏往,看servlet在springboot中是如何完成實例化及初始化的。
首先看servlet是如何new出來的骇塘。springboot中伊履,使用的是dispatcherServlet韩容,DispatcherServlet的類的繼承關系圖如下
該類使用自動裝配的方式初始化一個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出來一個servlet唐瀑,并初始化相關的配置群凶。
這個servlet對象是如何注冊到tomcat容器里呢,這里就有下面一個名稱為dispatcherServletRegistration
的bean來做的事情了介褥,具體代碼為:
@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
類的繼承關系圖了座掘。
從圖中看出,
DispatcherServletRegistrationBean
實現(xiàn)了接口ServletContextInitializer
柔滔,SpringBoot內(nèi)嵌tomcat代碼走讀(一)中溢陪,有對這個接口的調(diào)用代碼。this.webServer = factory.getWebServer(getSelfInitializer());
在獲取webServer
時睛廊,傳入想改接口的調(diào)用形真。接下來我們看DispatcherServletRegistrationBean的
OnStartup
的實現(xiàn),該方法實現(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
的實現(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
中實現(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的體系結構,為了提高擴展性疏遏,tomcat里分層定義了很多組件脉课,在最內(nèi)層組件為Wrapper,這里就是想warapper中添加servlet财异。這里用默認的wrapper倘零,StandardWrapper
,具體代碼為:
@Override
public void setServlet(Servlet servlet) {
instance = servlet;
}
至此戳寸,tomcat的基本功能就有了呈驶,servlet也有了。但是疫鹊,當前的servlet還不能用袖瞻,因為從上面的servlet生命周期看,這只完成了第一步拆吆,還有init這一步?jīng)]有完成虏辫。那么,init在什么時候呢锈拨。繼續(xù)看代碼。
從代碼中可以知道羹唠,當?shù)谝粋€請求過來時奕枢,servlet才進行init操作娄昆。接上篇文章的請求流程圖,這里看請求在servlet中的流向缝彬。這里流程圖從StandardWrapperValue的invoke方法開始進行萌焰。
調(diào)用流程圖如下:
按照流程圖,我們走讀一下代碼谷浅,具體看看相關的處理邏輯扒俯。
首先看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();
//去掉一些與主流程關系不太大的代碼
.......
// 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
//獲取一個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 {
// 開始處理請求
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// 開始處理請求
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}
}
} catch (ClientAbortException | CloseNowException e) {
斋配。孔飒。。许起。十偶。。
}
}
上面代碼比較長园细,做了一下刪減惦积。主要的功能是初始化servlet(init)和具體調(diào)用servlet的service
方法。
servlet
的init
方法在allocate()
中調(diào)用猛频。service
方法在doFilter
中調(diào)用狮崩。
servlet初始化過程(init)
具體看下allocate()
的實現(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的值鹿寻,使用默認值睦柴,在loadServlet中會有確定的賦值,但SingleThreadedModel已經(jīng)被廢棄毡熏,這里也走不到該分支中坦敌,所以,springboot中的servlet是一個實例。
//當為SingleThreadedModel類型時狱窘,容器中會有多個servlet實例杜顺,默認最多20個。
if (!singleThreadModel) {
// Load and initialize our instance if necessary
//前文提到了instance在bean初始化時已經(jīng)將instance賦值了蘸炸,這里instance躬络!=null,但instanceInitialized=false
if (instance == null || !instanceInitialized) {
synchronized (this) {
//該分支在該處不會走到。
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德撬,因為不會走到loadServlet中铲咨,singleThreadModel的值一直為false。容器中servlet實例只有一個蜓洪。提一句:如果singleThreadModel類型為SingleThreadModel時纤勒,容器中可以有多個servlet實例,放在一個大小為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);
}
}
這段代碼很簡單恐仑,就是調(diào)用servlet的init方法泉坐。
具體的init方法實現(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()
是留給子類實現(xiàn)的一個擴展接口,默認為空實現(xiàn)歧斟,主要的邏輯在initWebApplicationContext()
中纯丸,該方法主要是初始化一些策略,如文件解析器静袖,國際化解析器觉鼻,主題解析器,處理器映射器队橙,處理器適配器坠陈,異常處理器萨惑,視圖解析器等。具體的實現(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);
//國際化解析,沒有倾鲫,返回默認值粗合,AcceptHeaderLocaleResolver
initLocaleResolver(context);
//主題解析,沒有返回默認值FixedThemeResolver
initThemeResolver(context);
//處理器映射乌昔,若沒有隙疚,返回默認值BeanNameUrlHandlerMapping,RequestMappingHandlerMapping磕道,RouterFunctionMapping
initHandlerMappings(context);
//處理器適配器供屉,若沒有,返回默認值HttpRequestHandlerAdapter溺蕉,SimpleControllerHandlerAdapter伶丐,RequestMappingHandlerAdapter,HandlerFunctionAdapter
initHandlerAdapters(context);
//異常解析疯特,若沒有哗魂,返回默認值ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver漓雅,DefaultHandlerExceptionResolver
initHandlerExceptionResolvers(context);
//異常后录别,沒有返回視圖時的默認處理DefaultRequestToViewNameTranslator
initRequestToViewNameTranslator(context);
//視圖解析,沒有返回InternalResourceViewResolver
initViewResolvers(context);
//默認值SessionFlashMapManager
initFlashMapManager(context);
}
后面通過看具體的請求處理來看上面的處理器如何工作的邻吞。
到此组题,servlet的init方法正式處理完成,就可以提供服務了抱冷。
servlet提供服務過程(service)
下面崔列,看service
如何處理。
從前文中的代碼中可以知道徘层,在調(diào)用service
之前峻呕,需要獲取一個ApplicationFilterChain
,先來看看ApplicationFilterFactory
中createFilterChain
里做了哪些事情趣效。從類名中可以猜到瘦癌,這里主要是添加一些Filter
相關的信息。具體代碼為:
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
是在什么時候進行賦值的呢?此處又會回到ServletContextInitializer
這個接口的onStartup
方法里了。
前文里提到過斤寇,在createWebServer
時桶癣,有個方法引用getSelfInitializer
。這里同樣走到這段代碼里娘锁。
Filter相關的準備工作
我們在通常在springboot里定義過濾器時牙寞,可以使用如下代碼:
@Bean
public FilterRegistrationBean xxxFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
XxxFilter xxxFilter = new XxxFilter();
registrationBean.addUrlPatterns("/*");
registrationBean.setFilter(xxxFilter);
return registrationBean;
}
看下FilterRegistrationBean
的類圖:
FilterRegistrationBean同樣實現(xiàn)了ServletContextInitializer接口。
同時莫秆,我們還可以進行如下方式定義過濾器间雀。
@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)]有顯式的定義成一個FilterRegistrationBean
,在代碼中事實上也封裝成一個FilterRegistrationBean
镊屎。接下來看具體的代碼實現(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
//通過適配器模式,進行處理用狱。
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;
}
}
代碼比較簡單运怖,這里只做代碼的羅列,不做詳細說明齿拂。
接下來看FilterRegistrationBean
里onStartup
方法的處理邏輯驳规,該方法的實現(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
中實現(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
中捻勉。
回過頭來,看DynamicRegistrationBean中register
的第二個主要調(diào)用的方法configure()
對于過濾器來說刀森,實現(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);
這里的方法將每個filter通過StandardContext的addFilterMapBefore
方式,塞到filterMap中的arrays中研底。通過findFilterMaps
就能得到所有的filter了铸抑。
到此辆它,F(xiàn)ilter相關的準備工作完成钦铁。
需要補充上面流程的流程圖廓啊。
回到service
回到前文的ApplicationFilterChain
的獲取上。ApplicationFilterFactory
里通過context.findFilterMaps();
獲取所有的過濾器乾胶,放入filterChain中抖剿。
StandardWrapperValue
中代碼繼續(xù)往下走朽寞,調(diào)用了ApplicationFilterChain的doFilter
方法。
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 遍歷所有的filter斩郎,這里就是為啥我們在定義Filter時脑融,為了讓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);
}
} 愈污。。轮傍。暂雹。。创夜。
杭跪。。驰吓。涧尿。。檬贰。
}
}
以上代碼就進入到了servlet的另外一個生命周期了姑廉,提供service服務了。
下篇文章偎蘸,我們具體看下service接口里的邏輯庄蹋。