org.apache.struts2.dispatcher.ng.filter。StrutsPrepareAndExecuteFilter初始化过程:
1.创建InitOperations对象的实例
2.FilterHostConfig 维护一个javax.servlet.FilterConfig
3.Initializes the internal Struts logging
4.创建Dispatcher实例(ServletContext servletContext, Map<String, String> initParams)
5.dispatcher的初始化:
<1>default_properties: org/apache/struts2/default.properties
<2>
if(config != null) :加载config参数配置的xml文件(多个xml文件用","分隔)
else:default_xml:struts-default.xml,struts-plugin.xml,struts.xml
<3>加载configProviders参数定义的配置信息(定义实现ConfigurationProvider接口的类)
6.初始化静态资源的加载器
默认的静态资源位置 org.apache.struts2.static template org.apache.struts2.interceptor.debugging static
首先加载的是packages参数配置的静态资源位置
7.创建PrepareOperations实例(servletContext, dispatcher)
8.创建ExecuteOperations实例(servletContext, dispatcher)
9.清空Context
我们把struts2分为两块:一是struts2初始化,二是struts2处理请求,对请求作出响应。
这篇,我们先来分析下struts2的启动过程这部分。
struts2的启动入口为org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter。struts2的启动就是执行它的init()方法。我觉得它的启动就是为了一个目的,那就是为了创建出container对象和packageconfig对象。
public void init(FilterConfig filterConfig) throws ServletException { InitOperations init = new InitOperations(); try { FilterHostConfig config = new FilterHostConfig(filterConfig); init.initLogging(config); 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(); } }
FilterConfig filterConfig就是web.xml的配置信息。在init中对它进行了封装,变成了FilterHostConfig,当然,这些不是重点。我们关注的是
Dispatcher dispatcher = init.initDispatcher(config);
创建dispatcher(调度器),struts2正是通过这个调度器来实现整个控制功能的。
public Dispatcher initDispatcher( HostConfig filterConfig ) { Dispatcher dispatcher = createDispatcher(filterConfig); dispatcher.init(); return dispatcher; }
我们关注的是
dispatcher.init();
public void init() { if (configurationManager == null) { configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); } try { init_FileManager(); init_DefaultProperties(); // [1] init_TraditionalXmlConfigurations(); // [2] init_LegacyStrutsProperties(); // [3] init_CustomConfigurationProviders(); // [5] init_FilterInitParameters() ; // [6] init_AliasStandardObjects() ; // [7] 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); } }
首先得到的是配置管理器。这个配置管理器控制整个配置的加载过程。。[1]...[7] 是向configurationManager的加载器列表(containerProviders)中添加加载器(Provider),它们分别是:FileManagerProvider,DefaultPropertiesProvider,StrutsXMLConfigurationProvider等等。接下来是非常重要的一句:
Container container = init_PreloadConfiguration();
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); return container; }
public synchronized Configuration getConfiguration() { if (configuration == null) { setConfiguration(createConfiguration(defaultFrameworkBeanName)); try { configuration.reloadContainer(getContainerProviders()); } catch (ConfigurationException e) { setConfiguration(null); throw new ConfigurationException("Unable to load configuration.", e); } } else { conditionalReload(configuration.getContainer()); } return configuration; }
当设置完了configuration之后,才开始真正的创建"Container和PackageConfig对象"
configuration.reloadContainer(getContainerProviders());
public synchronized List reloadContainer(List providers) throws ConfigurationException { packageContexts.clear(); loadedFileNames.clear(); List packageProviders = new ArrayList(); ContainerProperties props = new ContainerProperties(); ContainerBuilder builder = new ContainerBuilder(); Container bootstrap = createBootstrapContainer(providers); for (final ContainerProvider containerProvider : providers) { bootstrap.inject(containerProvider); containerProvider.init(this); containerProvider.register(builder, props); } props.setConstants(builder); builder.factory(Configuration.class, new Factory() { 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 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 packageProviderNames = container.getInstanceNames(PackageProvider.class); 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; }
你会看到遍历providers,将每个加载器进行初始化和注册到builder中。builder是建造container的建造者。我们后面会看到,先不管这么多。我们接着看代码。
其实很多provider的init都是空的代码,不需要进行初始化,我们关注一个很重要的provider:StrutsXMLConfigurationProvider,这个provider是加载三个默认配置文件的。struts-default.xml,struts-lugin.xml,struts.xml。前两个是框架级别的,后面一个是应用级别的。我们看看它的init代码吧。
public void init(Configuration configuration) { this.configuration = configuration; this.includedFileNames = configuration.getLoadedFileNames(); loadDocuments(configFileName); }
private List loadConfigurationFiles(String fileName, Element includeElement) { List docs = new ArrayList(); List finalDocs = new ArrayList(); if (!includedFileNames.contains(fileName)) { if (LOG.isDebugEnabled()) { LOG.debug("Loading action configurations from: " + fileName); } includedFileNames.add(fileName); Iterator 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 { if (LOG.isInfoEnabled()) { 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()); 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() { 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) { // handleWildCardIncludes(includeFileName, docs, child); ClassPathFinder wildcardFinder = new ClassPathFinder(); wildcardFinder.setPattern(includeFileName); Vector wildcardMatches = wildcardFinder.findMatches(); for (String match : wildcardMatches) { finalDocs.addAll(loadConfigurationFiles(match, child)); } } else { finalDocs.addAll(loadConfigurationFiles(includeFileName, child)); } } } } finalDocs.add(doc); loadedFileUrls.add(url.toString()); } if (LOG.isDebugEnabled()) { LOG.debug("Loaded action configuration from: " + fileName); } } return finalDocs; }
其实它就是为了生成Document对象而已,加入到finalDocs列表中,这个将会在以后的register和loadpackage中用到。需要注意的是if ("include".equals(nodeName)) ,这个会将它包含的文件也加入到finalDocs中。
我们接着原来的代码,到了containerProvider.register(builder, props);
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (servletContext != null && !containerBuilder.contains(ServletContext.class)) { containerBuilder.factory(ServletContext.class, new Factory() { public ServletContext create(Context context) throws Exception { return servletContext; } }); } super.register(containerBuilder, props); }
我们只关注super.register(containerBuilder, props);
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { if (LOG.isInfoEnabled()) { LOG.info("Parsing configuration file [" + configFileName + "]"); } 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(); 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 { if (containerBuilder.contains(ctype, name)) { 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); } containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope); } 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); } } } else if ("constant".equals(nodeName)) { String name = child.getAttribute("name"); String value = child.getAttribute("value"); props.setProperty(name, value, childNode); } else if (nodeName.equals("unknown-handler-stack")) { List unknownHandlerStack = new ArrayList(); 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); } } } } }
我们先在这里插入一些东西:我们把provider分成两类:ContainterProvider和PackageProvider。你可以去看ConfigurationProvider接口,它就是继承了这两个接口的。
它们对外提供的功能主要就是从配置文件中加载对应的元素并转换为JAVA对象,注册到容器中。ContainterProvider是为了去加载容器配置元素:bean和constant等,这些元素要纳入容器中管理。PackageProvider是为了去加载Package配置元素,里面包含了 action,interceptor,result等运行时的事件映射节点,这些节点元素并不需要纳入容器中管理。struts2初始化的核心就是对容器配置元素和事件映射元素这两种不同元素的初始化过程,再进一步的讲就是将以各种形式配置的这两种元素转换为JAVA对象并统一管理的过程。
上面的代码应该不难分析,就是遍历每个Doc,解析每个配置文件,对于bean元素,得到它的属性,我们关注的是
containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
这里是把这种bean注册到ContainerBuilder的factories中去,那为什么是Builder呢?其实struts2在构造container时是采用了建造者模式的,它由builder来构造。builder.create(),当所有的bean注册完成后,就开始构造容器并把这些map放入到容器中去。你可以先看下面的分析,再会过来看这部分。
我们接着看ContainerBuilder的factory的代码。
public class LocatableFactory extends Located implements Factory { private Class implementation; private Class type; private String name; private Scope scope; public LocatableFactory(String name, Class type, Class implementation, Scope scope, Object location) { this.implementation = implementation; this.type = type; this.name = name; this.scope = scope; setLocation(LocationUtils.getLocation(location)); } @SuppressWarnings("unchecked") public T create(Context context) { Object obj = context.getContainer().inject(implementation); return (T) obj; } //................
}
我们可以看出我们把bean的name,type,class等属性传入到LocatableFactory,当我们想要一个bean对象,那么就可以从LocatableFactory中取出来。LocatableFactory.create(),这样就可以得到我们的一个对象。从这里可以看出在struts2容器中并不是存放bean对象,而是产生出bean对象的工厂。你后面从代码也可以看出来。
public ContainerBuilder factory(final Class type, final String name, final Factory<? extends T> factory, Scope scope) { InternalFactory internalFactory = new InternalFactory() { public T create(InternalContext context) { try { Context externalContext = context.getExternalContext(); return factory.create(externalContext); } catch (Exception e) { throw new RuntimeException(e); } } //........................ return factory(Key.newInstance(type, name), internalFactory, scope); }
ContainerBuilder的factory中创建了一个内置factory:InternalFactory internalFactory。这是struts2容器内部使用的Factory,而不是我们开发者使用的。很明显这些对我们传入的factory进行的封装。
我们接着看代码:factory(Key.newInstance(type, name), internalFactory, scope);
从这行代码,我们也可以看出:其实在struts2容器中的键是:type和 name的联合主键。
private ContainerBuilder factory(final Key key, InternalFactory<? extends T> factory, Scope scope) { ensureNotCreated(); checkKey(key); final InternalFactory<? extends T> scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory); factories.put(key, scopedFactory); if (scope == Scope.SINGLETON) { singletonFactories.add(new InternalFactory() { public T create(InternalContext context) { try { context.setExternalContext(ExternalContext.newInstance( null, key, context.getContainerImpl())); return scopedFactory.create(context); } finally { context.setExternalContext(null); } } }); } return this; }
这里是factory的一个重载方法。我们先来关注这两行代码:
final InternalFactory<? extends T> scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory); factories.put(key, scopedFactory);
我们看看Scope类:这是个枚举类。里面每种类型都实现了scopeFactory方法。
public enum Scope { DEFAULT { @Override InternalFactory<? extends T> scopeFactory(Class type, String name, InternalFactory<? extends T> factory) { return factory; } }, SINGLETON { @Override InternalFactory<? extends T> scopeFactory(Class type, String name, final InternalFactory<? extends T> factory) { return new InternalFactory() { T instance; public T create(InternalContext context) { synchronized (context.getContainer()) { if (instance == null) { instance = factory.create(context); } return instance; } } }; } }, THREAD { @Override InternalFactory<? extends T> scopeFactory(Class type, String name, final InternalFactory<? extends T> factory) { return new InternalFactory() { final ThreadLocal threadLocal = new ThreadLocal(); public T create(final InternalContext context) { T t = threadLocal.get(); if (t == null) { t = factory.create(context); threadLocal.set(t); } return t; } }; } }, REQUEST { @Override InternalFactory<? extends T> scopeFactory(final Class type, final String name, final InternalFactory<? extends T> factory) { return new InternalFactory() { public T create(InternalContext context) { Strategy strategy = context.getScopeStrategy(); try { return strategy.findInRequest( type, name, toCallable(context, factory)); } catch (Exception e) { throw new RuntimeException(e); } } @Override public String toString() { return factory.toString(); } }; } }, SESSION { InternalFactory<? extends T> scopeFactory(final Class type, final String name, final InternalFactory<? extends T> factory) { return new InternalFactory() { public T create(InternalContext context) { Strategy strategy = context.getScopeStrategy(); try { return strategy.findInSession( type, name, toCallable(context, factory)); } catch (Exception e) { throw new RuntimeException(e); } } @Override public String toString() { return factory.toString(); } }; } }, WIZARD { @Override InternalFactory<? extends T> scopeFactory(final Class type, final String name, final InternalFactory<? extends T> factory) { return new InternalFactory() { public T create(InternalContext context) { Strategy strategy = context.getScopeStrategy(); try { return strategy.findInWizard( type, name, toCallable(context, factory)); } catch (Exception e) { throw new RuntimeException(e); } } }; } };
abstract InternalFactory<? extends T> scopeFactory( Class type, String name, InternalFactory<? extends T> factory); }
我摘取了Scope的部分代码。在Scope枚举中声明了一个抽象方法 scopeFactory ,所以每一个枚举实例都实现了这个方法,它们各自实现了创建了不同生命周期的对象,其默认值为 singleton,即它是返回一个单例对象。Scope.DEFAULT 则是不做处理 直接返回 factory,这样当调用create方法时候,每次都是创建一个新对象。
其实可以参照我的上篇文章如何使用struts2中提供的IOC进行测试。我测试过了,确实是这样。
我们再返回factories.put(key, scopedFactory)。从这里我们现在可以肯定的说是把factory注册到builder中(我们先说是builder,其实后面会放入到container中)。不知不觉,我们已经把一个bean注册到builder中去了(放入到它的factories这个map中)。不要忘了key是type和name的联合主键。也许你早就不知道我们该返回哪行代码,继续分析啦。快哭了
仔细 回顾,我们要返回reloadContainer方法啦。在那里我们从遍历provider到provider的初始化到它的注册一直分析到每个bean的注册到builder中。我们接着往下分析:终于看到了这行代码:
container = builder.create(false);
是不是很开心,这不就是构造container嘛。
public Container create(boolean loadSingletons) { ensureNotCreated(); created = true; final ContainerImpl container = new ContainerImpl( new HashMap<KEY<?>, InternalFactory<?>>(factories)); if (loadSingletons) { container.callInContext(new ContainerImpl.ContextualCallable() { public Void call(InternalContext context) { for (InternalFactory<?> factory : singletonFactories) { factory.create(context); } return null; } }); } container.injectStatics(staticInjections); return container; }
class ContainerImpl implements Container { final Map<KEY<?>, InternalFactory<?>> factories; final Map<CLASS<?>, Set> factoryNamesByType; ContainerImpl( Map<KEY<?>, InternalFactory<?>> factories ) { this.factories = factories; Map<CLASS<?>, Set> map = new HashMap<CLASS<?>, Set>(); for ( Key<?> key : factories.keySet() ) { Set names = map.get(key.getType()); if (names == null) { names = new HashSet(); map.put(key.getType(), names); } names.add(key.getName()); } for ( Entry<CLASS<?>, Set> entry : map.entrySet() ) { entry.setValue(Collections.unmodifiableSet(entry.getValue())); } this.factoryNamesByType = Collections.unmodifiableMap(map); }
//..................
}
ContainerImpl是Container的一个实现。这个构造函数主要做3件事,1:得到builder的factories。2:为 Key(type,name) --- InternalFactory的 Map实例字段 赋值,其来源就是 ContainerBuilder中的factories.3:将 type 和 name 的一对多关系保存在 Map实例字段 factoryNamesByType 中。 把对象生命周期为单实例的对象先创建出来,其中if语句调用回调函数,将属于单例模式的bean事先调用create方法。singletonFactories变量中存放的是容器中属于单例模式的工厂的引用。这样一个完整的struts2容器就生成了。
我们这里目前只是生成了Container对象,还没有生成PackageConfig对象啊。接下来就是这部分了。(还是在reloadContainer方法中。紧随container = builder.create(false)之后。)。
/ Process the configuration providers first 先得到providers中实现了PackageProvider的provider 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来源于插件中直接实现PackageProvider接口的类 Set packageProviderNames = container.getInstanceNames(PackageProvider.class); for (String name : packageProviderNames) { PackageProvider provider = container.getInstance(PackageProvider.class, name); provider.init(this); provider.loadPackages(); packageProviders.add(provider); }
你还记得PackageProvider和ContainerProvider吗?只有得到了PackageProvider才能去加载package配置信息。因为这之前我们已经创建出了container,所以可以用注入:container.inject(containerProvider)。然后loadPackages加载package。这样就完成了整个PackageConfig对象的生成。
struts2的两类配置元素Container和PackageConfig 已经初始化完毕了。其中Container提供了IOC机制。struts2的启动过程也完成了。看了几天的代码,终于有点眉目了。。接下来会分析下struts2的处理请求的过程。
相关推荐
4. 启动扫描,工具将自动尝试多种攻击向量来检测Struts2-057漏洞。 5. 扫描完成后,查看报告,了解扫描结果。 "cmd.txt"可能包含了扫描工具的命令行参数或使用说明,用户可以参考这份文档来正确运行和配置扫描工具...
通过这个插件,你可以直接在JUnit测试用例中执行Struts2的Action,验证业务逻辑,无需启动完整的Web服务器。 在使用`struts2-junit-plugin-2.1.8.1.jar`之前,你需要确保已经安装并配置了以下组件: 1. **JDK**:...
### Struts2-Portlet学习手册 ...作为一种强大的开发工具,Struts2-Portlet不仅能够简化Portlet的开发过程,还能够帮助开发者构建出高效、稳定的应用程序。希望本手册能够对你在实际工作中有所帮助。
Struts2-blank是一个基础的、预配置的Struts2框架项目模板,旨在为开发者提供一个快速启动新项目的基础环境。Struts2是一款强大的、基于MVC(Model-View-Controller)架构的Java web框架,它极大地简化了构建Java ...
整合Struts2、Spring和Hibernate的过程主要包括以下几个步骤: 1. **Struts2配置**: - 首先,需要将Struts2的相关库文件(如struts2-core.jar)添加到项目的类路径中。 - 接着,在`web.xml`文件中配置Struts2的...
Struts2 Convention Plugin极大地简化了Struts2应用的开发和维护过程,通过遵循约定优于配置的原则,减少了大量的手动配置工作,使得开发人员能够更加专注于业务逻辑的实现。对于那些追求高效开发流程的项目而言,这...
例如,struts-2.x.x-all.zip包含了所有必要的库。核心库包括struts2-core-2.x.x.jar,它是框架的基础,提供Action、Interceptor等核心概念。xwork-2.x.x.jar是XWork框架,是Struts2的基石。ognl-2.6.x.jar是对象图...
1. 配置环境:确保项目中引入了所有必需的jar包,并正确配置了web.xml文件以启动Struts2过滤器。 2. 设计Action:根据业务需求创建Action类,实现对应的业务逻辑。 3. 编写配置:配置struts.xml文件,定义Action、...
在这个过程中,攻击者可以利用OGNL(Object-Graph Navigation Language)表达式注入,因为Struts2会尝试执行用户的输入作为OGNL表达式。攻击者可以通过构造特殊的HTTP请求,使得输入的数据在转换时被OGNL解释,从而...
1. **安装与集成**:将下载的Struts 2框架添加到项目类路径中,配置web.xml文件以启动Struts 2的前端控制器。 2. **创建Action类**:编写Action类并实现必要的业务逻辑。 3. **配置Action**:在struts.xml文件中...
Struts2是一个流行的Java Web应用程序框架,用于构建和维护可扩展、模块化和易于管理的企业级应用。这个压缩包"Struts2.3.7-all所有的jar包.zip"包含了...开发者可以解压后导入到他们的项目中,快速启动Struts2开发。
同时,还需要在web.xml中进行相关的初始化配置,以启动Struts2的前端控制器。 总的来说,Struts2是一个功能强大且灵活的Java Web框架,它简化了MVC模式的实现,提高了开发效率,同时提供了丰富的特性来满足复杂的...
在初始化过程中,Struts2会解析`struts.xml`的内容,并基于这些配置创建Action和对应的处理逻辑。通过配置文件,你可以定义Action类、Action的执行方法、结果页面的跳转规则以及使用哪些拦截器。 总的来说,Struts2...
在Java应用程序中,初始化通常指的是程序启动时对类、对象或者特定模块进行设置的过程。 在Struts框架中,初始化过程涉及到以下几个关键知识点: 1. **Struts ActionServlet**:这是Struts的核心控制器,负责处理...
"struts2最小标准lib"是一个精简版的库集合,它包含了运行Struts2框架最基本的功能组件,适合那些希望在项目中快速启动并运行Struts2的开发者。 这个压缩包中包含的文件很可能是Struts2的核心库文件,比如`struts2-...
- 开发者需要在`web.xml`中配置Struts 2的前端控制器(`FilterDispatcher`或`StrutsPrepareAndExecuteFilter`)以启动框架。 - 配置`struts.xml`文件来定义Action、拦截器和结果类型。 - 使用注解或XML配置Action...
Struts2是一个强大的MVC(模型-视图-控制器)框架,被广泛应用于Java Web开发。Struts2.3.1是该框架的一个版本,它提供了很多改进和新特性,旨在提升开发效率和应用程序的稳定性。这个例子主要关注的是如何将`struts...
2. **ActionServlet**:Struts框架中的控制器,负责接收客户端请求并将其转发给相应的Action进行处理。 3. **Action**:实现了Action接口的类,用于处理特定的业务逻辑。 4. **FormBean**:一种特殊的JavaBean,...
总的来说,Struts2精简jar包提供了一种便捷的方式来启动基于Struts2的项目,减少了配置工作量。但开发者仍需了解Struts2的工作原理,以及如何根据需求调整和扩展框架,以实现高效且可靠的web应用开发。