XML
概念
Xml可扩展的标记语言
XML与html区别
1、 Html的所有标记是固定的,不允许程序员新增,而xml语言可允许程序员任意的新增自己的标签,没有自己固定的标签;
2、 Html可以将数据和格式综合编写,然而xml语言则只能放入一定逻辑规则的数据;
Java 解析XML文档方案
应用程序 |
API |
Xml解析器 |
Xml文档 |
DOM和SAX 这两套API
应用程序 |
JAXP的接口与抽象类 |
Xerces的JAXP实现 |
Xerces的DOM或SAX解析器 |
查找解析器工厂类
入口方法:
public static DocumentBuilderFactory newInstance() {
return FactoryFinder.find(
/* The default property name according tothe JAXP spec */
DocumentBuilderFactory.class, // "javax.xml.parsers.DocumentBuilderFactory"
/* The fallback implementation class name*/
"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
}
第一次通过系统属性获取:
// Use thesystem property first
try {
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null) {
dPrint("found system property, value=" + systemProp);
return newInstance(type, systemProp, null, true);
}
}
catch (SecurityException se) {
if (debug) se.printStackTrace();
}
为什么要使用“AccessController.doPrivileged”,一探究竟...
String getSystemProperty(final String propName) {
return (String)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return System.getProperty(propName);
}
});
}
当我们的API去访问某些资源时,由于对资源访问做了保护处理,需要检查通过才可,使用此方法(AccessController.doPrivileged)可以忽略检查,能合理访问资源。具体资料说明:http://huangyunbin.iteye.com/blog/1942509。数据的最终来源于Properties类(继承hashmap)
第二次通过jaxp.properties获取:
String configFile = ss.getSystemProperty("java.home") + File.separator +
"lib" + File.separator + "jaxp.properties";
File f = new File(configFile);
firstTime = false;
if (ss.doesFileExist(f)) {
dPrint("Read properties file "+f);
cacheProps.load(ss.getFileInputStream(f));
}
将jaxp.properties文件中的键值对装载到cacheProps对象中。
第三次通过ServiceLoader获取:
通过查找META-INF/services目录中配置文件配置的服务
final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
final Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
return null;
}
如果按照以上三种方法都没找到对应的xml解析器,系统有默认的解析器
newInstance(type, fallbackClassName, null, true);
如果fallbackClassName的值以“com.sun.org.apache.xerces.internal”开头,fallbackClassName的默认值为com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
DOM具体解析API不再详细论述,后面将用两个案例来分别说明。第一个案例以跟读mybatis逆向工程框架来学习优秀的设计,第二个通过编写配置文件来自我重新开发demo。
解读mybatis逆向生成工程框架源码
所需核心的jar文件
mybatis-generator-core-1.3.2.jar
源码文件
mybatis-generator-core-1.3.2-sources.jar
1、传入xml解析的数据源
//指定 逆向工程配置文件
File configFile = new File("src/com/huawei/genrator/generatorConfig.xml");
ConfigurationParser cp = newConfigurationParser(warnings);
Configuration config =cp.parseConfiguration(configFile);//解析xml文件后,将配置数据保存在Configuration实例中
2、解析xml文件
parseErrors.clear();//清楚解析过程中错误脏数据
DocumentBuilderFactory factory =DocumentBuilderFactory.newInstance();//xml解析抽象工厂类
factory.setValidating(true);
try {
DocumentBuilder builder =factory.newDocumentBuilder();//实例化一个解析器
builder.setEntityResolver(newParserEntityResolver());
ParserErrorHandler handler = newParserErrorHandler(warnings,
parseErrors);
builder.setErrorHandler(handler);
Document document = null;
try {
document =builder.parse(inputSource);//调用解析方法,在内存中以document实例形式保存
} catch (SAXParseException e) {
throw newXMLParserException(parseErrors);
} catch (SAXException e) {
if (e.getException() == null) {
parseErrors.add(e.getMessage());
} else {
parseErrors.add(e.getException().getMessage());
}
}
if (parseErrors.size() > 0) {
throw newXMLParserException(parseErrors);
}
Configuration config;//所有的解析结果将被保存在这个实例中
Element rootNode = document.getDocumentElement();
DocumentType docType =document.getDoctype();
if (rootNode.getNodeType() ==Node.ELEMENT_NODE
&&docType.getPublicId().equals(
XmlConstants.IBATOR_CONFIG_PUBLIC_ID)) {
config =parseIbatorConfiguration(rootNode);
} else if (rootNode.getNodeType() ==Node.ELEMENT_NODE
&&docType.getPublicId().equals(
XmlConstants.MYBATIS_GENERATOR_CONFIG_PUBLIC_ID)) {
config =parseMyBatisGeneratorConfiguration(rootNode);//获取对应的解析xml实例
} else {
throw newXMLParserException(getString("RuntimeError.5")); //$NON-NLS-1$
}
if (parseErrors.size() > 0) {
throw new XMLParserException(parseErrors);
}
return config;
} catch (ParserConfigurationExceptione) {
parseErrors.add(e.getMessage());
throw newXMLParserException(parseErrors);
}
3、 通过xml文件的根节点,进行下一层节点的解析
Configuration configuration = new Configuration();
NodeList nodeList =rootNode.getChildNodes();//通过根元素获取其子元素,有效子元素为context节点
for(int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() !=Node.ELEMENT_NODE) {
continue;
}
if("properties".equals(childNode.getNodeName())) { //$NON-NLS-1$
parseProperties(configuration, childNode);
} else if ("classPathEntry".equals(childNode.getNodeName())){ //$NON-NLS-1$
parseClassPathEntry(configuration,childNode);
} else if("context".equals(childNode.getNodeName())) { //$NON-NLS-1$获取节点的名字,此案例中名字为context
parseContext(configuration, childNode);//解析context节点
}
}
return configuration;
4、 解析context节点
Properties attributes = parseAttributes(node);//解析context节点的属性,并保存到properties对象中
String defaultModelType = attributes.getProperty("defaultModelType");//获取defaultModelType对应的值
String targetRuntime =attributes.getProperty("targetRuntime"); //获取targetRuntime对应的值
String introspectedColumnImpl = attributes
.getProperty("introspectedColumnImpl"); //获取introspectedColumnImpl对应的值
String id = attributes.getProperty("id"); //获取id对应的值
ModelType mt = defaultModelType == null ? null :ModelType
.getModelType(defaultModelType);
Context context = new Context(mt);
context.setId(id);//将id值设置为context实例的ID属性
if (stringHasValue(introspectedColumnImpl)) {
context.setIntrospectedColumnImpl(introspectedColumnImpl);
}
if (stringHasValue(targetRuntime)) {
context.setTargetRuntime(targetRuntime);
}
configuration.addContext(context);//将context实例保存在configuration实例的list中
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}//解析context节点的子节点
if ("property".equals(childNode.getNodeName())){ //子节点名字为property
parseProperty(context, childNode);
} else if("plugin".equals(childNode.getNodeName())) { //子节点名字为plugin
parsePlugin(context, childNode);
} else if("commentGenerator".equals(childNode.getNodeName())) { //子节点名字为commentGenerator
parseCommentGenerator(context, childNode);
} else if ("jdbcConnection".equals(childNode.getNodeName())){ //子节点名字为jdbcConnection
parseJdbcConnection(context, childNode);
} else if("javaModelGenerator".equals(childNode.getNodeName())) { //子节点名字为javaModelGenerator
parseJavaModelGenerator(context, childNode);
} else if ("javaTypeResolver".equals(childNode.getNodeName())){ //子节点名字为javaTypeResolver
parseJavaTypeResolver(context, childNode);
} else if("sqlMapGenerator".equals(childNode.getNodeName())) { //子节点名字为sqlMapGenerator
parseSqlMapGenerator(context, childNode);
} else if("javaClientGenerator".equals(childNode.getNodeName())) { //子节点名字为javaClientGenerator
parseJavaClientGenerator(context, childNode);
} else if("table".equals(childNode.getNodeName())) { //$NON-NLS-1$
parseTable(context, childNode);
}
}
5、解析节点的属性
Properties attributes = new Properties();
NamedNodeMap nnm = node.getAttributes();//获取节点的属性,储存到NamedNodeMap实例中
for (int i = 0; i < nnm.getLength(); i++) {
Node attribute = nnm.item(i);//属性键值对解析为node属性
String value = parsePropertyTokens(attribute.getNodeValue());//对节点属性对应的值进行解析
attributes.put(attribute.getNodeName(), value);//将属性中对应的键值对转化为map对应的key-value
}
return attributes;
6、解析节点属性的值
final String OPEN = "${"; //$NON-NLS-1$如果属性对应的值以${和}开头和结尾,说明值配置在其他的properties文件中
final String CLOSE = "}"; //$NON-NLS-1$
String newString = string;
if (newString != null) {
int start = newString.indexOf(OPEN);
int end = newString.indexOf(CLOSE);
while (start > -1 && end > start) {
String prepend = newString.substring(0, start);
String append = newString.substring(end +CLOSE.length());
String propName = newString.substring(start +OPEN.length(),
end);
String propValue = properties.getProperty(propName);//获取系统中属性文件对应的值
if (propValue != null) {
newString = prepend + propValue + append;
}
start = newString.indexOf(OPEN, end);
end = newString.indexOf(CLOSE, end);
}
}
return newString;
7、解析commentGenerator节点
CommentGeneratorConfigurationcommentGeneratorConfiguration = new CommentGeneratorConfiguration();
context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);
Properties attributes = parseAttributes(node);//解析节点的属性
String type = attributes.getProperty("type");//$NON-NLS-1$
if (stringHasValue(type)) {
commentGeneratorConfiguration.setConfigurationType(type);
}
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //解析commentGenerator节点子节点
parseProperty(commentGeneratorConfiguration, childNode);
}
}
8、解析property节点
Properties attributes = parseAttributes(node);//解析节点
String name = attributes.getProperty("name");//获取name,不支持配置在其他的配置文件中
String value = attributes.getProperty("value");//获取value,不支持配置在其他的配置文件中
propertyHolder.addProperty(name, value);
9、解析jdbcConnection节点
JDBCConnectionConfiguration jdbcConnectionConfiguration =new JDBCConnectionConfiguration();
context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);
Properties attributes = parseAttributes(node);//解析jdbcConnection节点属性
String driverClass =attributes.getProperty("driverClass"); //获取driverClass属性值
String connectionURL = attributes.getProperty("connectionURL");//获取connectionURL属性值
String userId =attributes.getProperty("userId"); //获取userId属性值
String password =attributes.getProperty("password"); //获取password属性值
jdbcConnectionConfiguration.setDriverClass(driverClass);
jdbcConnectionConfiguration.setConnectionURL(connectionURL);
if (stringHasValue(userId)) {
jdbcConnectionConfiguration.setUserId(userId);
}
if (stringHasValue(password)) {
jdbcConnectionConfiguration.setPassword(password);
}
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //解析property子节点
parseProperty(jdbcConnectionConfiguration, childNode);
}
}
10、解析javaTypeResolver节点
JavaTypeResolverConfigurationjavaTypeResolverConfiguration = new JavaTypeResolverConfiguration();
context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);
Properties attributes = parseAttributes(node);//解析属性
String type = attributes.getProperty("type");//获取type属性的值
if (stringHasValue(type)) {
javaTypeResolverConfiguration.setConfigurationType(type);
}
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //解析子元素property
parseProperty(javaTypeResolverConfiguration, childNode);
}
}
}
11、解析javaModelGenerator节点
JavaModelGeneratorConfigurationjavaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
context
.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);
Properties attributes = parseAttributes(node);//解析属性
String targetPackage =attributes.getProperty("targetPackage"); //获取生成Java pojo的包名
String targetProject =attributes.getProperty("targetProject"); //获取生成Java pojo的根路径
javaModelGeneratorConfiguration.setTargetPackage(targetPackage);
javaModelGeneratorConfiguration.setTargetProject(targetProject);
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //$NON-NLS-1$
parseProperty(javaModelGeneratorConfiguration,childNode);//解析子节点property
}
}
12、解析sqlMapGenerator节点
SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration= new SqlMapGeneratorConfiguration();
context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);
Properties attributes = parseAttributes(node);
String targetPackage =attributes.getProperty("targetPackage"); //获取生成mapper文件的包名
String targetProject = attributes.getProperty("targetProject");//获取生成mapper文件的根路径
sqlMapGeneratorConfiguration.setTargetPackage(targetPackage);
sqlMapGeneratorConfiguration.setTargetProject(targetProject);
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //$NON-NLS-1$
parseProperty(sqlMapGeneratorConfiguration, childNode);//解析property子节点
}
}
13、解析javaClientGenerator节点
JavaClientGeneratorConfigurationjavaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);
Properties attributes = parseAttributes(node);//解析属性
String type = attributes.getProperty("type");//获取type属性
String targetPackage =attributes.getProperty("targetPackage"); //获取dao接口生成的包名
String targetProject = attributes.getProperty("targetProject");//获取dao接口生成的根目录
String implementationPackage = attributes
.getProperty("implementationPackage");
javaClientGeneratorConfiguration.setConfigurationType(type);
javaClientGeneratorConfiguration.setTargetPackage(targetPackage);
javaClientGeneratorConfiguration.setTargetProject(targetProject);
javaClientGeneratorConfiguration
.setImplementationPackage(implementationPackage);
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //解析property子节点
parseProperty(javaClientGeneratorConfiguration,childNode);
}
}
14、解析table节点
TableConfiguration tc = new TableConfiguration(context);
context.addTableConfiguration(tc);
Properties attributes = parseAttributes(node);
String catalog =attributes.getProperty("catalog"); //$NON-NLS-1$
String schema =attributes.getProperty("schema"); //$NON-NLS-1$
String tableName =attributes.getProperty("tableName"); //获取表名
String domainObjectName =attributes.getProperty("domainObjectName"); //$NON-NLS-1$
String alias = attributes.getProperty("alias");//$NON-NLS-1$
String enableInsert =attributes.getProperty("enableInsert"); //$NON-NLS-1$
String enableSelectByPrimaryKey = attributes
.getProperty("enableSelectByPrimaryKey");//$NON-NLS-1$
String enableSelectByExample = attributes
.getProperty("enableSelectByExample");//$NON-NLS-1$
String enableUpdateByPrimaryKey = attributes
.getProperty("enableUpdateByPrimaryKey");//$NON-NLS-1$
String enableDeleteByPrimaryKey = attributes
.getProperty("enableDeleteByPrimaryKey");//$NON-NLS-1$
String enableDeleteByExample = attributes
.getProperty("enableDeleteByExample");//$NON-NLS-1$
String enableCountByExample = attributes
.getProperty("enableCountByExample");//$NON-NLS-1$
String enableUpdateByExample = attributes
.getProperty("enableUpdateByExample");//$NON-NLS-1$
String selectByPrimaryKeyQueryId = attributes
.getProperty("selectByPrimaryKeyQueryId");//$NON-NLS-1$
String selectByExampleQueryId = attributes
.getProperty("selectByExampleQueryId");//$NON-NLS-1$
String modelType =attributes.getProperty("modelType"); //$NON-NLS-1$
String escapeWildcards =attributes.getProperty("escapeWildcards"); //$NON-NLS-1$
String delimitIdentifiers = attributes
.getProperty("delimitIdentifiers");//$NON-NLS-1$
String delimitAllColumns =attributes.getProperty("delimitAllColumns"); //$NON-NLS-1$
if (stringHasValue(catalog)) {
tc.setCatalog(catalog);
}
if (stringHasValue(schema)) {
tc.setSchema(schema);
}
if (stringHasValue(tableName)) {
tc.setTableName(tableName);
}
if (stringHasValue(domainObjectName)) {
tc.setDomainObjectName(domainObjectName);
}
if (stringHasValue(alias)) {
tc.setAlias(alias);
}
if (stringHasValue(enableInsert)) {
tc.setInsertStatementEnabled(isTrue(enableInsert));
}
if (stringHasValue(enableSelectByPrimaryKey)) {
tc.setSelectByPrimaryKeyStatementEnabled(
isTrue(enableSelectByPrimaryKey));
}
if (stringHasValue(enableSelectByExample)) {
tc.setSelectByExampleStatementEnabled(
isTrue(enableSelectByExample));
}
if (stringHasValue(enableUpdateByPrimaryKey)) {
tc.setUpdateByPrimaryKeyStatementEnabled(
isTrue(enableUpdateByPrimaryKey));
}
if (stringHasValue(enableDeleteByPrimaryKey)) {
tc.setDeleteByPrimaryKeyStatementEnabled(
isTrue(enableDeleteByPrimaryKey));
}
if (stringHasValue(enableDeleteByExample)) {
tc.setDeleteByExampleStatementEnabled(
isTrue(enableDeleteByExample));
}
if (stringHasValue(enableCountByExample)) {
tc.setCountByExampleStatementEnabled(
isTrue(enableCountByExample));
}
if (stringHasValue(enableUpdateByExample)) {
tc.setUpdateByExampleStatementEnabled(
isTrue(enableUpdateByExample));
}
if (stringHasValue(selectByPrimaryKeyQueryId)) {
tc.setSelectByPrimaryKeyQueryId(selectByPrimaryKeyQueryId);
}
if (stringHasValue(selectByExampleQueryId)) {
tc.setSelectByExampleQueryId(selectByExampleQueryId);
}
if (stringHasValue(modelType)) {
tc.setConfiguredModelType(modelType);
}
if (stringHasValue(escapeWildcards)) {
tc.setWildcardEscapingEnabled(isTrue(escapeWildcards));
}
if (stringHasValue(delimitIdentifiers)) {
tc.setDelimitIdentifiers(isTrue(delimitIdentifiers));
}
if (stringHasValue(delimitAllColumns)) {
tc.setAllColumnDelimitingEnabled(isTrue(delimitAllColumns));
}
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node childNode = nodeList.item(i);
if (childNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
if ("property".equals(childNode.getNodeName())){ //$NON-NLS-1$
parseProperty(tc, childNode);
} else if("columnOverride".equals(childNode.getNodeName())) { //$NON-NLS-1$
parseColumnOverride(tc, childNode);
} else if("ignoreColumn".equals(childNode.getNodeName())) { //$NON-NLS-1$
parseIgnoreColumn(tc, childNode);
} else if("generatedKey".equals(childNode.getNodeName())) { //$NON-NLS-1$
parseGeneratedKey(tc, childNode);
} else if("columnRenamingRule".equals(childNode.getNodeName())) {//$NON-NLS-1$
parseColumnRenamingRule(tc, childNode);
}
}
Xml中所有节点解析完毕后,存储得到config对象后,MyBatisGenerator对象调用generate()方法生成对应的*.Java和*mapper.xml文件,可以判断出后面所有的工作将是从config对象中获取所需数据。
解析表配置
introspectedTables = newArrayList<IntrospectedTable>();
JavaTypeResolver javaTypeResolver = ObjectFactory.createJavaTypeResolver(this,warnings);
Connection connection = null;
try {
callback.startTask(getString("Progress.0"));//链接数据库
connection =getConnection();//获取数据库链接
DatabaseIntrospectordatabaseIntrospector = new DatabaseIntrospector(
this, connection.getMetaData(), javaTypeResolver, warnings);
for(TableConfiguration tc : tableConfigurations) {
String tableName =composeFullyQualifiedTableName(tc.getCatalog(), tc.getSchema(), tc.getTableName(),'.');//获取完整表名
if(fullyQualifiedTableNames != null&& fullyQualifiedTableNames.size()> 0) {
if(!fullyQualifiedTableNames.contains(tableName)) {
continue;
}
}
if(!tc.areAnyStatementsEnabled()) {
warnings.add(getString("Warning.0",tableName)); //$NON-NLS-1$
continue;
}
callback.startTask(getString("Progress.1",tableName)); //解析表配置信息
List<IntrospectedTable>tables = databaseIntrospector.introspectTables(tc);//整理表信息(列信息)
if (tables !=null) {
introspectedTables.addAll(tables);
}
callback.checkCancel();
}
} finally {
closeConnection(connection);
}
1、 获取配置中的jdbc链接
Connection connection = ConnectionFactory.getInstance().getConnection(jdbcConnectionConfiguration);
获取链接的具体实现
Driver driver = getDriver(config);//获取jdbc驱动
Properties props = new Properties();//创建零时数据载体
if (stringHasValue(config.getUserId())) {
props.setProperty("user",config.getUserId()); //存放数据源用户名
}
if (stringHasValue(config.getPassword())) {
props.setProperty("password",config.getPassword()); //存放数据源密码
}
props.putAll(config.getProperties());
Connection conn =driver.connect(config.getConnectionURL(), props);//根据url和登录验证获取数据库链接
if (conn == null) {
throw newSQLException(getString("RuntimeError.7")); //$NON-NLS-1$
}
return conn;
2、 加载jdbc驱动
String driverClass =connectionInformation.getDriverClass();
Driver driver;
try {
Class<?>clazz = ObjectFactory.externalClassForName(driverClass);
driver =(Driver) clazz.newInstance();
} catch(Exception e) {
throw newRuntimeException(getString("RuntimeError.8"), e); //$NON-NLS-1$
}
returndriver;
3、 遍历表配置
// get the raw columns from the DB
Map<ActualTableName,List<IntrospectedColumn>> columns = getColumns(tc);//获取表的列数据
if(columns.isEmpty()) {
warnings.add(getString("Warning.19",tc.getCatalog(), //$NON-NLS-1$
tc.getSchema(),tc.getTableName()));
return null;
}
removeIgnoredColumns(tc,columns);//去掉可忽略的列信息
calculateExtraColumnInformation(tc,columns);//确定列信息的Java类型
applyColumnOverrides(tc,columns);//重写列信息
calculateIdentityColumns(tc,columns);
List<IntrospectedTable> introspectedTables= calculateIntrospectedTables(tc, columns);//解析表信息
// nowintrospectedTables has all the columns from all the
// tables in theconfiguration. Do some validation...
Iterator<IntrospectedTable>iter = introspectedTables.iterator();
while(iter.hasNext()) {
IntrospectedTable introspectedTable =iter.next();
if (!introspectedTable.hasAnyColumns()) {
// addwarning that the table has no columns, remove from the
// list
Stringwarning = getString("Warning.1",introspectedTable.getFullyQualifiedTable().toString()); //$NON-NLS-1$
warnings.add(warning);
iter.remove();
} else if(!introspectedTable.hasPrimaryKeyColumns()&&!introspectedTable.hasBaseColumns()) {
// addwarning that the table has only BLOB columns, remove from
// the list
Stringwarning = getString("Warning.18",introspectedTable.getFullyQualifiedTable().toString()); //$NON-NLS-1$
warnings.add(warning);
iter.remove();
} else {
// nowmake sure that all columns called out in the
//configuration
//actually exist
reportIntrospectionWarnings(introspectedTable,tc, introspectedTable.getFullyQualifiedTable());
}
}
return introspectedTables;
4、 去除可忽略的列信息
for (Map.Entry<ActualTableName,List<IntrospectedColumn>> entry : columns.entrySet()) {
Iterator<IntrospectedColumn>tableColumns = (entry.getValue()).iterator();
while(tableColumns.hasNext()) {
IntrospectedColumnintrospectedColumn = tableColumns.next();
if(tc.isColumnIgnored(introspectedColumn.getActualColumnName())) {//根据ignoredColumns判断列信息是否有效
tableColumns.remove();
if(logger.isDebugEnabled()) {
logger.debug(getString("Tracing.3", //$NON-NLS-1$
introspectedColumn.getActualColumnName(),entry.getKey().toString()));
}
}
}
}
5、 解析表信息
Map<ActualTableName,List<IntrospectedColumn>> columns) {
booleandelimitIdentifiers = tc.isDelimitIdentifiers()
||stringContainsSpace(tc.getCatalog())
||stringContainsSpace(tc.getSchema())
||stringContainsSpace(tc.getTableName());
List<IntrospectedTable> answer = newArrayList<IntrospectedTable>();
for (Map.Entry<ActualTableName,List<IntrospectedColumn>> entry : columns.entrySet()) {
ActualTableName atn = entry.getKey();
FullyQualifiedTable table = newFullyQualifiedTable(stringHasValue(tc.getCatalog()) ? atn
.getCatalog() :null,stringHasValue(tc.getSchema()) ? atn.getSchema() : null,
atn.getTableName(),tc.getDomainObjectName(),tc.getAlias(),
isTrue(tc.getProperty(PropertyRegistry.TABLE_IGNORE_QUALIFIERS_AT_RUNTIME)),
tc.getProperty(PropertyRegistry.TABLE_RUNTIME_CATALOG),
tc.getProperty(PropertyRegistry.TABLE_RUNTIME_SCHEMA),
tc.getProperty(PropertyRegistry.TABLE_RUNTIME_TABLE_NAME),
delimitIdentifiers, context);
IntrospectedTable introspectedTable =ObjectFactory.createIntrospectedTable(tc, table, context);//根据context具体配置的targetRuntime="MyBatis3"实例IntrospectedTable对象
for (IntrospectedColumn introspectedColumn :entry.getValue()) {
introspectedTable.addColumn(introspectedColumn);
}
calculatePrimaryKey(table, introspectedTable);//解析索引
answer.add(introspectedTable);
}
returnanswer;
生成文件
pluginAggregator = new PluginAggregator();//插件配置
for (PluginConfiguration pluginConfiguration :pluginConfigurations) {
Plugin plugin =ObjectFactory.createPlugin(this,pluginConfiguration);
if (plugin.validate(warnings)) {
pluginAggregator.addPlugin(plugin);
} else {
warnings.add(getString("Warning.24", //$NON-NLS-1$
pluginConfiguration.getConfigurationType(),id));
}
}
if (introspectedTables != null) {
for(IntrospectedTable introspectedTable : introspectedTables) {
callback.checkCancel();
introspectedTable.initialize();//初始化工作
introspectedTable.calculateGenerators(warnings, callback);//实例化JavaMapperGenerator,javaModelGenerators解析类型
generatedJavaFiles.addAll(introspectedTable .getGeneratedJavaFiles());
generatedXmlFiles.addAll(introspectedTable.getGeneratedXmlFiles());
generatedJavaFiles.addAll(pluginAggregator.contextGenerateAdditionalJavaFiles(introspectedTable));
generatedXmlFiles.addAll(pluginAggregator.contextGenerateAdditionalXmlFiles(introspectedTable));
}
}
generatedJavaFiles.addAll(pluginAggregator.contextGenerateAdditionalJavaFiles());
generatedXmlFiles.addAll(pluginAggregator.contextGenerateAdditionalXmlFiles());
1、 初始化工作
calculateJavaClientAttributes();//初始化接口、mapper文件存储路径信息
calculateModelAttributes();//初始化实体类存放路径信息
calculateXmlAttributes();//初始化mapper.xml文件的信息
if (tableConfiguration.getModelType() ==ModelType.HIERARCHICAL) {
rules = newHierarchicalModelRules(this);
} else if (tableConfiguration.getModelType() ==ModelType.FLAT) {
rules = newFlatModelRules(this);
} else {
rules = newConditionalModelRules(this);
}
context.getPlugins().initialized(this);
2、 生成Java文件GeneratedFile
List<GeneratedJavaFile> answer = newArrayList<GeneratedJavaFile>();
for (AbstractJavaGenerator javaGenerator :javaModelGenerators) {
List<CompilationUnit>compilationUnits = javaGenerator.getCompilationUnits();//获取实体类编译单元(源文件)
for(CompilationUnit compilationUnit : compilationUnits) {
GeneratedJavaFile gjf = newGeneratedJavaFile(compilationUnit, context.getJavaModelGeneratorConfiguration().getTargetProject(),
context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
context.getJavaFormatter());
answer.add(gjf);
}
}
for (AbstractJavaGenerator javaGenerator :clientGenerators) {
List<CompilationUnit>compilationUnits = javaGenerator.getCompilationUnits();
for(CompilationUnit compilationUnit : compilationUnits) {
GeneratedJavaFilegjf = new GeneratedJavaFile(compilationUnit,
context.getJavaClientGeneratorConfiguration().getTargetProject(),
context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
context.getJavaFormatter());
answer.add(gjf);
}
}
return answer;
3、 生成编译单元,由于类属性较多,仅举例说明(代码不完整)
向类中添加一个方法
TopLevelClass topLevelClass = new TopLevelClass(type);//实例顶层类
topLevelClass.setVisibility(JavaVisibility.PUBLIC);//类的访问权限设置为共有
commentGenerator.addJavaFileComment(topLevelClass);
Method method = new Method();//实例一个方法
method.setVisibility(JavaVisibility.PUBLIC);//方法权限为共有
method.setConstructor(true);//设置为构造器
method.setName(type.getShortName());//为方法设置名字
method.addBodyLine("oredCriteria= new ArrayList<Criteria>();"); //编写方法体
commentGenerator.addGeneralMethodComment(method,introspectedTable);
topLevelClass.addMethod(method);
向类中添加一个属性
Field field = newField();//实例一个类中属性
field.setVisibility(JavaVisibility.PROTECTED);//将该属性设置为受保护
field.setType(FullyQualifiedJavaType.getStringInstance());//设置属性类型为Java.lang.String
field.setName("orderByClause");//设置属性名字
commentGenerator.addFieldComment(field,introspectedTable);
topLevelClass.addField(field);//将属性添加到类
method = newMethod();//实例一个方法
method.setVisibility(JavaVisibility.PUBLIC);//设置方法访问权限为公有
method.setName("setOrderByClause");//方法名字为setOrderByClause
method.addParameter(newParameter(FullyQualifiedJavaType.getStringInstance(),"orderByClause")); //方法参数为orderByClause,类型为Java.lang.String
method.addBodyLine("this.orderByClause= orderByClause;"); //向方法体中添加代码
commentGenerator.addGeneralMethodComment(method,introspectedTable);
topLevelClass.addMethod(method);//将方法添加到类
method = newMethod();
method.setVisibility(JavaVisibility.PUBLIC);//设置为公有访问权限
method.setReturnType(FullyQualifiedJavaType.getStringInstance());//设置返回值类型为Java.lang.string
method.setName("getOrderByClause");//设置方法名为getOrderByClause
method.addBodyLine("returnorderByClause;"); //向方法体中添加代码
commentGenerator.addGeneralMethodComment(method,introspectedTable);
topLevelClass.addMethod(method);//将方法添加到类
4、 编译内部类
InnerClass answer = newInnerClass(FullyQualifiedJavaType.getGeneratedCriteriaInstance());//实例内部类
answer.setVisibility(JavaVisibility.PROTECTED);//设置为受保护的权限
answer.setStatic(true);//设置为静态属性
answer.setAbstract(true);//设置为抽象类
context.getCommentGenerator().addClassComment(answer,introspectedTable);
method = new Method();//添加一个方法
method.setVisibility(JavaVisibility.PROTECTED);//设置方法访问权限为受保护
method.setName("GeneratedCriteria"); //设置方法名字
method.setConstructor(true);//设置为构造方法
method.addBodyLine("super();"); //向方法体中添加代码
method.addBodyLine("criteria = newArrayList<Criterion>();"); //添加代码
answer.addMethod(method);
List<String> criteriaLists = newArrayList<String>();
criteriaLists.add("criteria");//$NON-NLS-1$
生成dao(mapper)接口的源码不再详细解析,最终将生成的Java文件保存在GeneratedJavaFile数据结构中,生成的mapper.xml文件保存在GeneratedXmlFile数据结构中。
5、 生成xml配置文件
List<GeneratedXmlFile> answer = newArrayList<GeneratedXmlFile>();
if (xmlMapperGenerator != null) {
Document document =xmlMapperGenerator.getDocument();
GeneratedXmlFile gxf = newGeneratedXmlFile(document,getMyBatis3XmlMapperFileName(),getMyBatis3XmlMapperPackage(),context.getSqlMapGeneratorConfiguration().getTargetProject(),
true,context.getXmlFormatter());
if(context.getPlugins().sqlMapGenerated(gxf, this)) {
answer.add(gxf);
}
}
return answer;
数据是根据stringbuffer拼接而成的
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\"encoding=\"UTF-8\" ?>"); //xml申明数据拼接
if (publicId != null && systemId != null) {
OutputUtilities.newLine(sb);
sb.append("<!DOCTYPE"); //$NON-NLS-1$
sb.append(rootElement.getName());
sb.append(" PUBLIC\""); //$NON-NLS-1$
sb.append(publicId);
sb.append("\"\""); //$NON-NLS-1$
sb.append(systemId);
sb.append("\">"); //$NON-NLS-1$
}
OutputUtilities.newLine(sb);//换行
sb.append(rootElement.getFormattedContent(0));//获取子节点下面的数据
return sb.toString();
6、 持久化mapper.xml和Java文件
持久化xml文件
for (GeneratedXmlFile gxf : generatedXmlFiles) {
projects.add(gxf.getTargetProject());
File targetFile;
String source;
try {
File directory= shellCallback.getDirectory(gxf.getTargetProject(), gxf.getTargetPackage());
targetFile =new File(directory, gxf.getFileName());
if(targetFile.exists()) {
if(gxf.isMergeable()) {
source= XmlFileMergerJaxp.getMergedSource(gxf,targetFile);
} else if(shellCallback.isOverwriteEnabled()) {
source= gxf.getFormattedContent();
warnings.add(getString("Warning.11",//$NON-NLS-1$
targetFile.getAbsolutePath()));
} else {
source = gxf.getFormattedContent();
targetFile= getUniqueFileName(directory, gxf.getFileName());
warnings.add(getString("Warning.2",targetFile.getAbsolutePath())); //$NON-NLS-1$
}
} else {
source= gxf.getFormattedContent();
}
} catch (ShellException e) {
warnings.add(e.getMessage());
continue;
}
callback.checkCancel();
callback.startTask(getString("Progress.15",targetFile.getName())); //$NON-NLS-1$
writeFile(targetFile,source, "UTF-8"); //$NON-NLS-1$
}
持久化java文件
for (GeneratedJavaFilegjf : generatedJavaFiles) {
projects.add(gjf.getTargetProject());
File targetFile;
String source;
try {//拼接出文件存放全路径
File directory = shellCallback.getDirectory(gjf.getTargetProject(),gjf.getTargetPackage());
targetFile = new File(directory, gjf.getFileName());
if (targetFile.exists()) {
if(shellCallback.isMergeSupported()) {
source =shellCallback.mergeJavaFile(gjf.getFormattedContent(),targetFile.getAbsolutePath(),
MergeConstants.OLD_ELEMENT_TAGS,gjf.getFileEncoding());
} else if(shellCallback.isOverwriteEnabled()) {
source =gjf.getFormattedContent();
warnings.add(getString("Warning.11",//$NON-NLS-1$
targetFile.getAbsolutePath()));
} else {
source =gjf.getFormattedContent();
targetFile =getUniqueFileName(directory, gjf.getFileName());
warnings.add(getString("Warning.2",targetFile.getAbsolutePath())); //$NON-NLS-1$
}
} else {
source = gjf.getFormattedContent();//对象字符串化,方便写入文件
}
callback.checkCancel();
callback.startTask(getString("Progress.15",targetFile.getName())); //$NON-NLS-1$
writeFile(targetFile, source, gjf.getFileEncoding());//将字符串内容写入文件
} catch(ShellException e) {
warnings.add(e.getMessage());
}
}
此处大家想必和我一样有一些疑惑:
1、 如果对象内容比较多,没有办法一次性转化为字符串怎么办?
2、 其中writeFile方法内部使用BufferedWriter的write方法,对写入的字符串长度是否有限制?
基于以上两个问题,我准备网上寻找答案,和大家一起解惑!!!
String对象的最大长度:看了String源码发现value其实就是一个char数组,String对象的长度及时char数组的长度,数组的长度而是一个整数,整数的最大值是2^31-1(2,147,483647),由此可见如此庞大的字符串已经够我们使用了。
框架中的writeFile()方法实现如下:
FileOutputStream fos = new FileOutputStream(file, false);//文件字节输出流
OutputStreamWriter osw;
if (fileEncoding == null) {
osw = newOutputStreamWriter(fos);//字节流转化为字符流
} else {
osw = newOutputStreamWriter(fos, fileEncoding);
}
BufferedWriter bw = new BufferedWriter(osw);//为了获得较高效率,将其转化为带缓冲的输出字符流中,以避免频繁调用缓冲器
bw.write(content);//我们的最大疑问就是此处如果字符串长度十分大,超过了缓冲区的最大长度怎么办?
bw.close();
下面我们需要再跟读BufferedWriter中write方法源码
public void write(String str, int off, int len) throwsIOException {
synchronized (lock) {
charcbuf[];
if (len<= WRITE_BUFFER_SIZE) {//如果字符串长度不大于1024
if(writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
cbuf = writeBuffer;
} else{ // Don't permanently allocate verylarge buffers.长度大于1024
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0);
write(cbuf, 0, len);
}
}
到此我们基本已经分析完毕,从xml配置文件的解析,通过解析的数据建立起与数据库的链接,获取对应的表信息,并按照配置根据系统支持的编译单元生成对应的Java和xml文件,最后持久化到硬盘。其中用到了dom解析,Java反射技术,数据库jdbc链接,文件io读写,线程安全等Java核心技术,其中线程安全框架出现较少,主要出现位置在io源码中。
框架中关键技术说明
n 通过一个类名,获取class对象
Class<?> clazz = null;
try {
ClassLoader cl =Thread.currentThread().getContextClassLoader();//获取当前线程的类加载器
clazz = Class.forName(type, true, cl);//通过类名和加载器生成对应的Class对象
} catch (Exception e) {
// ignore - failsafe below
}
if (clazz == null) {
clazz = Class.forName(type, true,ObjectFactory.class.getClassLoader());
}
return clazz;
n 通过class实例创建一个对应类的实例
Object answer;
try {
Class<?> clazz = internalClassForName(type);
answer = clazz.newInstance();//通过class对象实例一个对象
} catch (Exception e) {
throw new RuntimeException(getString(
"RuntimeError.6", type), e); //$NON-NLS-1$
}
return answer;
n 框架中循环整理
框架中由于有许多解析xml节点的编码,所以循环处理出现地方较多,此处单独用区别的方式说明一下它们。
传统for循环:for (int i = 0; i < tableConfigurations.size(); i++)
有循环体可看出传统循环依赖下标,多见于数组或则以数组实现的数据结构如list;
加强for循环:如for (TableConfiguration tc : tableConfigurations)
此种方法,不需要数组下标,不需要知道数组长度,写法十分简洁;
迭代器循环:Iterator it =list.iterator();
迭代器的实现具体根据数据结构底层的数据载体,list底层则是依赖数组,map则是依赖链式实现。
详细资料可参见:http://www.cnblogs.com/leskang/p/6031282.html
框架整体设计研读
异常设计
异常记录采用list记录字符串
日志设计
日志采用jdk自带的日志记录器和log4j,本框架默认使用log4j日志记录器
模式设计
1、工厂模式中的饱汉模式
Connection connection= ConnectionFactory.getInstance().getConnection(jdbcConnectionConfiguration);
private staticConnectionFactory instance = new ConnectionFactory();
public staticConnectionFactory getInstance() {
return instance;
}
类关系设计
n 将配置文件的解析存放到Configuration,可理解为对Context的一个包装类
n 将配置文件中的Context节点信息解析成Context对象,包装了jdbc链接配置信息、mapper.xml配置信息、数据库表对应Java实体和mapper接口配置等信息
n
资源国际化
private static finalString BUNDLE_NAME ="org.mybatis.generator.internal.util.messages.messages"; //指定资源文件路径
private static finalResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);//加载本地资源文件的对象
try {
return RESOURCE_BUNDLE.getString(key);//获取资源文件对应key的值
} catch(MissingResourceException e) {
return '!' + key + '!';
}
本框架中用到了静态引入的方式,以前自己没用过,也很少见别人这么使用,特在此说明
import staticorg.mybatis.generator.internal.util.messages.Messages.getString;
通过此种引用,可以在类中直接使用方法名而达到类名.静态方法的效果,具体细节可参考下面帖子http://blog.csdn.net/u012843873/article/details/51741675
l 加载资源绑定对象
if (locale == null || control == null) {
throw new NullPointerException();
}
CacheKey cacheKey = new CacheKey(baseName, locale,loader);//通过资源文件路径、本地国际化属性对象和类加载器创建一个唯一的缓存key
ResourceBundle bundle = null;
BundleReference bundleRef = cacheList.get(cacheKey);//在缓存数据载体中查找该缓存key是否已经加载到缓存中
if (bundleRef != null) {
bundle =bundleRef.get();
bundleRef =null;
}
if (isValidBundle(bundle) &&hasValidParentChain(bundle)) {
return bundle;
}
此处比较好奇,为什么需要有一个CacheKey对象,因为cacheList是一个map对象,CacheKey作为他的key存在,那么是否可以只使用baseName按照某种规则生成一个简略的key呢?我觉得是可行的,那么我们看看,框架如此设计的好处。
hashCodeCache =name.hashCode() << 3;
hashCodeCache ^=locale.hashCode();
ClassLoader loader =getLoader();
if (loader != null) {
hashCodeCache ^= loader.hashCode();
}
将资源名称和本地国际化对象类加载器分别计算hash值,然后在异或
说明:
1、 DOM解析XML文件,首先将xml文件以document实例的形式载入内存中;
2、 Configuration类,其中核心的数据为Context的list;
3、 Context类,解析多个配置后对应的Java pojo封装类,比如jdbc链接配置、mapper文件生成配置、Java pojo生成配置和数据表配置等;
4、 获取xml文件头遵循的dtd规则,案例中以此为准则“-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN”;
5、 获取节点的时候会有空白节点的出现,所有有效节点之间往往会有空白节点出现;
6、 结点属性内容保存在Properties实例中,此实例底层数据载体为map;
7、 Dom解析节点属性类NamedNodeMap;
8、 一个逆向xml配置文件对应一个Configuration实例,一个context节点对应一个Context实例
框架的缺点
1、 框架中硬编码出现大的位置较多,不好维护扩展;
2、 框架封装方法不够优雅,类定义的方法不太合理;
Mybatis逆向工程配置文件
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>//根元素
<context id="testTables"targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是: false:否 -->
<property name="suppressAllComments"value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码,这里配置的是mysql的,当然也可以配置oracle等数据库 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/test_wangjian"userId="root"
password="jane@123456">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL
和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals"value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.mybatis.entity"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages"value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings"value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.mybatis.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages"value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.mybatis.mapper"targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages"value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="goods"></table>
<table tableName="users"></table>
<!-- <table tableName="orderdetail"></table>
<tabletableName="t_user"></table> -->
</context>
</generatorConfiguration>