Mybatis 分多个配置文, 重写org.mybatis.spring.SqlSessionFactoryBean支持多个mybatis-config.xml...

本文详细介绍了如何在MyBatis中整合多个配置文件,以支持不同功能模块使用各自的配置,减少代码检索时间和维护成本。通过自定义SqlSessionFactoryBean类,实现了从多个指定路径加载配置文件,并合并其内容,包括settings、typeAliases、plugins和mappers等部分。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现有版本的mybatis不支持多个mybatis-config.xml配置文件,但是实际应用中开发者希望它能与spring或struts一样支持多个配置文件,不同功能模块使用不同的配置文件以减少开发中代码检索的时间与维护成本.

下面,是我根据网上资料结合自己实际遇到的问题重写的

参考资料 http://www.xinglongjian.com/index.php/2012/09/15/mybatismanyconfig/

上面的链接中只合并了 typeAliases与mappers

但是我们实际的配置文件可能还包含 mybatis配置settings,mybatis插件plugins 至少我的项目需要这两项

spring中配置

<bean id="sqlSessionFactory_db1" class="cn.com.softvan.dao.utils.MySqlSessionFactoryBean">
		<property name="configLocationX" value="classpath*:config/mybatis/mybatis-config*.xml" />
		<property name="dataSource" ref="dataSource_db1" />
</bean>

 

具体类

package cn.com.softvan.dao.utils;

import static org.springframework.util.Assert.notNull;
import static org.springframework.util.ObjectUtils.isEmpty;
import static org.springframework.util.StringUtils.hasLength;
import static org.springframework.util.StringUtils.tokenizeToStringArray;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.sql.DataSource;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.DocumentHelper;
import org.dom4j.DocumentType;
import org.dom4j.Element;
import org.dom4j.ElementHandler;
import org.dom4j.ElementPath;
import org.dom4j.io.SAXReader;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.beans.decoder.ValueObject;

public class MySqlSessionFactoryBean extends	org.mybatis.spring.SqlSessionFactoryBean {
	private static final Log logger = LogFactory.getLog(MySqlSessionFactoryBean.class);
	private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
	private Interceptor[] plugins;
	private Class<?>[] typeAliases;
	private String typeAliasesPackage;
	private TypeHandler<?>[] typeHandlers;
	private String typeHandlersPackage;
	private TransactionFactory transactionFactory;
	private Properties configurationProperties;
	private Resource[] configLocations;
	private DataSource dataSource;
	private String environment = SqlSessionFactoryBean.class.getSimpleName();
	private DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
	private Resource[] mapperLocations;
	private SqlSessionFactory sqlSessionFactory;
	//-settings 是否获取标记--防止多次获取--
	private boolean settings_flag=true;
	//-plugins 是否获取标记--防止多次获取--
	private boolean plugins_flag=true;
	/* 修改该方法 */
	public void setConfigLocation(Resource configLocation) {
		this.configLocations = configLocation != null ? new Resource[] { configLocation } : null;
	}
	/* 增加该方法 */
	public void setConfigLocationX(String filePath) {
		 try {
			ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();  
			//将加载多个模式匹配的Resource  
            Resource[] resources = (Resource[]) resolver.getResources(filePath);  
            configLocations=resources;
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (logger.isDebugEnabled()) {
			if(configLocations!=null){
				for(Resource resource:configLocations){
					logger.debug("==================================="+resource);
				}
			}
		}
	}
	/**
	 * 合并mybatis配置文件
	 */
	public Document SQLConfigMap() {
		Document doc = DocumentHelper.createDocument();
		doc.setXMLEncoding("UTF-8");
		DocumentFactory documentFactory = new DocumentFactory();
		DocumentType docType = documentFactory.createDocType("configuration", "-//mybatis.org//DTD Config 3.0//EN", "http://mybatis.org/dtd/mybatis-3-config.dtd");
		doc.setDocType(docType);
		Element rootElement = doc.addElement("configuration");
		rootElement.addElement("settings");
		rootElement.addElement("typeAliases");
		rootElement.addElement("plugins");
		rootElement.addElement("mappers");
		return doc;
	}

	public void readXML(Resource configXML,
			final Element elementSettings,
			final Element elementTypeAlias,
			final Element elementPlugins,
			final Element elementMapper) {
		SAXReader saxReader = new SAXReader();
		saxReader.setEntityResolver(new EntityResolver() {
			@Override
			public InputSource resolveEntity(String publicId, String systemId)
					throws SAXException, IOException {
				String jarPath = SqlSessionFactory.class.getProtectionDomain()
						.getCodeSource().getLocation().getPath();
				String filePath = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
				InputStream jarIn = null;
				try {
					JarFile jarFile = new JarFile(jarPath);
					JarEntry jarEntry = jarFile.getJarEntry(filePath);
					jarIn = jarFile.getInputStream(jarEntry);
				} catch (IOException e) {
					e.printStackTrace();
				}
				return new InputSource(jarIn);
			}
		});
		/* settings 获取*/
		saxReader.addHandler("/configuration/settings/setting",
				new ElementHandler() {
					public void onEnd(ElementPath path) {
						if(settings_flag){
							//System.out.println("settings 获取---"+path.getPath());
							Element row = path.getCurrent();
							Element els = elementSettings.addElement("setting");
							els.addAttribute("name", row.attributeValue("name"))
							   .addAttribute("value", row.attributeValue("value"));
							row.detach();
						}
					}

					public void onStart(ElementPath arg0) {
					}
				});
		/* typeAliases合并 */
		saxReader.addHandler("/configuration/typeAliases/typeAlias",
				new ElementHandler() {
					public void onEnd(ElementPath path) {
						Element row = path.getCurrent();
						Element els = elementTypeAlias.addElement("typeAlias");
						els.addAttribute("alias", row.attributeValue("alias"))
								.addAttribute("type",row.attributeValue("type"));
						row.detach();
					}

					public void onStart(ElementPath arg0) {
					}
				});
		/* plugins合并 */
		saxReader.addHandler("/configuration/plugins/plugin",
				new ElementHandler() {
					public void onEnd(ElementPath path) {
						if(plugins_flag){
							//System.out.println("plugins 获取---"+path.getPath());
							Element row = path.getCurrent();
							Element els = elementPlugins.addElement("plugin");
							els.addAttribute("interceptor", row.attributeValue("interceptor"));
							//child
//							System.out.println("xxxx=====xxx=="+row.elements().size());
							if(row.elements()!=null){
								Iterator iter=row.elementIterator();
								while(iter.hasNext()){
									Element oldels=(Element) iter.next();
									Element els_property=els.addElement("property");
									els_property.addAttribute("name", oldels.attributeValue("name"))
												.addAttribute("value", oldels.attributeValue("value"));
								}
								
							}
							row.detach();
						}
					}

					public void onStart(ElementPath arg0) {
					}
				});
		/* mapper合并 */
		saxReader.addHandler("/configuration/mappers/mapper",
				new ElementHandler() {
					public void onEnd(ElementPath path) {
						Element row = path.getCurrent();
						Element els = elementMapper.addElement("mapper");
						String mapper = row.attributeValue("mapper");
						String resource = row.attributeValue("resource");
						els.addAttribute("mapper", mapper);
						els.addAttribute("resource", resource);
						row.detach();
					}

					public void onStart(ElementPath arg0) {

					}
				});

		try {
			saxReader.read(configXML.getInputStream());
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * @return SqlSessionFactory
	 * @throws IOException
	 */
	protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
		Configuration configuration = null;
		XMLConfigBuilder xmlConfigBuilder = null;
		Document document = this.SQLConfigMap();
		Element root = document.getRootElement();
		Element elementSettings = root.element("settings");
		Element elementTypeAlias = root.element("typeAliases");
		Element elementPlugins = root.element("plugins");
		Element elementMapper = root.element("mappers");
		for (Resource configLocation : configLocations) {
			readXML(configLocation,elementSettings, elementTypeAlias,elementPlugins, elementMapper);
			settings_flag=false;
			plugins_flag=false;
		}
		// Reader reader = null; InputStream inputStream = null;
		if (this.configLocations != null) {
			//--打印合并后的xml配置
			logger.debug("xml="+document.asXML());
			InputStream inputSteam = new ByteArrayInputStream(document.asXML().getBytes());
			xmlConfigBuilder = new XMLConfigBuilder(inputSteam, null,
					this.configurationProperties);
			configuration = xmlConfigBuilder.getConfiguration();
			if (inputSteam != null) {
				inputSteam.close();
				inputSteam = null;
			}
			document = null;
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Property 'configLocation' not specified,using default MyBatis Configuration");
			}
			configuration = new Configuration();
			configuration.setVariables(this.configurationProperties);
		}

		if (hasLength(this.typeAliasesPackage)) {
			String[] typeAliasPackageArray = tokenizeToStringArray( this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			for (String packageToScan : typeAliasPackageArray) {
				configuration.getTypeAliasRegistry().registerAliases(packageToScan);
				if (logger.isDebugEnabled()) {
					logger.debug("Scanned package: '" + packageToScan + "' for aliases");
				}
			}
		}
		if (!isEmpty(this.typeAliases)) {
			for (Class<?> typeAlias : this.typeAliases) {
				configuration.getTypeAliasRegistry().registerAlias(typeAlias);
				if (logger.isDebugEnabled()) {
					logger.debug("Registered type alias: '" + typeAlias + "'");
				}
			}
		}

		if (!isEmpty(this.plugins)) {
			for (Interceptor plugin : this.plugins) {
				configuration.addInterceptor(plugin);
				if (logger.isDebugEnabled()) {
					logger.debug("Registered plugin: '" + plugin + "'");
				}
			}
		}

		if (hasLength(this.typeHandlersPackage)) {
			String[] typeHandlersPackageArray = tokenizeToStringArray( this.typeHandlersPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			for (String packageToScan : typeHandlersPackageArray) {
				configuration.getTypeHandlerRegistry().register(packageToScan);
				if (logger.isDebugEnabled()) {
					logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
				}
			}
		}

		if (!isEmpty(this.typeHandlers)) {
			for (TypeHandler<?> typeHandler : this.typeHandlers) {
				configuration.getTypeHandlerRegistry().register(typeHandler);
				if (logger.isDebugEnabled()) {
					logger.debug("Registered type handler: '" + typeHandler + "'");
				}
			}
		}

		if (xmlConfigBuilder != null) {
			try {
				xmlConfigBuilder.parse();
				if (logger.isDebugEnabled()) {
					logger.debug("Parsed configuration file: '" + this.configLocations + "'");
				}
			} catch (Exception ex) {
				throw new NestedIOException("Failed to parse config resource: " + this.configLocations, ex);
			} finally {
				ErrorContext.instance().reset();
			}
		}

		if (this.transactionFactory == null) {
			this.transactionFactory = new SpringManagedTransactionFactory();
		}

		Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
		configuration.setEnvironment(environment);

		if (this.databaseIdProvider != null) {
			try {
				configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
			} catch (SQLException e) {
				throw new NestedIOException("Failed getting a databaseId", e);
			}
		}

		if (!isEmpty(this.mapperLocations)) {
			for (Resource mapperLocation : this.mapperLocations) {
				if (mapperLocation == null) {
					continue;
				}

				try {
					XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
							mapperLocation.getInputStream(), configuration,
							mapperLocation.toString(),
							configuration.getSqlFragments());
					xmlMapperBuilder.parse();
				} catch (Exception e) {
					throw new NestedIOException(
							"Failed to parse mapping resource: '" + mapperLocation + "'", e);
				} finally {
					ErrorContext.instance().reset();
				}

				if (logger.isDebugEnabled()) {
					logger.debug("Parsed mapper file: '" + mapperLocation + "'");
				}
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
			}
		}
		return this.sqlSessionFactoryBuilder.build(configuration);
	}

	public void afterPropertiesSet() throws Exception {
		notNull(dataSource, "Property 'dataSource' is required");
		notNull(sqlSessionFactoryBuilder,"Property 'sqlSessionFactoryBuilder' is required");
		this.sqlSessionFactory = buildSqlSessionFactory();
	}

	public SqlSessionFactory getObject() throws Exception {
		if (this.sqlSessionFactory == null) {
			afterPropertiesSet();
		}
		return this.sqlSessionFactory;
	}
	
	/*
	 * public Class<? extends SqlSessionFactory> getObjectType() { return
	 * this.sqlSessionFactory == null ? SqlSessionFactory.class :
	 * this.sqlSessionFactory.getClass(); }
	 * 
	 * public void onApplicationEvent(ApplicationEvent event) { if (failFast &&
	 * event instanceof ContextRefreshedEvent) { // fail-fast -> check all
	 * statements are completed
	 * this.sqlSessionFactory.getConfiguration().getMappedStatementNames(); } }
	 */
	public SqlSessionFactoryBuilder getSqlSessionFactoryBuilder() {
		return sqlSessionFactoryBuilder;
	}

	public void setSqlSessionFactoryBuilder(
			SqlSessionFactoryBuilder sqlSessionFactoryBuilder) {
		this.sqlSessionFactoryBuilder = sqlSessionFactoryBuilder;
	}

	public void setPlugins(Interceptor[] plugins) {
		this.plugins = plugins;
	}

	public void setTypeAliases(Class<?>[] typeAliases) {
		this.typeAliases = typeAliases;
	}

	public void setTypeAliasesPackage(String typeAliasesPackage) {
		this.typeAliasesPackage = typeAliasesPackage;
	}

	public void setTypeHandlers(TypeHandler<?>[] typeHandlers) {
		this.typeHandlers = typeHandlers;
	}

	public void setTypeHandlersPackage(String typeHandlersPackage) {
		this.typeHandlersPackage = typeHandlersPackage;
	}

	public void setTransactionFactory(TransactionFactory transactionFactory) {
		this.transactionFactory = transactionFactory;
	}

	public void setConfigurationProperties(Properties configurationProperties) {
		this.configurationProperties = configurationProperties;
	}

	public void setMapperLocations(Resource[] mapperLocations) {
		this.mapperLocations = mapperLocations;
	}

	public void setDataSource(DataSource dataSource) {
		if (dataSource instanceof TransactionAwareDataSourceProxy) {
			this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
		} else {
			this.dataSource = dataSource;
		}
	}
}

 

<think>好的,我现在需要回答用户关于MyBatis中`@MapperScan`注解的`factoryBean()`属性的用法和配置的问题。首先,我得回忆一下MyBatis-Spring整合的相关知识,特别是`@MapperScan`的作用。 用户提到的`factoryBean()`是`@MapperScan`的一个属性,用来指定生成Mapper接口代理的工厂Bean。根据引用[2],`doScan`方法扫描基础包并处理Bean定义,这里可能涉及到`MapperScannerConfigurer`,它负责扫描Mapper接口。而`factoryBean`属性可能用于自定义生成这些Bean的工厂。 接着,引用[3]提到`SqlSessionFactoryBean`会创建自己的环境配置,这可能和`factoryBean`有关联。当用户需要自定义Mapper的创建过程时,比如使用特定的SqlSessionFactory,就需要设置这个属性。例如,如果有多个数据源,可能需要不同的工厂Bean来生成对应的Mapper。 然后,引用[4]指出Spring中的Bean可能被代理,因此直接获取类可能不一致。这说明自定义工厂Bean可能需要处理代理逻辑,确保生成的Mapper接口正确注入。 最后,引用[5]提到`XMLConfigBuilder`解析配置,可能和工厂Bean的配置方式有关。用户可能在XMLJava配置中需要指定`factoryBean`,但通常`@MapperScan`是在Java配置中使用。 总结来说,`factoryBean()`用于指定自定义的工厂Bean类,以控制Mapper接口的实例化过程。配置时需要将该属性设置为自定义的`MapperFactoryBean`类,并确保Spring容器能正确初始化。可能需要结合`SqlSessionFactory`或数据源配置来使用。</think>关于`@MapperScan`注解的`factoryBean`属性配置,主要用于指定自定义的Mapper接口代理生成工厂。以下是具体用法说明: ### 一、`factoryBean()`属性作用 该属性用于指定生成Mapper接口实例的工厂Bean类型,默认值为`MapperFactoryBean.class`。通过修改此属性可以实现: 1. 自定义Mapper代理类的生成逻辑 2. 支持多数据源场景下的工厂切换 3. 集成特殊的事务管理需求 ### 二、基础配置示例 ```java @Configuration @MapperScan( basePackages = "com.example.mapper", factoryBean = CustomMapperFactoryBean.class ) public class MyBatisConfig { // 数据源和SqlSessionFactory配置 } ``` ### 三、自定义工厂实现 需继承`MapperFactoryBean`并重写相关方法: ```java public class CustomMapperFactoryBean<T> extends MapperFactoryBean<T> { @Override protected void checkDaoConfig() { super.checkDaoConfig(); // 添加自定义初始化逻辑 } @Override public T getObject() throws Exception { // 自定义代理生成逻辑 return super.getObject(); } } ``` ### 四、多数据源场景应用 当存在多个`SqlSessionFactory`时,可配合`sqlSessionFactoryRef`使用: ```java @MapperScan( basePackages = "com.example.db1.mapper", factoryBean = Db1MapperFactoryBean.class, sqlSessionFactoryRef = "db1SqlSessionFactory" ) @MapperScan( basePackages = "com.example.db2.mapper", factoryBean = Db2MapperFactoryBean.class, sqlSessionFactoryRef = "db2SqlSessionFactory" ) ``` ### 五、注意事项 1. 自定义工厂类必须提供公共无参构造器 2. 需要正确关联`SqlSessionFactory`(如引用[3]所述) 3. 生成的Bean可能被AOP代理(如引用[4]所述) 4. 建议保持与`@Configuration`类相同的包扫描路径(如引用[2]中的`doScan`机制)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值