Web.xml
在任何基于Struts人Web程序中,第一项需要配置的就是Web.xml,将所有的请求交给StrutsPrepareAndExecuteFilter过滤器进行过滤。
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
StrutsPrepareAndExecuteFilter 初始化
StrutsPrepareAndExecuteFilter是实现的Filter接口的过滤器,必须实现init, doFilter和Destory方法。init方法主要任务是初始化Filter相关参数,加载配置参数,很显然对Struts.xml文件的解析也是在该方法调用的过程中完成的。
下面我们就来剖析一下init方法
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns = null;
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
try {
# FilterHostConfig对FilterConfig新型简易封装
FilterHostConfig config = new FilterHostConfig(filterConfig);
# 初始化 Struts 日志类
init.initLogging(config);
# Dispatcher 封装ServletContext和FilterConfig, 会调用dispatcher的init 方法
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}
InitOperation类
public class InitOperations {
public InitOperations() {
}
// 创建Dispatch对象
public Dispatcher initDispatcher( HostConfig filterConfig ) {
Dispatcher dispatcher = createDispatcher(filterConfig);
dispatcher.init();
return dispatcher;
}
private Dispatcher createDispatcher( HostConfig filterConfig ) {
//保存Web.xml中filter的配置参数
Map<String, String> params = new HashMap<String, String>();
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
return new Dispatcher(filterConfig.getServletContext(), params);
}
//其他方法
}
Dispatcher 类
private ServletContext servletContext;
private Map<String, String> initParams;
private ConfigurationManager configurationManager;
public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
this.servletContext = servletContext;
this.initParams = initParams;
}
//主角登场, 读取并解析配置文件
public void init() {
if (configurationManager == null) {
configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
#BeanSelectionProver.DEFAULT_BEAN_NAME 就是struts
}
try {
# 都会调用 configurationManager.addContainerProvider方法
init_FileManager();
init_DefaultProperties();
init_TraditionalXmlConfigurations(); //完成struts-default.xml,struts-plugin.xml,struts.xml相关配置对象ConfigurationProvider创建,并保存在ConfigurationManager的集合中,由此我们也知道了这三者的先后加载顺序了
init_LegacyStrutsProperties();
init_CustomConfigurationProviders();
init_FilterInitParameters() ;
init_AliasStandardObjects() ;
Container container = init_PreloadConfiguration();//完成配置对象初始化
container.inject(this);
init_CheckWebLogicWorkaround(container);
if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
private void init_TraditionalXmlConfigurations() {
String configPaths = initParams.get("config");
if (configPaths == null) {
# struts-default.xml,struts-plugin.xml,struts.xml
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split("\\s*[,]\\s*");
for (String file : files) {
if (file.endsWith(".xml")) {
if ("xwork.xml".equals(file)) {
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
} else {
# 调用 createStrutsXmlConfigurationProvider configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}
protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}
private Container init_PreloadConfiguration() {
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
ContainerHolder.store(container);
return container;
}
StrutsXmlConfigurationProvider类
在init_TraditionalXmlConfigurations方法调用是会创建三个StrutsXmlConfigurationProvider对象,其filename分别是struts-default.xml,struts-plugin.xml, struts.xml
public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {
private File baseDir = null;
private String filename;
private ServletContext servletContext;
public StrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
super(filename, errorIfMissing);
this.servletContext = ctx;
this.filename = filename;
reloadKey = "configurationReload-"+filename;
……
File file = new File(filename);
if (file.getParent() != null) {
this.baseDir = file.getParentFile();
}
}
//其他属性和方法
}
XmlConfigurationProvider类
public class XmlConfigurationProvider implements ConfigurationProvider {
private String configFileName;
private boolean errorIfMissing;
public XmlConfigurationProvider(String filename, boolean errorIfMissing) {
this.configFileName = filename;
this.errorIfMissing = errorIfMissing;
……
setDtdMappings(mappings);
}
ConfigurationProvider接口
public interface ConfigurationProvider extends ContainerProvider, PackageProvider {
}
ContainerProvider 接口
public interface ContainerProvider {
public void destroy();
public void init(Configuration configuration) throws ConfigurationException;
public boolean needsReload();
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
}
ConfigurationManager类
public class ConfigurationManager {
protected Configuration configuration;
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
private List<PackageProvider> packageProviders = new CopyOnWriteArrayList<PackageProvider>();
protected String defaultFrameworkBeanName;
public synchronized Configuration getConfiguration() {
if (configuration == null) {
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
# 调用DefaultConfiguration的reloadContainer方法
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
conditionalReload();
}
return configuration;
}
public synchronized void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
//用集合List保存所用配置相关的ContainerProvider
public void addContainerProvider(ContainerProvider provider) {
if (!containerProviders.contains(provider)) {
containerProviders.add(provider);
}
}
DefaultConfiguration类
public class DefaultConfiguration implements Configuration {
protected static final Logger LOG = LoggerFactory.getLogger(DefaultConfiguration.class);
// Programmatic Action Configurations
protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>();
protected RuntimeConfiguration runtimeConfiguration;
protected Container container;
protected String defaultFrameworkBeanName;
protected Set<String> loadedFileNames = new TreeSet<String>();
protected List<UnknownHandlerConfig> unknownHandlerStack;
//完成集合中所有ConfigurationProvider的解析
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
packageContexts.clear();
loadedFileNames.clear();
List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
ContainerProperties props = new ContainerProperties();
ContainerBuilder builder = new ContainerBuilder();
for (final ContainerProvider containerProvider : providers)
{
//遍历集合,对每个containerProvider进行初始化,保存配置信息,
#对struts.xml文件加载也在此过程中完成(调用XmlConfigurationProvider的init方法和StrutsXmlConfigurationProvider的register方法)
containerProvider.init(this);
containerProvider.register(builder, props);
}
props.setConstants(builder);
builder.factory(Configuration.class, new Factory<Configuration>() {
public Configuration create(Context context) throws Exception {
return DefaultConfiguration.this;
}
});
ActionContext oldContext = ActionContext.getContext();
try {
// Set the bootstrap container for the purposes of factory creation
Container bootstrap = createBootstrapContainer();
setContext(bootstrap);
container = builder.create(false);
setContext(container);
objectFactory = container.getInstance(ObjectFactory.class);
// Process the configuration providers first
for (final ContainerProvider containerProvider : providers)
{
if (containerProvider instanceof PackageProvider) {
container.inject(containerProvider);
((PackageProvider)containerProvider).loadPackages();
packageProviders.add((PackageProvider)containerProvider);
}
}
// Then process any package providers from the plugins
Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
if (packageProviderNames != null) {
for (String name : packageProviderNames) {
PackageProvider provider = container.getInstance(PackageProvider.class, name);
provider.init(this);
provider.loadPackages();
packageProviders.add(provider);
}
}
rebuildRuntimeConfiguration();
} finally {
if (oldContext == null) {
ActionContext.setContext(null);
}
}
return packageProviders;
}
由于篇幅的原因,对Struts.xml解析过程将放在下一篇文章中详细阐述,这是我们使用struts框架是主要的配置文件,很有必要了解struts是如何加载的。