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的处理请求的过程。
相关推荐
这个项目的初衷是为了将groovy引入我们的struts web项目,在开发的过程中,发现有跟多可以做的是全,不过在最初的版本,struts-gpipe主要包括两个功能:1,经过简单的配置可以将groovy引入struts项目,这个时候我们...
项目功能:web启动时,使用quartz启动所有任务。 辅助功能:查看、新增、修改、删除任务列表。 主要jar版本:struts-1.0,quartz-all-1.8.5.jar 开发环境:eclipse 3.2 + oracle 11g 发布环境:tomcate 5.5,若...
2.1.2 MVC模式的处理过程 2.2 Model规范 2.2.1 Model1规范 2.2.2 Model2规范 2.3 使用MVC的优劣 2.3.1 使用MVC模式的好处 2.3.2 使用MVC模式的不足之处 2.4 目前市场上常见的轻量级J2EE开发容器 2.5 小结 第二篇 ...
Struts原理、开发及项目实施 Holen 2002-9-12 <br/>1、 摘要 2、 关键词 3、 Framework 4、 Struts的起源 5、 Struts工作原理 6、 Struts安装 7、 一个实例 8、 Struts优缺点...
2.1.2 MVC模式的处理过程 2.2 Model规范 2.2.1 Model1规范 2.2.2 Model2规范 2.3 使用MVC的优劣 2.3.1 使用MVC模式的好处 2.3.2 使用MVC模式的不足之处 2.4 目前市场上常见的轻量级J2EE开发容器 2.5 小结 第二篇 ...
2.1.2 MVC模式的处理过程 2.2 Model规范 2.2.1 Model1规范 2.2.2 Model2规范 2.3 使用MVC的优劣 2.3.1 使用MVC模式的好处 2.3.2 使用MVC模式的不足之处 2.4 目前市场上常见的轻量级J2EE开发容器 2.5 小结 第二篇 ...
知道了这个过程,你就知道应该去怎么配置spring和struts以及ibatis了。 -------------------------------------------------- 顺便说下,log4j日志环境的搭建。很简单。 1 导入 commons-logging-1.1.jar log4j-...
案例6-2 结合Hibernate和Struts实现商务系统身份验证 224 第7章 Eclipse中SWT/JFace开发 237 7.1 安装SWT Designer 237 7.2 SWT/JFace程序基本实现过程 242 案例7-1 SWT/JFace开发初体验 243 7.3 SWT/...
全书分4篇,共24章,其中,第1篇为技能学习篇,主要包括Java Web开发环境、JSP语法、JSP内置对象、Java Bean技术、Servlet技术、EL与JSTL标签库、数据库应用开发、初识Struts2基础、揭密Struts2高级技术、Hib锄劬e...
2. 深入 STRUTS架构..................................................................................37 2.1. 随便谈谈......................................................................................
如J2EE体系 结构图1-2所示: 图1-2 J2EE应用系统结构 1.2.4 J2EE 1. J2EE介绍 J2EE(Java 2 Platform, Enterprise Edition)是SUN公司定义的一个开放式企业级应用规范,他提供了一个多层次的分布式应 用模型和一系列...
本文后面的章节将从数据持久层->业务层->Web层的开发顺序,逐层讲解文件上传下载的开发过程。 数据持久层 1、领域对象及映射文件 您可以使用Hibernate Middlegen、HIbernate Tools、Hibernate ...
2. 深入STRUTS 架构..................................................................................37 2.1. 随便谈谈.......................................................................................
2、第一个文件是一个web项目,可以导入MyEclipse中直接启动看效果; 3、第二个文件时一个word文档,记录了UEditor的使用过程; 4、需要一个资源分只是为了能下载东西,往大伙儿见谅; 5、最后一点是提示:如果要把...
本章我们将会开发基于Struts2框架的Hello World.我们的Hello World程序是你开发基于Struts2框架程序的第一步.这里我们会提供循序渐进的教程来开发基于Struts2框架的Hello World程序. 教程包含基本的步骤像创建目录...
Mojito JS交付轻量级(<5kb)JS框架,用于在网络上构建,发布和跟踪实验。... 阅读有关构建,预览和启动过程的信息。 步骤4.托管和发布托管您的容器并使用CI自动执行发布过程。 了解有关通过AWS和Bit
2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring...
所谓热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。 对于Java应用程序来说,热部署就是在运行时更新Java类文件。在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色。大多数...
2.1.2 MVC模式的处理过程 2.2 Model规范 2.2.1 Model1规范 2.2.2 Model2规范 2.3 使用MVC的优劣 2.3.1 使用MVC模式的好处 2.3.2 使用MVC模式的不足之处 2.4 目前市场上常见的轻量级J2EE开发容器 2.5 小结 第二篇 ...