这一篇博客,我将带着大家从源码的角度来分析环境搭建的配置。
我们都知道,一个JAVA EE程序基本的配置都是在web.xml
文件中的,我们就从这个文件开始分析。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>test</display-name>
<servlet>
<servlet-name>study</servlet-name>
<servlet-class>self.micromagic.app.EternaServlet</servlet-class>
<init-param>
<param-name>initFiles</param-name>
<param-value>cp:study/index.xml;</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>study</servlet-name>
<url-pattern>/study.do</url-pattern>
</servlet-mapping>
<jsp-config>
<taglib>
<taglib-uri>http://code.google.com/p/eterna</taglib-uri>
<taglib-location>/WEB-INF/tld/eterna.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
在web.xml文件中,我们定义了一个Servlet,映射了/study.do
路径,这个Servlet是eterna为我们提供的一个实现,我们在学习的时候可以直接使用。在这个Servlet配置中,我们使用了一个初始化参数initFiles
,表示要初始化的文件,我们可以查看EternaServlet
的源码,看看还有那些配置可以使用。
代码清单1
/**
* 用于加载eterna配置的servlet.
*
* 可设置的参数如下:
*
* defaultModel 当没有指定model名称时, 调用此属性指定的默认的model
* 默认值为: ModelCaller.DEFAULT_MODEL_NAME
*
* initFiles 需要加载的eterna的配置文件列表
*
* parentFiles 需要加载的eterna父配置文件列表
*
* charset 使用的字符集, 默认值为: UTF-8
*
*
* @see ModelCaller#DEFAULT_MODEL_NAME
* @see FactoryManager#CONFIG_INIT_FILES
* @see FactoryManager#CONFIG_INIT_PARENTFILES
*
* @author micromagic@sina.com
*/
在EternaServlet类注释上,已经告诉我们了可以使用的配置参数。值得一提的是在参数中有parentFiles
和initFiles
都是配置文件列表,二者的关系有点像JAVA的继承,在子初始化文件中如果存在和父初始化文件中相同的组件,那么父初始化文件中的组件将被覆盖。在配置初始化文件时使用cp:
表示classpath路径。
继续向下看这个类的源码
代码清单2
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
if (!this.charset.equals(request.getCharacterEncoding()))
{
request.setCharacterEncoding(this.charset);
}
response.setContentType("text/html;charset=" + this.charset);
AppData data = AppData.getCurrentData();
data.request = request;
data.response = response;
data.contextRoot = request.getContextPath();
data.servletConfig = this.getServletConfig();
data.position = AppData.POSITION_SERVLET;
try
{
data.maps[AppData.REQUEST_PARAMETER_MAP] = RequestParameterMap.create(request);
data.maps[AppData.REQUEST_ATTRIBUTE_MAP] = ValueContainerMap.createRequestAttributeMap(request);
data.maps[AppData.SESSION_ATTRIBUTE_MAP] = ValueContainerMap.createSessionAttributeMap(request);
String queryStr = request.getQueryString();
if (queryStr != null)
{
String modelNameTag = this.getFactoryManager().getEternaFactory().getModelNameTag();
int index;
int plusCount = 2;
if (queryStr.length() > 0 && queryStr.charAt(0) != '?')
{
if (queryStr.startsWith(modelNameTag + "="))
{
index = 0;
plusCount = 1;
}
else
{
index = -1;
}
}
else
{
index = queryStr.indexOf("?" + modelNameTag + "=");
}
if (index == -1)
{
index = queryStr.indexOf("&" + modelNameTag + "=");
}
if (index != -1)
{
int endIndex = queryStr.indexOf('&', index + 1);
if (endIndex != -1)
{
data.modelName = queryStr.substring(index + modelNameTag.length() + plusCount, endIndex);
}
else
{
data.modelName = queryStr.substring(index + modelNameTag.length() + plusCount);
}
}
}
request.setAttribute(ModelCaller.DEFAULT_MODEL_TAG, this.defaultModel);
ModelExport export = this.getFactoryManager().getEternaFactory().getModelCaller().callModel(data);
if (export != null)
{
data.export = export;
}
}
catch (ConfigurationException ex)
{
log.warn("Error in service.", ex);
}
catch (SQLException ex)
{
log.warn("SQL Error in service.", ex);
}
catch (Throwable ex)
{
log.error("Other Error in service.", ex);
}
finally
{
try
{
if (data.export != null)
{
this.doExport(data, request, response);
}
}
catch (Throwable ex)
{
log.error("Error in doExport.", ex);
}
data.modelName = null;
data.export = null;
data.servletConfig = null;
data.clearData();
}
}
这是Servlet处理客户端请求的方法,eterna首先会设置请求和响应的编码,然后对AppData中的属性做一些赋值操作(AppData会贯穿eterna整个处理过程),在EternaServlet中是通过解析QueryString
来获得需要执行的Model的,所以正常情况下,请求地址应该形如:http://[domain]:[host]/[servletmapping]?model=[modelName],以搭建的环境为例,我们请求的地址应为:http://localhost/eterna/study.do?model=[modelName],而在我们之前测试的时候,我们并没有添加查询参数也可以访问,其实通过代码,我们不难理解,当不存在modelName时,eterna会使用默认的modelName,ModelCaller.DEFAULT_MODEL_NAME
,其实也就是index
,我们在测试时访问的地址就等价于:http://localhost/eterna/study.do?model=index。
在eterna中是有日志的,在我们搭建的环境中并没有配置,所以需要看日志的小伙伴可以在src
文件夹下添加commons-logging.properties
文件,文件参考内容如下:
priority=100
org.apache.commons.logging.LogFactory=self.micromagic.util.Jdk14Factory
接下来,我们再来看index.xml
,这个是eterna的配置文件
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE eterna-config PUBLIC "eterna" "http://eterna.googlecode.com/files/eterna_1_5.dtd">
<eterna-config>
<factory>
<objs>
<export name="index.export" path="/view.jsp" viewName="index.view" />
<model name="index" modelExportName="index.export"
transactionType="notNeed" />
<view name="index.view">
<component name="div" type="div"
comParam="text:'this is my first eterna application.'" />
</view>
</objs>
</factory>
</eterna-config>
eterna配置文件的根元素为eterna-config
,在根元素下包含的唯一元素为factory
,表示用于构造所有对象的工厂。
objs
表示所有对象的集合。
export
元素代表一个业务的出口。
属性名 | 说明 |
---|---|
name | “export”的名称, 它在同一个”Factory”中必须是唯一的. |
modelName | 指定转到下一个”model”的名称. 如果指定了modelName, 则不可指定viewName、path、redirect |
path | 指定业务出口的页面,如果指定了path则不可指定modelName.另外modelName和path必须指定一个 |
viewName | 指定出口所使用的”view”对象 |
errorExport | 业务出口是否是一个出错的处理出口, 默认为”false”. |
redirect | 业务出口是否需要redirect, 默认为”false” |
model
元素代表代表一个业务。
属性名 | 说明 |
---|---|
name | “ModelAdapter”的名称, 它在同一个”Factory”中必须是唯一的 |
generator | 指定”ModelAdapter”的构造器, 这个类必须实现 [“self.micromagic.eterna.model.ModelAdapterGenerator”]接口 |
keepCaches | 是否需要保持所有的cache, 在退出这个model后和进入model前 一致, 默认为”false”. |
frontModelName | 需要执行前置model的名称, 只有needFrontModel为”true”时, 这个属性才有效. 注: frontModel在一次完整的model请求期间只会执行一次 |
modelExportName | 指定业务的正常出口 |
errorExportName | 指定业务的异常(或出错处理)出口. |
transactionType | 业务的事务处理方式, 默认为”requared” |
dataSourceName | 使用的数据源名称 |
positions | 可执行此业务的位置, 默认为”servlet”和”portletRender” |
model、view、export就是典型的MVC。
component
对应html中的一个元素,具体属性的说明,我们可以查看配置文件的dtd,这里就不再赘述了。