springmvc是一個servlet,controller中單獨寫處理head請求的方法,此方法能夠用來檢查服務器的狀態,由於不返回body因此比get請求更節省網絡資源。java
springmvc的servlet圖node
XXXAware在spring中表示對xxx能夠感知:若是在某個類中想要使用spring的一些東西,能夠實現xxxAware接口告訴spring,spring能夠給你送過來,接收的方法實現接口惟一的方法setxxx,好比有個類要用applicationContext,咱們只需讓它實現applicationContextAware接口,而後實現接口中惟一的方法void setApplicationContext(ApplicationContext applicationContext)就能夠了。web
EnvironmentCapable接口就是具備提供Enviroment的能力,Enviroment getEnvironment();spring
ApplicationContext服務器
Environment網絡
HttpServletBean中Enviroment使用的是Standrad_Servlet_Environment封裝了ServletContext,ServletConfig,JndiProperty,系統環境變量和系統屬性,這些都封裝到了其propertySources屬性下。mvc
ServletContextPropertySource封裝的就是ServletConfig,保存的是ServletContextapp
mandatorywebapp |
英 [ˈmændətəri]ide |
美 [ˈmændətɔ:ri] |
|
adj. |
強制的; 命令的; 受委託的; |
||
n. |
受託者; |
[例句]Attendance is mandatory.
務必參加。
@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");
}
}
httpServletBean的init中,首先將servlet中配置參數使用BeanWrapper設置到DispatcherServlet的相關屬性,而後調用模板方法initServletBean,子類就經過這個方法初始化。
BeanWrapper是什麼,怎麼用?
是spring提供的操做havaBean屬性的工具,使用它能夠直接修改一個對象的屬性。
FrameworkServlet
從httpServletBean中可知,FrameworkServlet的初始化入口方法應該是initServletBean
package org.springframework.web.servlet;
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");
}
}
FrameworkServlet在構建過程當中的主要做用就是初始化webApplicationContext;
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
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);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
initWebApplicationContext方法作了三件事
獲取spring的根容器rootContext,
設置webApplicationContext,並根據狀況調用onRefresh方法
將webApplicationContext設置到ServletContext的attribute中
獲取spring的根容器rootContext
默認狀況下spring會將本身的容器設置成ServletContext的屬性,默認根容器的key爲 org.springframework.web.context.WebApplicationContext.ROOT,定義在org.springframework.web.context.WebApplicationContext中;
獲取根容器只須要調用ServletContext的getAtttribute:
ServletContext#getAttribute(「org.springframework.web.context.WebApplicationContext.ROOT」)
設置webApplicationContext並根據狀況調用onrefresh方法
有三種方法設置webapplicationcontext:
第一種在構造方法中已經傳遞了webApplicatioonContext參數,這時只須要一些設置便可。
第二種方法webapplicationContext已經在servletContext中了,配置servlet的時候將servletContext中的webapplicationContext的name配置到contextAttribute屬性就能夠了。
第三種方法是在前兩種方式都無效的狀況下本身建立一個,正常狀況下就是使用這種方式。在createWebApplicationcontext方法中,內部又調用了configureAndRefreshWebApplicationContext方法。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
在接收到事件後調用一次onRefresh,並將refreshEventReceived標誌置爲true,表示已經refresh過。
上面三種方法無論哪一種,最終都會調用一次refresh方法,而且DispatcherServlet正是經過重寫這個模板來實現初始化的。
前面介紹了配置servlet時能夠設置的一些初始化參數,總結以下:
contextAttribute:在servletContext的屬性中,要作WebApplicationContext的屬性名稱
contextClass:建立webApplicationContext的類型
contextConfigLocation:Springmvc配置文件的位置
publishContext:是否將webApplicationContext設置到ServletContext的屬性。
DispatcherServlet
onRefresh方法是DispatcherServlet的入口方法,onRefresh方法調用了initStrategies,其中調用了9個初始化的方法:
package org.springframework.web.servlet
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
初始化servlet使用的九個組件
/**
* 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);
}
爲何不把initStrategies直接寫到onRefresh中去呢?這是功能分層的緣由,onRefresh是用來刷新的,這樣寫可使邏輯更清晰一些。
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver + "]");
}
}
}
初始化分爲兩步,首先用context.getBean在容器裏面按照註冊的名稱或者類型(localeResolver名稱或者localeResolver.class類型)進行查找,因此在spring mvc的配置文件中只須要配置相應類型的組件,容器就能夠找到。若是找不到,就按照getDefaultStrategy按照類型得到默認的組件。這裏的context指的是frameworkServlet中建立的webApplicationContext,而不是servletContext,
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
//得到須要的策略類型
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<T>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Error loading DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]: problem with class file or dependent class", err);
}
}
return strategies;
}
else {
return new LinkedList<T>();
}
}
classname來自classNames,classnames來自value,value來自defaultStrategy
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
DEFAULT_STRATEGIES_PATH的值是DispatcherServlet.properties中定義的
在同目錄下的properties文件
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
定義了八個組件,multipartResolver是沒有默認配置的,並非每一個應用都須要上傳功能,因此multioartResolver不須要默認配置。
都作了默認配置,也不是spring的推薦配置,只是在沒有配置的時候有個默認值,不至於空着,默認配置是相應類型沒有配置的時候纔會使用,如當使用<mvc:annotation-driven>後,並不會使用默認配置,由於它配置了handlerMapping.handlerAdapter和handler-ExceptionResolver,並且還作了不少別的工做。DispatcherServlet建立過程主要對9大組件進行初始化。
在spring中xml文件中經過命名空間配置的標籤時怎麼解析的
spring xml中能夠有不少命名空間配置的信息,這些命名空間配置是怎麼解析的?對於具體的一個命名空間。spring是如何找到解析他們的類的?
解析標籤的類都放在相應的META-INF目錄下的springhandlers文件中。如mvc命名空間解析設置在spring-webmvc-xxx.jar下META-INF下spring.handlers文件中,內容是
http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
如內部將mvc:annotation-drivern的解析交給AnnotationDrivenBeanDefinitionParser
public interface NamespaceHandler {
void init();
BeanDefinition parse(Element element, ParserContext parserContext);
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
接口Namespacehandler的實現類主要有三個:NamespaceHandlerSupport,SimpleConstructorNamespaceHandler,SimplePropertyNamespaceHandler;
NamespaceHandlerSupport是NamespaceHandler的默認實現類,通常的NamespaceHandler都繼承自這個類;特殊狀況springSecurity的SecurityNamespaceHandler是直接實現的NamespaceHandler接口;SimpleConstructorNamespaceHandler用於統一對C:配置的構造方法進行解析,SimplePropertyNamespaceHandler統一對經過p:配置的參數進行解析。
public class MvcNamespaceHandler extends NamespaceHandlerSupport{}
NamespaceHandlerSupport
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
/**
* Stores the {@link BeanDefinitionParser} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*/
private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>();
/**
* Stores the {@link BeanDefinitionDecorator} implementations keyed by the
* local name of the {@link Element Elements} they handle.
*/
private final Map<String, BeanDefinitionDecorator> decorators =
new HashMap<String, BeanDefinitionDecorator>();
/**
* Stores the {@link BeanDefinitionDecorator} implementations keyed by the local
* name of the {@link Attr Attrs} they handle.
*/
private final Map<String, BeanDefinitionDecorator> attributeDecorators =
new HashMap<String, BeanDefinitionDecorator>();
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Element}.
*/
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
/**
* Decorates the supplied {@link Node} by delegating to the {@link BeanDefinitionDecorator} that
* is registered to handle that {@link Node}.
*/
@Override
public BeanDefinitionHolder decorate(
Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
return findDecoratorForNode(node, parserContext).decorate(node, definition, parserContext);
}
/**
* Locates the {@link BeanDefinitionParser} from the register implementations using
* the local name of the supplied {@link Node}. Supports both {@link Element Elements}
* and {@link Attr Attrs}.
*/
private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
BeanDefinitionDecorator decorator = null;
String localName = parserContext.getDelegate().getLocalName(node);
if (node instanceof Element) {
decorator = this.decorators.get(localName);
}
else if (node instanceof Attr) {
decorator = this.attributeDecorators.get(localName);
}
else {
parserContext.getReaderContext().fatal(
"Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
}
if (decorator == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " +
(node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
}
return decorator;
}
/**
* Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
* handle the specified element. The element name is the local (non-namespace qualified)
* name.
*/
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
/**
* Subclasses can call this to register the supplied {@link BeanDefinitionDecorator} to
* handle the specified element. The element name is the local (non-namespace qualified)
* name.
*/
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
this.decorators.put(elementName, dec);
}
/**
* Subclasses can call this to register the supplied {@link BeanDefinitionDecorator} to
* handle the specified attribute. The attribute name is the local (non-namespace qualified)
* name.
*/
protected final void registerBeanDefinitionDecoratorForAttribute(String attrName, BeanDefinitionDecorator dec) {
this.attributeDecorators.put(attrName, dec);
}
}
NamespaaceHandlerSupport定義了三個處理器parsers,decorators.attributeDecorators分別用於處理解析工做,處理標籤類型,處理屬性類型的裝飾。具體的處理器由子類實現,而後註冊到NamespaceHandlerSupport上面,因此要定義一個命名空間的解析器,只須要在init中定義相應的parsers,decorators.attributeDecorators,並註冊到NamespacehandlerSupport上面。
mvc命名空間的mvcNamespaceHandler的代碼:
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
}
}
從上面代碼能夠看出解析annotation-driven的是子類解析器AnnotationDrivenBeanDefinitionParser,註冊到了NamespaceHandlerSupport的parsers上
上面主要解析了springmvc建立servlet的三個層次HttpServletBean ,FrameworkServlet,DispatcherServlet;HttpServletBean直接繼承了java中的HttpServlet,做用是將相應的servlet的配置參數設置到對應的屬性上,FrameworkServlet初始化了WebApplicationContext,DispatcherServlet初始化自身的9個組件。
spring的特色:結構簡單,實現複雜,結構簡單是頂層設計好,實現複雜是實現的功能比較多,可配置的地方 也很是多。