问题
在详细介绍ibatis初始化过程之前,让我们先来思考几个问题。1. ibatis初始化的目标是什么?上文中提到过,ibatis初始化的核心目标是构造SqlMapClientImpl对象,主要是其内部重要属性delegate这个代理对象的初始化。delegate这个对象耦合了用户端的操作行为和执行环境,持有执行操作所需要的所有数据。2. 如何解析ibatis的sqlmap配置文件和sqlmap映射文件?可以采用通用的xml文件解析工具,如SAX等。3. 如何将配置文件中每个节点值注入到SqlMapClientImpl对象中?可以给不同类型节点设置对应的handler,遍历节点时,调用handler对象的处理方法,将节点值注入到SqlMapClientImpl对象的属性中。带着上面的这些问题,我们开始探索ibatis的初始化过程。核心类图初始化过程主要涉及以下几个重要类,理解这些类的含义非常重要。主要类图如下:1. Nodelet该接口是对配置文件中节点处理方式的抽象,该接口仅有一个process()方法,表示对该类节点的处理方式。2. NodeletParser同SAX类似的一个xml解析器。不同点在于SAX对所有节点采用同样的处理方式,而NodeletParser可以针对不同的节点个性化配置相 应的处理方式。 NodeletParser内部维护了一个letMap,这个map维护着节点标识信息XPath和对应的处理方式Nodelet的关系。3. XmlParserState用于维护配置文件解析过程中的上下文信息,配置文件解析过程中产生的数据都放入XmlParserState中统一维护。注意: ibatis2.3.0版本中没有这个类,它使用BaseParser的内部类Variables维护上下文信息。4. SqlMapConfigParser用于解析sqlMap配置文件,其内部组合了NodeletParser对象,用于完成sqlMap配置文件解析。该对象构造函数中,完成向NodeletParser属性中添加sqlMap配置文件中节点XPath和对应的Nodelet映射关系。比如<typeAlias>节点的处理方式如下:- private void addTypeAliasNodelets() {
- parser.addNodelet("/sqlMapConfig/typeAlias", new Nodelet() {
- public void process(Node node) throws Exception {
- Properties prop = NodeletUtils.parseAttributes(node, state.getGlobalProps()); //解析节点信息
- String alias = prop.getProperty("alias");
- String type = prop.getProperty("type");
- state.getConfig().getTypeHandlerFactory().putTypeAlias(alias, type); //向XmlParserState写数据
- }
- });
- }
- private void addSqlNodelets() {
- parser.addNodelet("/sqlMap/sql", new Nodelet() {
- public void process(Node node) throws Exception {
- Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
- String id = attributes.getProperty("id");
- if (state.isUseStatementNamespaces()) {
- id = state.applyNamespace(id);
- }
- if (state.getSqlIncludes().containsKey(id)) {
- throw new SqlMapException("Duplicate <sql>-include '" + id + "' found.");
- } else {
- state.getSqlIncludes().put(id, node);
- }
- }
- });
- }
- <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
- <property name="dataSource" ref="dataSource"/>
- <property name="configLocation" value="classpath/sqlmap-ibatis.xml"/>
- </bean>
- <sqlMapConfig>
- <sqlMap resource="sqlmap/cases/CaseSQL.xml"/>
- ...
- </sqlMapConfig>
- <select id="QUERY-CASE-BY-CASE-ID" parameterClass="map" resultMap="RM-Case">
- SELECT ID ,GMT_MODIFIED ,GMT_CREATE ,STATUS
- FROM AVATAR_CASE
- WHERE ID = #caseId#
- </select>
- public void afterPropertiesSet() throws Exception {
- ...
- this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties); //初始化核心方法,构建sqlMapClient对象 ...
- }
- protected SqlMapClient buildSqlMapClient(
- Resource[] configLocations, Resource[] mappingLocations, Properties properties)
- throws IOException {
- ...
- SqlMapClient client = null;
- SqlMapConfigParser configParser = new SqlMapConfigParser();
- for (int i = 0; i < configLocations.length; i++) {
- InputStream is = configLocations[i].getInputStream();
- try {
- client = configParser.parse(is, properties); //通过SqlMapConfigParser解析配置文件,生成SQLMapClientImpl对象
- }
- ...
- return client;
- }
- public SqlMapClient parse(InputStream inputStream, Properties props) {
- if (props != null) state.setGlobalProps(props);
- return parse(inputStream);
- }
- public SqlMapClient parse(InputStream inputStream) {
- ...
- parser.parse(inputStream); // 调用NodeletParser解析配置文件
- return state.getConfig().getClient(); //返回SqlMapClientImpl对象
- ...
- }
- public void parse(InputStream inputStream) throws NodeletException {
- try {
- Document doc = createDocument(inputStream);
- parse(doc.getLastChild()); //从根节点开始解析
- }
- ...
- }
- public void parse(Node node) {
- Path path = new Path();
- processNodelet(node, "/");
- process(node, path);
- }
- private void processNodelet(Node node, String pathString) {
- Nodelet nodelet = (Nodelet) letMap.get(pathString);
- if (nodelet != null) {
- try {
- nodelet.process(node);
- }
- ...
- }
- }
- private void process(Node node, Path path) {
- if (node instanceof Element) {
- //处理Element信息
- String elementName = node.getNodeName();
- path.add(elementName);
- processNodelet(node, path.toString());
- processNodelet(node, new StringBuffer("//").append(elementName).toString());
- //处理Attribute信息
- NamedNodeMap attributes = node.getAttributes();
- int n = attributes.getLength();
- for (int i = 0; i < n; i++) {
- Node att = attributes.item(i);
- String attrName = att.getNodeName();
- path.add("@" + attrName);
- processNodelet(att, path.toString());
- processNodelet(node, new StringBuffer("//@").append(attrName).toString());
- path.remove();
- }
- // 处理Children信息
- NodeList children = node.getChildNodes();
- for (int i = 0; i < children.getLength(); i++) {
- process(children.item(i), path); //递归处理子节点
- }
- path.add("end()");
- processNodelet(node, path.toString());
- path.remove();
- path.remove();
- } else if (node instanceof Text) {
- // Text
- path.add("text()");
- processNodelet(node, path.toString());
- processNodelet(node, "//text()");
- path.remove();
- }
- }
- protected void addSqlMapNodelets() {
- parser.addNodelet("/sqlMapConfig/sqlMap", new Nodelet() {
- public void process(Node node) throws Exception {
- ...
- Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
- String resource = attributes.getProperty("resource");
- String url = attributes.getProperty("url");
- ...
- new SqlMapParser(state).parse(reader); //调用SqlMapParser解析sqlMap映射文件
- }
- }
- });
- }
- public void parse(InputStream inputStream) throws NodeletException {
- parser.parse(inputStream);//调用NodeletParser解析配置文件
- }
- protected void addStatementNodelets() {
- ...
- parser.addNodelet("/sqlMap/select", new Nodelet() {
- public void process(Node node) throws Exception {
- statementParser.parseGeneralStatement(node, new SelectStatement());
- }
- });