Struts2.3.15.1源码浅析

Struts2 两大运行主线:
  • 1.初始化主线:初始化主线主要是为Struts2创建运行环境(此处的环境与Struts2身处的Web环境是有区别的),初始化入口StrutsPrepareAndExecuteFilter继承 Filter,遵循Filter规范,初始化只是在应用启动的时候运行一次,以后无论过来多少HttpServletRequest都不会再运行啦。

StrutsPrepareAndExecuteFilter.java

Java代码  

  1. public void init(FilterConfig filterConfig) throws ServletException {
  2. InitOperations init = new InitOperations();//Http请求预处理工具类,ExecuteOperates  Http请求逻辑处理类
  3. Dispatcher dispatcher = null;
  4. try {
  5. FilterHostConfig config = new FilterHostConfig(filterConfig);
  6. init.initLogging(config);
  7. dispatcher = init.initDispatcher(config);//核心分发器Dispatcher初始化
  8. //初始化静态资源加载器
  9. init.initStaticContentLoader(config, dispatcher);
  10. //初始化进行http预处理操作类
  11. prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
  12. //初始化进行http请求逻辑处理操作类
  13. execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
  14. this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
  15. postInit(dispatcher, filterConfig);
  16. } finally {
  17. if (dispatcher != null) {
  18. dispatcher.cleanUpAfterInit();
  19. }
  20. init.cleanup();//初始化清除工作
  21. }
  22. }

首先我们来看看,Struts2核心分发器初始化过程:

InitOperations.java

Java代码  

  1. /**
  2. * Creates and initializes the dispatcher
  3. */
  4. public Dispatcher initDispatcher( HostConfig filterConfig ) {
  5. Dispatcher dispatcher = createDispatcher(filterConfig);
  6. dispatcher.init();
  7. return dispatcher;
  8. }
  9. /**
  10. * Create a {@link Dispatcher}
  11. */
  12. private Dispatcher createDispatcher( HostConfig filterConfig ) {
  13. Map<String, String> params = new HashMap<String, String>();
  14. for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
  15. String name = (String) e.next();
  16. String value = filterConfig.getInitParameter(name);
  17. params.put(name, value);
  18. }
  19. return new Dispatcher(filterConfig.getServletContext(), params);
  20. }

顺着流程,接着探索dispatcher.init(),这里是加载配置文件的地方

Dispatcher.java

Java代码  

  1. public void init() {
  2. if (configurationManager == null) {
  3. configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
  4. }
  5. try {
  6. init_FileManager();
  7. init_DefaultProperties(); // [1]加载default.properties org/apache/struts2/default.properties
  8. init_TraditionalXmlConfigurations(); // [2]加载struts-default.xml,struts-plugin.xml,struts.xml
  9. init_LegacyStrutsProperties(); // [3]
  10. init_CustomConfigurationProviders(); // [5]初始化自定义的provider配置类全名和实现ConfigurationProvider接口,用逗号隔开即可
  11. init_FilterInitParameters() ; // [6]
  12. init_AliasStandardObjects() ; // [7]加载框架内部内置的对象struts-default.xml中<bean>节点
  13. Container container = init_PreloadConfiguration();
  14. container.inject(this);
  15. init_CheckWebLogicWorkaround(container);
  16. if (!dispatcherListeners.isEmpty()) {
  17. for (DispatcherListener l : dispatcherListeners) {
  18. l.dispatcherInitialized(this);
  19. }
  20. }
  21. } catch (Exception ex) {
  22. if (LOG.isErrorEnabled())
  23. LOG.error("Dispatcher initialization failed", ex);
  24. throw new StrutsException(ex);
  25. }
  26. }

让我们截取init_TraditionalXmlConfigurations();的加载过程来了解Struts2是如何来加载我们配置的配置文件的:

Java代码  

  1. private void init_TraditionalXmlConfigurations() {
  2. //首先读取web.xml中的config初始参数 如果不存在,默认加载加载struts-default.xml,struts-plugin.xml,struts.xml
  3. //如果不想使用默认值,在web.xml中设置config参数即可
  4. String configPaths = initParams.get("config");
  5. if (configPaths == null) {
  6. configPaths = DEFAULT_CONFIGURATION_PATHS;
  7. }
  8. String[] files = configPaths.split("\\s*[,]\\s*");
  9. for (String file : files) {
  10. if (file.endsWith(".xml")) {
  11. if ("xwork.xml".equals(file)) {//xwork.xml文件单独解析
  12. configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
  13. } else {
  14. configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
  15. }
  16. } else {
  17. throw new IllegalArgumentException("Invalid configuration file name");
  18. }
  19. }
  20. }

createStrutsXmlConfigurationProvider方法创建StrutsXmlConfigurationProvider对象,其继承XmlConfigurationProvider对象,而XmlConfigurationProvider 实现了ConfigurationProvider接口,ConfigurationProvider使用java很少见到的多继承机制,继承了ContainerProvider和PackageProvider接口。 XmlConfigurationProvider负责读取和解析配置文件,

Java代码  

  1. /**
  2. * Create a PackageConfig from an XML element representing it.
  3. */
  4. protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
  5. String packageName = packageElement.getAttribute("name");
  6. PackageConfig packageConfig = configuration.getPackageConfig(packageName);
  7. if (packageConfig != null) {
  8. if (LOG.isDebugEnabled()) {
  9. LOG.debug("Package [#0] already loaded, skipping re-loading it and using existing PackageConfig [#1]", packageName, packageConfig);
  10. }
  11. return packageConfig;
  12. }
  13. PackageConfig.Builder newPackage = buildPackageContext(packageElement);
  14. if (newPackage.isNeedsRefresh()) {
  15. return newPackage.build();
  16. }
  17. if (LOG.isDebugEnabled()) {
  18. LOG.debug("Loaded " + newPackage);
  19. }
  20. // add result types (and default result) to this package
  21. addResultTypes(newPackage, packageElement);
  22. // load the interceptors and interceptor stacks for this package
  23. loadInterceptors(newPackage, packageElement);
  24. // load the default interceptor reference for this package
  25. loadDefaultInterceptorRef(newPackage, packageElement);
  26. // load the default class ref for this package
  27. loadDefaultClassRef(newPackage, packageElement);
  28. // load the global result list for this package
  29. loadGlobalResults(newPackage, packageElement);
  30. // load the global exception handler list for this package
  31. loadGobalExceptionMappings(newPackage, packageElement);
  32. // get actions
  33. NodeList actionList = packageElement.getElementsByTagName("action");
  34. for (int i = 0; i < actionList.getLength(); i++) {
  35. Element actionElement = (Element) actionList.item(i);
  36. addAction(actionElement, newPackage);
  37. }
  38. // load the default action reference for this package
  39. loadDefaultActionRef(newPackage, packageElement);
  40. PackageConfig cfg = newPackage.build();
  41. configuration.addPackageConfig(cfg.getName(), cfg);
  42. return cfg;
  43. }

其中

  • addAction()方法负责读取节点,并将数据保存在ActionConfig中;
  • addResultTypes()方法负责读取节点,并将数据保存在ResultTypeConfig中;ResultTypeConfig使用了构造器模式创建对象
  • loadInterceptors()方法负责读取节点,并将数据保存在InterceptorConfig中;
  • loadInterceptorStack()方法负责读取节点,并将数据保存在InterceptorStackConfig中;
  • loadInterceptorStacks()方法负责读取节点,并将数据保存在InterceptorStackConfig中;

而以上这些方法都将会被addPackage()invoke,并将数据汇集到PackageConfig中。

配置文件实际的加载流程: DefaultConfiguration.reloadContainer()时调用了containerProvider.init(this);

XmlConfigurationProvider.java

Java代码  

  1. public void init(Configuration configuration) {
  2. this.configuration = configuration;
  3. this.includedFileNames = configuration.getLoadedFileNames();
  4. loadDocuments(configFileName);
  5. }

加载Documents:

Java代码  

  1. private void loadDocuments(String configFileName) {
  2. try {
  3. loadedFileUrls.clear();
  4. documents = loadConfigurationFiles(configFileName, null);
  5. } catch (ConfigurationException e) {
  6. throw e;
  7. } catch (Exception e) {
  8. throw new ConfigurationException("Error loading configuration file " + configFileName, e);
  9. }
  10. }
  11. private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
  12. List<Document> docs = new ArrayList<Document>();
  13. List<Document> finalDocs = new ArrayList<Document>();
  14. if (!includedFileNames.contains(fileName)) {
  15. if (LOG.isDebugEnabled()) {
  16. LOG.debug("Loading action configurations from: " + fileName);
  17. }
  18. includedFileNames.add(fileName);
  19. Iterator<URL> urls = null;
  20. InputStream is = null;
  21. IOException ioException = null;
  22. try {
  23. urls = getConfigurationUrls(fileName);
  24. } catch (IOException ex) {
  25. ioException = ex;
  26. }
  27. if (urls == null || !urls.hasNext()) {
  28. if (errorIfMissing) {
  29. throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
  30. } else {
  31. if (LOG.isInfoEnabled()) {
  32. LOG.info("Unable to locate configuration files of the name "
  33. + fileName + ", skipping");
  34. }
  35. return docs;
  36. }
  37. }
  38. URL url = null;
  39. while (urls.hasNext()) {
  40. try {
  41. url = urls.next();
  42. is = fileManager.loadFile(url);
  43. InputSource in = new InputSource(is);
  44. in.setSystemId(url.toString());
  45. docs.add(DomHelper.parse(in, dtdMappings));
  46. } catch (XWorkException e) {
  47. if (includeElement != null) {
  48. throw new ConfigurationException("Unable to load " + url, e, includeElement);
  49. } else {
  50. throw new ConfigurationException("Unable to load " + url, e);
  51. }
  52. } catch (Exception e) {
  53. throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);
  54. } finally {
  55. if (is != null) {
  56. try {
  57. is.close();
  58. } catch (IOException e) {
  59. LOG.error("Unable to close input stream", e);
  60. }
  61. }
  62. }
  63. }
  64. //sort the documents, according to the "order" attribute
  65. Collections.sort(docs, new Comparator<Document>() {
  66. public int compare(Document doc1, Document doc2) {
  67. return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
  68. }
  69. });
  70. for (Document doc : docs) {
  71. Element rootElement = doc.getDocumentElement();
  72. NodeList children = rootElement.getChildNodes();
  73. int childSize = children.getLength();
  74. for (int i = 0; i < childSize; i++) {
  75. Node childNode = children.item(i);
  76. if (childNode instanceof Element) {
  77. Element child = (Element) childNode;
  78. final String nodeName = child.getNodeName();
  79. //解析每个action配置是,对于include文件可以使用通配符*来进行配置
  80. //如Struts.xml中可配置成<include file="actions_*.xml"/>
  81. if ("include".equals(nodeName)) {
  82. String includeFileName = child.getAttribute("file");
  83. if (includeFileName.indexOf(‘*‘) != -1) {
  84. // handleWildCardIncludes(includeFileName, docs, child);
  85. ClassPathFinder wildcardFinder = new ClassPathFinder();
  86. wildcardFinder.setPattern(includeFileName);
  87. Vector<String> wildcardMatches = wildcardFinder.findMatches();
  88. for (String match : wildcardMatches) {
  89. finalDocs.addAll(loadConfigurationFiles(match, child));
  90. }
  91. } else {
  92. finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
  93. }
  94. }
  95. }
  96. }
  97. finalDocs.add(doc);
  98. loadedFileUrls.add(url.toString());
  99. }
  100. if (LOG.isDebugEnabled()) {
  101. LOG.debug("Loaded action configuration from: " + fileName);
  102. }
  103. }
  104. return finalDocs;
  105. }

init_CustomConfigurationProviders(); // [5]初始化自定义的provider配置类全名和实现ConfigurationProvider接口,用逗号隔开即可

Java代码  

  1. private void init_CustomConfigurationProviders() {
  2. String configProvs = initParams.get("configProviders");
  3. if (configProvs != null) {
  4. String[] classes = configProvs.split("\\s*[,]\\s*");
  5. for (String cname : classes) {
  6. try {
  7. Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
  8. ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
  9. configurationManager.addContainerProvider(prov);
  10. } catch (InstantiationException e) {
  11. throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
  12. } catch (IllegalAccessException e) {
  13. throw new ConfigurationException("Unable to access provider: "+cname, e);
  14. } catch (ClassNotFoundException e) {
  15. throw new ConfigurationException("Unable to locate provider class: "+cname, e);
  16. }
  17. }
  18. }
  19. }

接下来我们把目光放到Container container = init_PreloadConfiguration();上,从代码的表面意义上可以看出是对容器进行初始化

Java代码  

  1. private Container init_PreloadConfiguration() {
  2. Configuration config = configurationManager.getConfiguration();
  3. Container container = config.getContainer();
  4. boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));
  5. LocalizedTextUtil.setReloadBundles(reloadi18n);
  6. ContainerHolder.store(container);
  7. return container;
  8. }

Configuration与ConfigurationManager作为Struts2初始化过程中的两大强力的辅助类,对于配置元素的管理启动了至关重要的作用。

Configuration,提供了框架所有配置元素访问的接口,而且对所有配置元素进行初始化调度,接下来我们就从 Configuration config = configurationManager.getConfiguration();一点一点地来揭示Configuration对配置元素初始化调度的本质。

Java代码  

  1. /**
  2. * Get the current XWork configuration object.  By default an instance of DefaultConfiguration will be returned
  3. *
  4. * @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
  5. */
  6. public synchronized Configuration getConfiguration() {
  7. if (configuration == null) {
  8. setConfiguration(createConfiguration(defaultFrameworkBeanName));
  9. try {
  10. configuration.reloadContainer(getContainerProviders());
  11. } catch (ConfigurationException e) {
  12. setConfiguration(null);
  13. throw new ConfigurationException("Unable to load configuration.", e);
  14. }
  15. } else {
  16. conditionalReload();
  17. }
  18. return configuration;
  19. }

关键在于configuration.reloadContainer(getContainerProviders());方法,我们接着看代码:

Java代码  

  1. /**
  2. * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
  3. * buildRuntimeConfiguration().
  4. *
  5. * @throws ConfigurationException
  6. */
  7. public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
  8. packageContexts.clear();
  9. loadedFileNames.clear();
  10. List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
  11. ContainerProperties props = new ContainerProperties();
  12. ContainerBuilder builder = new ContainerBuilder();
  13. Container bootstrap = createBootstrapContainer(providers);
  14. for (final ContainerProvider containerProvider : providers)
  15. {
  16. bootstrap.inject(containerProvider);
  17. containerProvider.init(this);
  18. containerProvider.register(builder, props);
  19. }
  20. props.setConstants(builder);
  21. builder.factory(Configuration.class, new Factory<Configuration>() {
  22. public Configuration create(Context context) throws Exception {
  23. return DefaultConfiguration.this;
  24. }
  25. });
  26. ActionContext oldContext = ActionContext.getContext();
  27. try {
  28. // Set the bootstrap container for the purposes of factory creation
  29. setContext(bootstrap);
  30. container = builder.create(false);
  31. setContext(container);
  32. objectFactory = container.getInstance(ObjectFactory.class);
  33. // Process the configuration providers first
  34. for (final ContainerProvider containerProvider : providers)
  35. {
  36. if (containerProvider instanceof PackageProvider) {
  37. container.inject(containerProvider);
  38. ((PackageProvider)containerProvider).loadPackages();
  39. packageProviders.add((PackageProvider)containerProvider);
  40. }
  41. }
  42. // Then process any package providers from the plugins
  43. Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
  44. for (String name : packageProviderNames) {
  45. PackageProvider provider = container.getInstance(PackageProvider.class, name);
  46. provider.init(this);
  47. provider.loadPackages();
  48. packageProviders.add(provider);
  49. }
  50. rebuildRuntimeConfiguration();
  51. } finally {
  52. if (oldContext == null) {
  53. ActionContext.setContext(null);
  54. }
  55. }
  56. return packageProviders;
  57. }

很显然我们在这个方法中看到,方法的参数即是一组配置元素的加载器(ConfigurationProvider) Struts2的初始换主线自此全部结束。

*****************************************华丽的分割线***************************************

下面我们再来看看Struts2的第二条主线:

######请求处理主线 我们回到StrutsPrepareAndExecuteFilter来看看,Filter标准中的doFilter()方法:

Java代码  

  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  2. HttpServletRequest request = (HttpServletRequest) req;
  3. HttpServletResponse response = (HttpServletResponse) res;
  4. try {
  5. prepare.setEncodingAndLocale(request, response);
  6. prepare.createActionContext(request, response);
  7. prepare.assignDispatcherToThread();
  8. if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
  9. chain.doFilter(request, response);
  10. } else {
  11. request = prepare.wrapRequest(request);
  12. ActionMapping mapping = prepare.findActionMapping(request, response, true);
  13. if (mapping == null) {
  14. boolean handled = execute.executeStaticResourceRequest(request, response);
  15. if (!handled) {
  16. chain.doFilter(request, response);
  17. }
  18. } else {
  19. execute.executeAction(request, response, mapping);
  20. }
  21. }
  22. } finally {
  23. prepare.cleanupRequest(request);
  24. }
  25. }

prepare.createActionContext(request, response);:创建上下文ActionContext并初始化其内部线程安全的ThreadLocal对象

Java代码  

  1. /**
  2. * Creates the action context and initializes the thread local
  3. */
  4. public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
  5. ActionContext ctx;
  6. Integer counter = 1;
  7. Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
  8. if (oldCounter != null) {
  9. counter = oldCounter + 1;
  10. }
  11. ActionContext oldContext = ActionContext.getContext();
  12. if (oldContext != null) {
  13. // detected existing context, so we are probably in a forward
  14. ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
  15. } else {
  16. ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
  17. stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));
  18. ctx = new ActionContext(stack.getContext());
  19. }
  20. request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
  21. ActionContext.setContext(ctx);
  22. return ctx;
  23. }

prepare.assignDispatcherToThread();:将dispatcher对象绑定到Dispatcher内部线程安全的instance对象中

request = prepare.wrapRequest(request);:对HttpServletRequest进行封装,传统的Web容器元素的数据是通过HttpServletRequest 的接口实现访问,但是在Struts2中数据的储存位置发生了变化,它们不在适合于web对象绑定在一起,而是以ActionContext的形式存在于当前 的线程中,故传统的访问方式无法访问到Struts2中的数据,因此Struts2针对这种情况作出了对HttpServletRequest装饰的扩展。

PrepareOperations.java

Java代码  

  1. /**
  2. * Wraps the request with the Struts wrapper that handles multipart requests better
  3. * @return The new request, if there is one
  4. * @throws ServletException
  5. */
  6. public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {
  7. HttpServletRequest request = oldRequest;
  8. try {
  9. // Wrap request first, just in case it is multipart/form-data
  10. // parameters might not be accessible through before encoding (ww-1278)
  11. request = dispatcher.wrapRequest(request, servletContext);
  12. } catch (IOException e) {
  13. throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);
  14. }
  15. return request;
  16. }

Dispatcher.java

Java代码  

  1. /**
  2. * Wrap and return the given request or return the original request object.
  3. * </p>
  4. * This method transparently handles multipart data as a wrapped class around the given request.
  5. * Override this method to handle multipart requests in a special way or to handle other types of requests.
  6. * Note, {@link org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper} is
  7. * flexible - look first to that object before overriding this method to handle multipart data.
  8. *
  9. * @param request the HttpServletRequest object.
  10. * @param servletContext Our ServletContext object
  11. * @return a wrapped request or original request.
  12. * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
  13. * @throws java.io.IOException on any error.
  14. */
  15. public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
  16. // don‘t wrap more than once
  17. if (request instanceof StrutsRequestWrapper) {
  18. return request;
  19. }
  20. String content_type = request.getContentType();
  21. if (content_type != null && content_type.contains("multipart/form-data")) {
  22. MultiPartRequest mpr = getMultiPartRequest();
  23. LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
  24. request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext), provider);
  25. } else {
  26. request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
  27. }
  28. return request;
  29. }

ActionMapping mapping = prepare.findActionMapping(request, response, true);ActionMapping是一个普通的java类,但是它将 URL形式的HTTP请求与Struts2中的Action建立起了联系。Struts2在进行Http请求处理时,由ActionMapper的实现类在运行期查找相应的 事件映射关系并生成ActionMapping对象。

Java代码  

  1. /**
  2. * Finds and optionally creates an {@link ActionMapping}.  if forceLookup is false, it first looks in the current request to see if one
  3. * has already been found, otherwise, it creates it and stores it in the request.  No mapping will be created in the
  4. * case of static resource requests or unidentifiable requests for other servlets, for example.
  5. * @param forceLookup if true, the action mapping will be looked up from the ActionMapper instance, ignoring if there is one
  6. * in the request or not
  7. */
  8. public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
  9. ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
  10. if (mapping == null || forceLookup) {
  11. try {
  12. //ActionMapper类才是Struts2进行URL Mapping关系查找的核心类
  13. mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
  14. if (mapping != null) {
  15. request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
  16. }
  17. } catch (Exception ex) {
  18. dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
  19. }
  20. }
  21. return mapping;
  22. }

ActionMapper类具有多个默认的实现类,每个实现类具有不同的ActionMapping查找规则,所以这个地方给我们留下了无限的遐想,是个很好的扩展点。

execute.executeAction(request, response, mapping);:开始真正执行业务逻辑 ExecuteOperations.java

Java代码  

  1. /**
  2. * Executes an action
  3. * @throws ServletException
  4. */
  5. public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
  6. dispatcher.serviceAction(request, response, servletContext, mapping);
  7. }

Dispatcher.java

Java代码  

  1. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
  2. ActionMapping mapping) throws ServletException {
  3. Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
  4. // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
  5. ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
  6. boolean nullStack = stack == null;
  7. if (nullStack) {
  8. ActionContext ctx = ActionContext.getContext();
  9. if (ctx != null) {
  10. stack = ctx.getValueStack();
  11. }
  12. }
  13. if (stack != null) {
  14. extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
  15. }
  16. String timerKey = "Handling request from Dispatcher";
  17. try {
  18. UtilTimerStack.push(timerKey);
  19. String namespace = mapping.getNamespace();
  20. String name = mapping.getName();
  21. String method = mapping.getMethod();
  22. Configuration config = configurationManager.getConfiguration();
  23. ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
  24. namespace, name, method, extraContext, true, false);
  25. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
  26. //如果ActionMapping对象中包含Result对象,则直接跳过Action而执行Result
  27. // if the ActionMapping says to go straight to a result, do it!
  28. if (mapping.getResult() != null) {
  29. Result result = mapping.getResult();
  30. result.execute(proxy.getInvocation());
  31. } else {
  32. proxy.execute();
  33. }
  34. // If there was a previous value stack then set it back onto the request
  35. if (!nullStack) {
  36. request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
  37. }
  38. } catch (ConfigurationException e) {
  39. // WW-2874 Only log error if in devMode
  40. if (devMode) {
  41. String reqStr = request.getRequestURI();
  42. if (request.getQueryString() != null) {
  43. reqStr = reqStr + "?" + request.getQueryString();
  44. }
  45. LOG.error("Could not find action or result\n" + reqStr, e);
  46. } else {
  47. if (LOG.isWarnEnabled()) {
  48. LOG.warn("Could not find action or result", e);
  49. }
  50. }
  51. sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
  52. } catch (Exception e) {
  53. if (handleException || devMode) {
  54. sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
  55. } else {
  56. throw new ServletException(e);
  57. }
  58. } finally {
  59. UtilTimerStack.pop(timerKey);
  60. }
  61. }

Map<String, Object> extraContext = createContextMap(request, response, mapping, context);: 该方法主要把Application、Session、Request的key value值拷贝到Map中,并放在HashMap<String,Object>中

Dispatcher.java

Java代码  

  1. /**
  2. * Create a context map containing all the wrapped request objects
  3. *
  4. * @param request The servlet request
  5. * @param response The servlet response
  6. * @param mapping The action mapping
  7. * @param context The servlet context
  8. * @return A map of context objects
  9. */
  10. public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
  11. ActionMapping mapping, ServletContext context) {
  12. // request map wrapping the http request objects
  13. Map requestMap = new RequestMap(request);
  14. // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately
  15. Map params = new HashMap(request.getParameterMap());
  16. // session map wrapping the http session
  17. Map session = new SessionMap(request);
  18. // application map wrapping the ServletContext
  19. Map application = new ApplicationMap(context);
  20. Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
  21. if (mapping != null) {
  22. extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
  23. }
  24. return extraContext;
  25. }

接下来调用Configuration对象的reloadContainer()利用ContainerBuilder对象生成Container对象,为生成ActionProxy对象做好准备,

ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false);创建ActionProxyFacotry的过程也完成了ActionInvocation对象的创建:

DefaultActionProxyFactory.java----------createActionProxy()

Java代码  

  1. public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
  2. DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
  3. container.inject(proxy);
  4. proxy.prepare();//
  5. return proxy;
  6. }

DefaultActionProxy.java----------prepare()

Java代码  

  1. protected void prepare() {
  2. String profileKey = "create DefaultActionProxy: ";
  3. try {
  4. UtilTimerStack.push(profileKey);
  5. config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
  6. if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
  7. config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
  8. }
  9. if (config == null) {
  10. throw new ConfigurationException(getErrorMessage());
  11. }
  12. resolveMethod();
  13. if (!config.isAllowedMethod(method)) {
  14. throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);
  15. }
  16. invocation.init(this);
  17. } finally {
  18. UtilTimerStack.pop(profileKey);
  19. }
  20. }

DefaultActionInvocation.java-------------init(ActionProxy proxy)

Java代码  

  1. public void init(ActionProxy proxy) {
  2. this.proxy = proxy;
  3. Map<String, Object> contextMap = createContextMap();
  4. // Setting this so that other classes, like object factories, can use the ActionProxy and other
  5. // contextual information to operate
  6. ActionContext actionContext = ActionContext.getContext();
  7. if (actionContext != null) {
  8. actionContext.setActionInvocation(this);
  9. }
  10. createAction(contextMap);
  11. if (pushAction) {
  12. stack.push(action);//压入CompoundRoot 压栈操作
  13. contextMap.put("action", action);
  14. }
  15. invocationContext = new ActionContext(contextMap);
  16. invocationContext.setName(proxy.getActionName());
  17. // get a new List so we don‘t get problems with the iterator if someone changes the list
  18. List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
  19. interceptors = interceptorList.iterator();
  20. }

DefaultActionInvocation.java---createAction(contextMap);

Java代码  

  1. protected void createAction(Map<String, Object> contextMap) {
  2. // load action
  3. String timerKey = "actionCreate: " + proxy.getActionName();
  4. try {
  5. UtilTimerStack.push(timerKey);
  6. action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
  7. } catch (InstantiationException e) {
  8. throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
  9. } catch (IllegalAccessException e) {
  10. throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
  11. } catch (Exception e) {
  12. String gripe;
  13. if (proxy == null) {
  14. gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";
  15. } else if (proxy.getConfig() == null) {
  16. gripe = "Sheesh.  Where‘d that ActionProxy get to?  I can‘t find it in the current ActionInvocation!?";
  17. } else if (proxy.getConfig().getClassName() == null) {
  18. gripe = "No Action defined for ‘" + proxy.getActionName() + "‘ in namespace ‘" + proxy.getNamespace() + "‘";
  19. } else {
  20. gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for ‘" + proxy.getActionName() + "‘ in namespace ‘" + proxy.getNamespace() + "‘";
  21. }
  22. gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
  23. throw new XWorkException(gripe, e, proxy.getConfig());
  24. } finally {
  25. UtilTimerStack.pop(timerKey);
  26. }
  27. if (actionEventListener != null) {
  28. action = actionEventListener.prepare(action, stack);
  29. }
  30. }

继续回到Dispatcher中的proxy.execute();继续执行业务逻辑 DefaultActionProxy.java-------------execute()

Java代码  

  1. public String execute() throws Exception {
  2. ActionContext nestedContext = ActionContext.getContext();
  3. ActionContext.setContext(invocation.getInvocationContext());
  4. String retCode = null;
  5. String profileKey = "execute: ";
  6. try {
  7. UtilTimerStack.push(profileKey);
  8. retCode = invocation.invoke();
  9. } finally {
  10. if (cleanupContext) {
  11. ActionContext.setContext(nestedContext);
  12. }
  13. UtilTimerStack.pop(profileKey);
  14. }
  15. return retCode;
  16. }

DefaultActionInvocation.java---invoke()

Java代码  

  1. /**
  2. * @throws ConfigurationException If no result can be found with the returned code
  3. */
  4. public String invoke() throws Exception {
  5. String profileKey = "invoke: ";
  6. try {
  7. UtilTimerStack.push(profileKey);
  8. if (executed) {
  9. throw new IllegalStateException("Action has already executed");
  10. }
  11. if (interceptors.hasNext()) {
  12. final InterceptorMapping interceptor = interceptors.next();
  13. String interceptorMsg = "interceptor: " + interceptor.getName();
  14. UtilTimerStack.push(interceptorMsg);
  15. try {
  16. resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
  17. }
  18. finally {
  19. UtilTimerStack.pop(interceptorMsg);
  20. }
  21. } else {
  22. resultCode = invokeActionOnly();
  23. }
  24. // this is needed because the result will be executed, then control will return to the Interceptor, which will
  25. // return above and flow through again
  26. if (!executed) {
  27. if (preResultListeners != null) {
  28. for (Object preResultListener : preResultListeners) {
  29. PreResultListener listener = (PreResultListener) preResultListener;
  30. String _profileKey = "preResultListener: ";
  31. try {
  32. UtilTimerStack.push(_profileKey);
  33. listener.beforeResult(this, resultCode);
  34. }
  35. finally {
  36. UtilTimerStack.pop(_profileKey);
  37. }
  38. }
  39. }
  40. // now execute the result, if we‘re supposed to
  41. if (proxy.getExecuteResult()) {
  42. executeResult();
  43. }
  44. executed = true;
  45. }
  46. return resultCode;
  47. }
  48. finally {
  49. UtilTimerStack.pop(profileKey);
  50. }
  51. }

resultCode = invokeActionOnly();:真正调用Action实际逻辑的地方

DefaultActionInvocation.java---invokeActionOnly()

Java代码  

  1. public String invokeActionOnly() throws Exception {
  2. return invokeAction(getAction(), proxy.getConfig());
  3. }
  4. protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
  5. String methodName = proxy.getMethod();
  6. if (LOG.isDebugEnabled()) {
  7. LOG.debug("Executing action method = " + actionConfig.getMethodName());
  8. }
  9. String timerKey = "invokeAction: " + proxy.getActionName();
  10. try {
  11. UtilTimerStack.push(timerKey);
  12. boolean methodCalled = false;
  13. Object methodResult = null;
  14. Method method = null;
  15. try {
  16. method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
  17. } catch (NoSuchMethodException e) {
  18. // hmm -- OK, try doXxx instead
  19. try {
  20. String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
  21. method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);
  22. } catch (NoSuchMethodException e1) {
  23. // well, give the unknown handler a shot
  24. if (unknownHandlerManager.hasUnknownHandlers()) {
  25. try {
  26. methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
  27. methodCalled = true;
  28. } catch (NoSuchMethodException e2) {
  29. // throw the original one
  30. throw e;
  31. }
  32. } else {
  33. throw e;
  34. }
  35. }
  36. }
  37. if (!methodCalled) {
  38. methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);
  39. }
  40. return saveResult(actionConfig, methodResult);
  41. } catch (NoSuchMethodException e) {
  42. throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
  43. } catch (InvocationTargetException e) {
  44. // We try to return the source exception.
  45. Throwable t = e.getTargetException();
  46. if (actionEventListener != null) {
  47. String result = actionEventListener.handleException(t, getStack());
  48. if (result != null) {
  49. return result;
  50. }
  51. }
  52. if (t instanceof Exception) {
  53. throw (Exception) t;
  54. } else {
  55. throw e;
  56. }
  57. } finally {
  58. UtilTimerStack.pop(timerKey);
  59. }
  60. }

OK action执行完了,还要根据ResultConfig返回到view,也就是在invoke方法中调用executeResult方法。

Java代码  

  1. // now execute the result, if we‘re supposed to
  2. if (proxy.getExecuteResult()) {
  3. executeResult();
  4. }

DefaultActionInvocation.java---executeResult()

Java代码  

  1. /**
  2. * Uses getResult to get the final Result and executes it
  3. *
  4. * @throws ConfigurationException If not result can be found with the returned code
  5. */
  6. private void executeResult() throws Exception {
  7. result = createResult();
  8. String timerKey = "executeResult: " + getResultCode();
  9. try {
  10. UtilTimerStack.push(timerKey);
  11. if (result != null) {
  12. result.execute(this);
  13. } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
  14. throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
  15. + " and result " + getResultCode(), proxy.getConfig());
  16. } else {
  17. if (LOG.isDebugEnabled()) {
  18. LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
  19. }
  20. }
  21. } finally {
  22. UtilTimerStack.pop(timerKey);
  23. }
  24. }

DefaultActionInvocation.java---createResult()

Java代码  

  1. public Result createResult() throws Exception {
  2. if (explicitResult != null) {
  3. Result ret = explicitResult;
  4. explicitResult = null;
  5. return ret;
  6. }
  7. ActionConfig config = proxy.getConfig();
  8. Map<String, ResultConfig> results = config.getResults();
  9. ResultConfig resultConfig = null;
  10. try {
  11. resultConfig = results.get(resultCode);
  12. } catch (NullPointerException e) {
  13. if (LOG.isDebugEnabled()) {
  14. LOG.debug("Got NPE trying to read result configuration for resultCode [#0]", resultCode);
  15. }
  16. }
  17. if (resultConfig == null) {
  18. // If no result is found for the given resultCode, try to get a wildcard ‘*‘ match.
  19. resultConfig = results.get("*");
  20. }
  21. if (resultConfig != null) {
  22. try {
  23. return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
  24. } catch (Exception e) {
  25. if (LOG.isErrorEnabled()) {
  26. LOG.error("There was an exception while instantiating the result of type #0", e, resultConfig.getClassName());
  27. }
  28. throw new XWorkException(e, resultConfig);
  29. }
  30. } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
  31. return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
  32. }
  33. return null;
  34. }

具体result.execute(this);的实现可以参考一下各类中的具体代码:


 时间匆忙,只是对整个执行流程做了代码层面的演示,待后续将详细说明附上 ^.^

时间: 2024-11-08 06:03:56

Struts2.3.15.1源码浅析的相关文章

Struts2源码浅析-ConfigurationProvider

ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 [java] view plaincopyprint? public interface ConfigurationProvider extends ContainerProvider, PackageProvider { } ContainerProvider接口定义 [java] view plaincopyprint? /** * 容器加载 注册

ReactiveCocoa2 源码浅析

ReactiveCocoa2 源码浅析 标签(空格分隔): ReactiveCocoa iOS Objective-C ? 开车不需要知道离合器是怎么工作的,但如果知道离合器原理,那么车子可以开得更平稳. ReactiveCocoa 是一个重型的 FRP 框架,内容十分丰富,它使用了大量内建的 block,这使得其有强大的功能的同时,内部源码也比较复杂.本文研究的版本是2.4.4,小版本间的差别不是太大,无需担心此问题. 这里只探究其核心 RACSignal 源码及其相关部分.本文不会详细解释里

【深入浅出jQuery】源码浅析2--奇技淫巧

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 我在 github 上关于 jQuery 源码的全文注解,感兴趣的可以围观一下

Volley框架源码浅析(一)

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Voll

PM2源码浅析

PM2工作原理 最近在玩一个游戏,<地平线:黎明时分>,最终Boss是一名叫黑底斯的人,所谓为人,也许不对,黑底斯是一段强大的毁灭进程,破坏了盖娅主进程,从而引发的整个大陆机械兽劣化故事. 为什么要讲这么一段呢,是希望大家可以更好地理解pm2的原理,要理解pm2就要理解god和santan的关系,god和santan的关系就相当于盖娅和黑底斯在pm2中的01世界中,每一行代码每一个字节都安静的工作god就是Daemon进程 守护进程,重启进程,守护node程序世界的安宁,santan就是进程的

Android源码浅析(一)——VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置

Android源码浅析(一)--VMware Workstation Pro和Ubuntu Kylin 16.04 LTS安装配置 最近地方工作,就是接触源码的东西了,所以好东西还是要分享,系列开了这么多,完结 的也没几个,主要还是自己覆盖的太广了,却又不精通,嘿嘿,工作需要,所以写下了本篇博客 一.VMware 12 我选择的虚拟机试VMware,挺好用的感觉,下载VMware就不说了,善用搜索键嘛,这里我提供一个我现在在用的 下载地址:链接:http://pan.baidu.com/s/1k

【Spark Core】任务执行机制和Task源码浅析2

引言 上一小节<任务执行机制和Task源码浅析1>介绍了Executor的注册过程. 这一小节,我将从Executor端,就接收LaunchTask消息之后Executor的执行任务过程进行介绍. 1. Executor的launchTasks函数 DriverActor提交任务,发送LaunchTask指令给CoarseGrainedExecutorBackend,接收到指令之后,让它内部的executor来发起任务,即调用空闲的executor的launchTask函数. 下面是Coars

Volley框架源码浅析(二)

尊重原创 http://write.blog.csdn.net/postedit/25921795 在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列:本地队列和网络队列 /** The cache triage queue. */ private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<

Android手势源码浅析-----手势绘制(GestureOverlayView)

Android手势源码浅析-----手势绘制(GestureOverlayView)