1清钥、HttpServletBean
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware
HttpServletBean 繼承了HttpServlet,所以它只是在功能上對 HttpServlet 進(jìn)行了一些擴(kuò)展同眯。
init() 方法,覆蓋了她的祖先類GenericServlet 中定義的空實現(xiàn)白对,并且這個方法會在容器初始化每個servlet的時候調(diào)用一次怔匣。
HttpServletBean 是在HttpServlet 的基礎(chǔ)上提供了把 servlet 配置中相關(guān)的一些屬性、參數(shù)設(shè)置到成員變量上的功能亏狰,這么做的好處是可以很方便的通過 getter/setter 方法獲取參數(shù)值役纹,而不是通過一個通用的 Map 去獲取。
/**
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
2暇唾、FrameworkServlet
為什么是到這個類的 initServletBean() 方法呢促脉?
因為根據(jù)繼承關(guān)系,DispatcherServlet 繼承 FrameworkServlet策州,DispatcherServlet是在配置文件中配置的瘸味。貌似不對,initServletBean() 方法有注釋抽活,Let subclasses do whatever initialization they like硫戈。
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
protected WebApplicationContext initWebApplicationContext()
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent)
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac)
public String getNamespace() {
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
然后回到 FrameworkServlet 的 initWebApplicationContext() 方法,繼續(xù)向下執(zhí)行
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
3下硕、DispatcherServlet
public class DispatcherServlet extends FrameworkServlet
這個實現(xiàn)調(diào)用初始化策略
/**
* This implementation calls {@link #initStrategies}.
*/
@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);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
initHandlerMappings 默認(rèn)會先探測 ApplicationContext 對象中已經(jīng)設(shè)置好的任何HandlerMapping對象丁逝,如果有就使用定義好的汁胆,如果沒有則調(diào)用方法getDefaultStrategies,使用默認(rèn)配置霜幼。DispathcerServlet.properties 文件中默認(rèn)的 BeanNameUrlHandlerMapping 和 DefaultAnnotationHandlerMapping 兩個HandlerMapping 對象嫩码。
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface)
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
4、AbstractAutowireCapableBeanFactory
public <T> T createBean(Class<T> beanClass) throws BeansException {
// Use prototype bean definition, to avoid registering bean as dependent bean.
RootBeanDefinition bd = new RootBeanDefinition(beanClass);
bd.setScope(SCOPE_PROTOTYPE);
bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
return (T) createBean(beanClass.getName(), bd, null);
}
看下 DefaultAnnotationHandlerMapping 的層次關(guān)系罪既,BeanNameUrlHandlerMapping 類層次關(guān)系是一樣的铸题。
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered
public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware
public abstract class ApplicationObjectSupport implements ApplicationContextAware
5、ApplicationObjectSupport
public abstract class ApplicationObjectSupport implements ApplicationContextAware
抽象類 ApplicationObjectSupport 實現(xiàn)了 ApplicationContextAware 接口琢感,spring容器的后置處理器會調(diào)用 setApplicationContext 方法丢间。
DefaultAnnotationHandlerMapping 實例化,父類同樣實例化驹针。
public final void setApplicationContext(ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// Reset internal context state.
this.applicationContext = null;
this.messageSourceAccessor = null;
}
else if (this.applicationContext == null) {
// Initialize with passed-in context.
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
}
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
}
else {
// Ignore reinitialization if same context passed in.
if (this.applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this.applicationContext + "], passed-in one is [" + context + "]");
}
}
}
initApplicationContext(context); 會先執(zhí)行子類 WebApplicationObjectSupport 的 initApplicationContext 方法烘挫,然后執(zhí)行自己的 initApplicationContext 方法,ApplicationObjectSupport 本身的該方法沒有任何處理柬甥,只是調(diào)用了一個空的 initApplicationContext 方法饮六,這個無參的重載方法被當(dāng)作一個鉤子供子類方法來實現(xiàn)。
6苛蒲、AbstractDetectingUrlHandlerMapping
spring 默認(rèn)的兩個處理器映射類都繼承自 AbstractDetectingUrlHandlerMapping卤橄,并且類初始化將被執(zhí)行的 initApplicationContext 方法也在這個類中得到實現(xiàn)。
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
先執(zhí)行超類的 initApplicationContext 方法臂外,這個超類的方法完成的任務(wù)就是對定義好的累計惡氣改裝并放入到 adaptedInterceptors 數(shù)組中供以后使用窟扑。
detectHandlers(); 請求與處理器映射的關(guān)鍵方法。
7寄月、WebApplicationObjectSupport
protected void initApplicationContext(ApplicationContext context) {
super.initApplicationContext(context);
if (this.servletContext == null && context instanceof WebApplicationContext) {
this.servletContext = ((WebApplicationContext)context).getServletContext();
if (this.servletContext != null) {
this.initServletContext(this.servletContext);
}
}
}
8辜膝、回到 AbstractDetectingUrlHandlerMapping
/**
* Register all handlers found in the current ApplicationContext.
* <p>The actual URL determination for a handler is up to the concrete
* {@link #determineUrlsForHandler(String)} implementation. A bean for
* which no such URLs could be determined is simply not considered a handler.
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
* @see #determineUrlsForHandler(String)
*/
protected void detectHandlers() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
// 注冊處理器,即添加一些映射關(guān)系到 handlerMap中漾肮,LinkedHashMap
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
9厂抖、AbstractUrlHandlerMapping
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
if (urlPath.equals("/")) {
if (logger.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
在容器初始化時會建立所有 url 和 controller 的對應(yīng)關(guān)系,保存到 Map<url, controller> 中克懊。Tomcat啟動時會通知spring初始化容器(加載bean定義信息和初始化所有單例bean)忱辅,然后springmvc會遍歷容器中的bean,獲取每一個controller‘中的所有方法訪問的url谭溉,然后將url和controller保存到一個Map中墙懂。
1、ApplicationContext 初始化時建立所有url和controller類的對應(yīng)管理(用Map保存)
2扮念、根據(jù)請求url找到對應(yīng)的controller损搬,并從controller中找到處理請求的方法
3、request參數(shù)綁定到方法的形參,執(zhí)行方法處理結(jié)果巧勤,返回結(jié)果視圖
但在某些特殊情況下嵌灰,bean需要實現(xiàn)某個功能,但該功能必須借助于spring容器才能實現(xiàn)颅悉,此時就必須讓該bean獲取它所在的spring容器沽瞭,可以讓該bean實現(xiàn)ApplicationContextAwire接口。
必須在spring配置文件中指定該類剩瓶,@Component
spring容器會檢測容器中的所有bean驹溃,如果發(fā)現(xiàn)某個bean實現(xiàn)了ApplicationContextAwire接口,spring容器會在創(chuàng)建該bean之后,自動調(diào)用該bean的setApplicationContextAware() 方法延曙,調(diào)用該方法時豌鹤,會將容器本身作為參數(shù)傳遞給該方法,該方法的實現(xiàn)部分將spring傳入的參數(shù)(容器本身)賦給對象的實例變量applicationContext枝缔,因此接下來可以通過該applicationContext實例變量訪問容器本身傍药。
注解 @Component
為什么ApplicationObjectSupport 沒有@Component注解,而自定義的SpringContextUtis 卻必須有注解魂仍?
因為DefaultAnnotationHandlerMapping 實例化,父類同樣實例化拣挪。
10擦酌、DefaultAnnotationHandlerMapping
determineUrlsForHandler 方法,查看定義的 RequestMapping 注解菠劝,然后把符合要求的urls返回赊舶。