StrutsPrepareAndExecuteFilter实现了Filter接口 init方法为初始化入口
StrutsPrepareAndExecuteFilter init方法
public void init(FilterConfig filterConfig) throws ServletException {
//初始化辅助类 类似一个Delegate
InitOperations init = new InitOperations();
try {
// FilterHostConfig 封装了FilterConfig参数对象
FilterHostConfig config = new FilterHostConfig(filterConfig);
//LoggerFactory配置加载
//如果没有web.xml 没有配置“loggerFactory”参数 尝试org.apache.commons.logging.LogFactory
//如果失败 使用JdkLoggerFactory
//TODO SPI
init.initLogging(config);
//TODO 创建Dispatcher 注册加载器 执行加载器 创建容器 解析xml
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
//预处理类 请求处理时才会真正用到
//1.主要负责在每次请求 创建ActionContext 清除ActionContext
//2.当接收到一个请求时 通过uri查找 ActionConfig 创建ActionMapping
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
//处理请求 Delegate
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
//空实现 留作扩展
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}
InitOperations 类似与一个Delegate 主要负责实例化Dispatche 再把初始化操作转交给Dispatche init处理
public Dispatcher initDispatcher( HostConfig filterConfig ) {
//创建Dispatcher
Dispatcher dispatcher = createDispatcher(filterConfig);
//核心方法 Container容器的创建 xml解析在此方法发生
dispatcher.init();
return dispatcher;
}
private Dispatcher createDispatcher( HostConfig filterConfig ) {
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 init方法 1.针对配置文件 注册不同的加载器 保存到ConfigurationManager类中的一个变量中 2.创建容器 解析xml
public void init() {
//创建配置操作管理类 , 会保存元素加载器
if (configurationManager == null) {
configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
try {
/**初始化各种形式加载器,保存到ConfigurationManager#containerProviders Map集合中 没有真正执行加载 解析逻辑*/
//org/apache/struts2/default.properties属性文件 里面定义了一系列struts常量
init_DefaultProperties(); // [1]
//web.xml配置的 config参数 [配置多个用","分开]
//如果没有该参数 默认为 struts-default.xml[框架级],struts-plugin.xml[框架级],struts.xml[系统级别]
//根据文件名称 创建加载器 加载xml主要有一下两个解析器
//XmlConfigurationProvider[xwork.xml],
//StrutsXmlConfigurationProvider[struts相关配置文件]配置元素加载器
init_TraditionalXmlConfigurations(); // [2]
//struts.locale 注册
init_LegacyStrutsProperties(); // [3]
//实例化 我们自定义的加载器 保存到containerProviders集合中
// web.xml configProviders参数 多个用","分开 配置器必须是ConfigurationProvider接口的实例
//TODO SPI
init_CustomConfigurationProviders(); // [5]
//web.xml配置的init-param参数 加载器 最终会保存到Container容器中
init_FilterInitParameters() ; // [6]
//TODO 根据我们在struts.xml定义的 常量 选择插件类
//比如集成spring 会用到org.apache.struts2.spring.StrutsSpringObjectFactory
init_AliasStandardObjects() ; // [7]
/** 执行加载器 */
//TODO 创建容器 解析xml 真正执行加载器方法
Container container = init_PreloadConfiguration();
//执行当前Dispatcher对象 依赖关系注入
container.inject(this);
//额外动作
init_CheckConfigurationReloading(container);
init_CheckWebLogicWorkaround(container);
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}
ConfigurationManager 主要管理 创建的各种加载器
public class ConfigurationManager {
protected static final Logger LOG = LoggerFactory.getLogger(ConfigurationManager.class);
//配置元素管理器
protected Configuration configuration;
protected Lock providerLock = new ReentrantLock();
//创建的xml加载器会保存到次集合中
private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
}
Dispatcher的 createConfigurationManager方法
protected ConfigurationManager createConfigurationManager(String name) {
//name - > struts
return new ConfigurationManager(name);
}
1.default.properties 属性文件加载器
private void init_DefaultProperties() {
//保存到ConfigurationManager加载器集合中
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
2.创建struts相关文件加载器 StrutsXmlConfigurationProvider
private void init_TraditionalXmlConfigurations() {
//web.xml 配置的config
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.addConfigurationProvider(createXmlConfigurationProvider(file, false));
} else {
//struts xml加载器
//StrutsXmlConfigurationProvider
configurationManager.addConfigurationProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}
protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
return new XmlConfigurationProvider(filename, errorIfMissing);
}
protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}
3.web.xml扩展的ContainerProviders加载器 实例化
private void init_CustomConfigurationProviders() {
//web.xml 中configProviders 节点
String configProvs = initParams.get("configProviders");
if (configProvs != null) {
String[] classes = configProvs.split("\\s*[,]\\s*");
for (String cname : classes) {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
}
}
}
init_PreloadConfiguration 方法主要完成创建容器, 解析xml动作
private Container init_PreloadConfiguration() {
//创建Container 解析xml
Configuration config = configurationManager.getConfiguration();
Container container = config.getContainer();
boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
LocalizedTextUtil.setReloadBundles(reloadi18n);
return container;
}
init_PreloadConfiguration 方法中调用了 ConfigurationManager的getConfiguration 方法
public synchronized Configuration getConfiguration() {
//创建配置元素管理器
if (configuration == null) {
// defaultFrameworkBeanName - > struts
setConfiguration(createConfiguration(defaultFrameworkBeanName));
try {
// getContainerProviders 返回注册的各种加载器
// reloadContainer 创建Container 解析xml
configuration.reloadContainer(getContainerProviders());
} catch (ConfigurationException e) {
setConfiguration(null);
throw new ConfigurationException("Unable to load configuration.", e);
}
} else {
conditionalReload();
}
return configuration;
}
protected Configuration createConfiguration(String beanName) {
return new DefaultConfiguration(beanName);
}
DefaultConfiguration的reloadContainer方法 会去执行已注册的各种加载器 ,和创建容器
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
packageContexts.clear();
loadedFileNames.clear();
List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
// 保存struts常量
ContainerProperties props = new ContainerProperties();
//容器构建器
ContainerBuilder builder = new ContainerBuilder();
for (final ContainerProvider containerProvider : providers) {
/**
* 初始化Document 准备解析
* 具体在XmlConfigurationProvider实现类 会处理include节点
* 处理完成之后Document会保存到XmlConfigurationProvider#documents list集合中
* include file路径会保存到XmlConfigurationProvider#loadedFileUrls set集合中
* 从代码中发现 include file属性中 可以使用通配符 "*"
*/
/** StrutsXmlConfigurationProvider 是 XmlConfigurationProvider的子类 */
/** StrutsXmlConfigurationProvider struts*.xml */
containerProvider.init(this);
//针对"bean","constant","unknown-handler-stack"节点 不包括"package"节点 解析xml
//每一个bean 对应一个LocatableFactory LocatableFactory保存了bean的定义
//bean定义 保存到ContainerBuilder#factories map集合中
//配置文件中定义的常量 保存到props中
containerProvider.register(builder, props);
}
//将常量保存到ContainerBuilder#factories map集合中
//每一个常量对应一个LocatableConstantFactory
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 {
//创建辅助容器 ContainerImpl并且 实例化 struts一些核心类
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) {
//com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#setObjectFactory(ObjectFactory)
container.inject(containerProvider);
//解析 xml package节点
//保存packageContexts map集合中
//com.opensymphony.xwork2.config.providers.XmlConfigurationProvider line 481
((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);
}
}
//TODO
rebuildRuntimeConfiguration();
} finally {
if (oldContext == null) {
ActionContext.setContext(null);
}
}
return packageProviders;
}
StrutsXmlConfigurationProvider的init方法 具体在父类XmlConfigurationProvider中实现
public void init(Configuration configuration) {
this.configuration = configuration;
this.includedFileNames = configuration.getLoadedFileNames();
// configFileName ->struts.xml
//1.递归处理include节点
//2.生成Document 集合
loadDocuments(configFileName);
}
loadDocuments方法中调用了loadConfigurationFiles方法 返回一个Document集合
private void loadDocuments(String configFileName) {
loadedFileUrls.clear();
//List<Document> documents
documents = loadConfigurationFiles(configFileName, null);
}
loadConfigurationFiles方法 递归处理include节点 最终生成Document集合
private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
List<Document> docs = new ArrayList<Document>();
List<Document> finalDocs = new ArrayList<Document>();
//防止include重复引入
if (!includedFileNames.contains(fileName)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Loading action configurations from: " + fileName);
}
includedFileNames.add(fileName);
Iterator<URL> urls = null;
InputStream is = null;
IOException ioException = null;
try {
urls = getConfigurationUrls(fileName);
} catch (IOException ex) {
ioException = ex;
}
if (urls == null || !urls.hasNext()) {
if (errorIfMissing) {
throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
} else {
LOG.info("Unable to locate configuration files of the name " + fileName + ", skipping");
return docs;
}
}
URL url = null;
while (urls.hasNext()) {
try {
url = urls.next();
is = FileManager.loadFile(url);
InputSource in = new InputSource(is);
in.setSystemId(url.toString());
//生成Document对象
docs.add(DomHelper.parse(in, dtdMappings));
} catch (XWorkException e) {
if (includeElement != null) {
throw new ConfigurationException("Unable to load " + url, e, includeElement);
} else {
throw new ConfigurationException("Unable to load " + url, e);
}
} catch (Exception e) {
final String s = "Caught exception while loading file " + fileName;
throw new ConfigurationException(s, e, includeElement);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
LOG.error("Unable to close input stream", e);
}
}
}
}
//sort the documents, according to the "order" attribute
Collections.sort(docs, new Comparator<Document>() {
public int compare(Document doc1, Document doc2) {
return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
}
});
for (Document doc : docs) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("include".equals(nodeName)) {
String includeFileName = child.getAttribute("file");
//可以使用通配符匹配
if (includeFileName.indexOf('*') != -1) {
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Vector<String> wildcardMatches = wildcardFinder.findMatches();
for (String match : wildcardMatches) {
//递归处理include节点
finalDocs.addAll(loadConfigurationFiles(match, child));
}
} else {
//递归处理include节点
finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
finalDocs.add(doc);
loadedFileUrls.add(url.toString());
}
}
return finalDocs;
}
StrutsXmlConfigurationProvider的register方法 主要在父类 XmlConfigurationProvider中实现
1.遍历init方法中生成的Document 集合 解析xml文件中定义的bean,constant常量节点 不会处理package节点
2.解析bean节点的值 包装成LocatableFactory对象 注册到ContainerBuilder中factories map集合中
3.解析constant节点的值 保存到ContainerProperties 对象中
XmlConfigurationProvider 的register 这里只解析 bean , constant节点
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
Map<String, Node> loadedBeans = new HashMap<String, Node>();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
//解析bean节点
if ("bean".equals(nodeName)) {
String type = child.getAttribute("type");
String name = child.getAttribute("name");
String impl = child.getAttribute("class");
String onlyStatic = child.getAttribute("static");
String scopeStr = child.getAttribute("scope");
boolean optional = "true".equals(child.getAttribute("optional"));
Scope scope = Scope.SINGLETON;
if ("default".equals(scopeStr)) {
scope = Scope.DEFAULT;
} else if ("request".equals(scopeStr)) {
scope = Scope.REQUEST;
} else if ("session".equals(scopeStr)) {
scope = Scope.SESSION;
} else if ("singleton".equals(scopeStr)) {
scope = Scope.SINGLETON;
} else if ("thread".equals(scopeStr)) {
scope = Scope.THREAD;
}
if (StringUtils.isEmpty(name)) {
name = Container.DEFAULT_NAME;
}
try {
Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
Class ctype = cimpl;
if (StringUtils.isNotEmpty(type)) {
ctype = ClassLoaderUtil.loadClass(type, getClass());
}
if ("true".equals(onlyStatic)) {
// Force loading of class to detect no class def found exceptions
cimpl.getDeclaredClasses();
containerBuilder.injectStatics(cimpl);
} else {
// beanName + class 构成唯一约束
if (containerBuilder.contains(ctype, name)) {
//用loadedBeans map集合检查是否有重复配置的bean
Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
if (throwExceptionOnDuplicateBeans) {
throw new ConfigurationException("Bean type " + ctype + " with the name " + name + " has already been loaded by " + loc, child);
}
}
// Force loading of class to detect no class def found exceptions
cimpl.getDeclaredConstructors();
if (LOG.isDebugEnabled()) {
LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
}
//LocatableFactory 类似spring中 BeanDefinition
//bean定义 保存到ContainerBuilder#factories map集合中
//目前为止 并未真正实例化bean
containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
}
//loadedBeans 检查重复配置的bean
loadedBeans.put(ctype.getName() + name, child);
} catch (Throwable ex) {
if (!optional) {
throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
} else {
LOG.debug("Unable to load optional class: " + ex);
}
}
//constant常量节点
} else if ("constant".equals(nodeName)) {
String name = child.getAttribute("name");
String value = child.getAttribute("value");
//ContainerProperties ->props
props.setProperty(name, value, childNode);
} else if (nodeName.equals("unknown-handler-stack")) {
List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>();
NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref");
int unknownHandlersSize = unknownHandlers.getLength();
for (int k = 0; k < unknownHandlersSize; k++) {
Element unknownHandler = (Element) unknownHandlers.item(k);
unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name")));
}
if (!unknownHandlerStack.isEmpty()) configuration.setUnknownHandlerStack(unknownHandlerStack);
}
}
}
}
}
XmlConfigurationProvider的loadPackages方法 解析package节点下的所有子节点interceptor ,ResultType等等
保存到DefaultConfiguration packageContexts map集合中
public void loadPackages() throws ConfigurationException {
List<Element> reloads = new ArrayList<Element>();
for (Document doc : documents) {
Element rootElement = doc.getDocumentElement();
NodeList children = rootElement.getChildNodes();
int childSize = children.getLength();
for (int i = 0; i < childSize; i++) {
Node childNode = children.item(i);
if (childNode instanceof Element) {
Element child = (Element) childNode;
final String nodeName = child.getNodeName();
if ("package".equals(nodeName)) {
//解析package节点 包装成PackageConfig对象
PackageConfig cfg = addPackage(child);
if (cfg.isNeedsRefresh()) {
reloads.add(child);
}
}
}
}
//空实现 扩展时用
loadExtraConfiguration(doc);
}
if (reloads.size() > 0) {
reloadRequiredPackages(reloads);
}
for (Document doc : documents) {
//空实现 扩展时用
loadExtraConfiguration(doc);
}
documents.clear();
configuration = null;
}
package节点下的所有子节点
protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
PackageConfig.Builder newPackage = buildPackageContext(packageElement);
if (newPackage.isNeedsRefresh()) {
return newPackage.build();
}
//处理所有的ResultType 包括自定义的 , strust-default.xml中定义的
addResultTypes(newPackage, packageElement);
//interceptor节点
loadInterceptors(newPackage, packageElement);
//default-interceptor-ref
loadDefaultInterceptorRef(newPackage, packageElement);
//default-class-ref节点
loadDefaultClassRef(newPackage, packageElement);
//全局result global-results节点
loadGlobalResults(newPackage, packageElement);
//global-exception-mappings节点 异常处理
loadGobalExceptionMappings(newPackage, packageElement);
NodeList actionList = packageElement.getElementsByTagName("action");
for (int i = 0; i < actionList.getLength(); i++) {
Element actionElement = (Element) actionList.item(i);
// action节点 result节点处理
addAction(actionElement, newPackage);
}
//default-action-ref
loadDefaultActionRef(newPackage, packageElement);
PackageConfig cfg = newPackage.build();
//TODO 保存到Map
packageContexts 集合中
configuration.addPackageConfig(cfg.getName(), cfg);
return cfg;
}
最后整理解析的ActionConfig Map集合[DefaultConfiguration#packageContexts] 最终已Map<nameSpace,Map<actionName, ActionConfig>>形式存储
public void rebuildRuntimeConfiguration() {
runtimeConfiguration = buildRuntimeConfiguration();
}
protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();
for (PackageConfig packageConfig : packageContexts.values()) {
if (!packageConfig.isAbstract()) {
String namespace = packageConfig.getNamespace();
Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);
if (configs == null) {
configs = new LinkedHashMap<String, ActionConfig>();
}
Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();
for (Object o : actionConfigs.keySet()) {
String actionName = (String) o;
ActionConfig baseConfig = actionConfigs.get(actionName);
//这里设置action的拦截器
//TODO buildFullActionConfig
configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
}
//key -> nameSpace
//value - > <actionName, ActionConfig> ,ActionConfig包含了拦截器
namespaceActionConfigs.put(namespace, configs);
if (packageConfig.getFullDefaultActionRef() != null) {
namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
}
}
}
return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
}
解析完成后, 最终会保存到DefaultConfiguration runtimeConfiguration变量中
初始化顺序图: