您的位置:首页 > 编程语言 > Java开发

Spring MVC源码解析

2016-05-03 18:39 706 查看

转载请标明出处 http://coderknock.com



XXXAware

XXXAware在Spring中该类接口用于通过Spring自动向XXXAware实现类中实现的setXXX(XXX xxx)方法中注入XXX对象,方便在实现类中调用XXX对象。

例如:

A类需要使用当前的ApplicationContext,那么只要是A实现ApplicationContextAware接口,然后实现接口中的setApplicationContext(ApplicationContext applicationContext)方法,Spring就会自动调用setApplicationContext方法将applicationContext传给A类。

XXXCapable

实现XXXCapable接口后说明该实现类具有提供XXX的能力,当Spring需要XXX时会通过该类的getXXX方法来获取XXX对象。

Environment

Environment具体功能与Servlet中的ServletContext类似。

HttpServletBean中Environment使用的是StandardServletEnvironment。

@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = this.createEnvironment();
}
return this.environment;
}

protected ConfigurableEnvironment createEnvironment() {
return new StandardServletEnvironment();
}


StandardServletEnvironment中封装了ServletContext、ServletConfig、JndiProperty、系统环境变量以及系统属性。

StandardServletEnvironment{
activeProfiles=[

],
defaultProfiles=[
default
],
propertySources=[
servletConfigInitParams,
servletContextInitParams,
jndiProperties,
systemProperties,
systemEnvironment
]
}




具体属性大家可以调试一下查看

转载请标明出处 http://coderknock.com



Servlet在创建时会直接调用无参init方法

HttpServletBean的init方法如下:

@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
try {
// 将web.xml中init-param参数封装到pvs变量中,requiredProperties是指必须的一些参数,没有的话会报错
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
//bw是对DispatcherServlet的封装
//【BeanWrpper是Spring提供的用于操作JavaBean属性的一个工具类,通过它封装一个对象后可以直接修改对象的属性】
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//对DispatcherServlet做一些初始化的工作
initBeanWrapper(bw);
//将配置值设置到DispatcherServlet
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}

// 子类通过该方法初始化【该类中只是声明了该方法】
initServletBean();

if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}

protected void initServletBean() throws ServletException {
}


转载请标明出处 http://coderknock.com

FrameworkServlet,通过之前HttpServletBean详解,我们知道,FrameworkServlet是通过initServletBean来实现自身的初始化的。我们看一下initServletBean:

@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 void initFrameworkServlet() throws ServletException {
}


可以看到这个方法中的核心语句:

this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();


其中initFrameworkServlet是供子类Override后实现自身的一些初始化工作【DispatcherServlet中并没有对initFrameworkServlet进行Override】。

可见,FrameworkServlet在构建工程中主要作用就是初始化webApplicationContext:

protected WebApplicationContext initWebApplicationContext() {
//获取rootContext
WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//判断是否在构造方法中以及初始化了webApplicationContext
if (this.webApplicationContext != null) {
// 一个WebApplicationContext实例在构建实例的时候被注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// 上下文尚未Refresh -> 配置并Refresh
// 设置父上下文,设置WebApplicationContext id等等
if (cwac.getParent() == null) {
// 如果上下文被注入时没有明确的设置Parent,则在这里设置
// 设置rootContext为Parent【可能为null】
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//如果在构建实例时没有注入WebApplicationContext
//查找WebApplicationContext是否已经存在于ServletContext中【通过配置在Servlet中的contextAttribute获取】
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果还是没有WebApplicationContext,那么创建一个
wac = createWebApplicationContext(rootContext);
}

if (!this.refreshEventReceived) {
// 如果还是没有WebApplicationContext 既不是一个ConfigurableApplicationContext支持Refresh,并且WebApplicationContext注入时没有Refresh- >手动触发onRefresh【供子类Override】。
onRefresh(wac);
}

if (this.publishContext) {
// 将WebApplicationContex保存到ServletContext
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;
}

protected void onRefresh(ApplicationContext context) {
// For subclasses: do nothing by default.
}


initWebApplicationContext做了三件事:

1. 获取Spring都得rootContext

Spring默认将rootContext设置于ServletContext的属性中,属性名为
ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";


设置WebApplicationContext并根据情况调用onRefresh方法

设置WebApplicationContext一共三种方法:

一、构造方法传入

这种方法主要用于Servlet3.0以后的环境中,在程序中使用ServletContext.addServlet方式注册Servlet,这时可以在新建FrameworkServlet和其子类时通过构造方法传递WebApplicationContext

二、WebApplicationContext已经存在ServletContext中,这是只要将DispatcherServlet配置是将WebApplicationContext配置到contextAttribute就可以。

三、创建一个WebApplicationContext【最终调用下面的方法】

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
//获取要创建的类型
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
//检查获取到的类似是否是ConfigurableWebApplicationContext的子类
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//具体创建
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

wac.setEnvironment(getEnvironment());
wac.setParent(parent);
//将servlet配置地址【web.xml contextConfigLocation设置】
wac.setConfigLocation(getContextConfigLocation());

configureAndRefreshWebApplicationContext(wac);

return wac;
}

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());
//添加ContextRefreshListener
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();
}


将WebApplicationContext设置到ServletContext中

总结一下可配置项:

1. contextAttribute:在ServletContext中,用作WebApplicationContext的属性名称

2. contextClass:创建的WebApplicationContext的类型

3. contextConfigLocation:SpringMVC配置文件的位置

4. publishContext:是否将WebApplicationContext设置到ServletContext中。

转载请标明出处 http://coderknock.com

通过之前FrameworkServlet的分析,我们知道DispatcherServlet的入口方法是onRefresh。

@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}


onRefresh调用了initStrategies,initStrategies中初始化了九大组件,其中的initXXXX方法基本实现都是通过
WebApplicationContext.getBean(名称, class);
获取注册的对应组件,如果找不到会通过
getDefaultStrategy
方法获取默认的组件。

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);
}
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>();
}
}

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());
}
}


getDefaultStrategy调用了getDefaultStrategies,而getDefaultStrategies是通过defaultStrategies来查找value值的。defaultStrategies对应的是DispatcherServlet.properties:

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


这里的默认设置并不是最优配置,也不是Spring推荐配置,只是为了防止没有配置。

这些默认配置是在没有配置时才会使用,使用
<mvc:annotation-driven/>
之后并不会全部使用默认配置。
<mvc:annotation-driven/>
会配置HandlerMapping、HandlerAdapter、HandlerExceptionResolver等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息