Dans l'article précédent "Introduction approfondie à la série Mybatis (2)---Introduction à la configuration (code source mybatis)", grâce à une simple analyse du code source mybatis, nous pouvons voir que Dans le Fichier de configuration mybatis, dans le nœud racine de configuration ci-dessous, vous pouvez configurer les propriétés, typeAliases, plugins, objectFactory, objectWrapperFactory, paramètres, environnements, databaseIdProvider, typeHandlers et nœuds de mappeurs. Cette fois, nous allons donc d'abord introduire le nœud de propriétés et le nœud d'environnements.
Afin de permettre à chacun de mieux lire le code source de mybatis, je vais vous donner un exemple simple d'utilisation des propriétés.
1 <configuration> 2 <!-- 方法一: 从外部指定properties配置文件, 除了使用resource属性指定外,还可通过url属性指定url 3 <properties resource="dbConfig.properties"></properties> 4 --> 5 <!-- 方法二: 直接配置为xml --> 6 <properties> 7 <property name="driver" value="com.mysql.jdbc.Driver"/> 8 <property name="url" value="jdbc:mysql://localhost:3306/test1"/> 9 <property name="username" value="root"/> 10 <property name="password" value="root"/> 11 </properties>
Donc, si j'utilise les deux méthodes en même temps, quelle méthode est prioritaire ?
Lorsque les deux méthodes ci-dessus ont une configuration XML, la priorité est donnée et la configuration des propriétés spécifiées en externe est la deuxième. Quant à pourquoi, cela sera mentionné dans la prochaine analyse du code source, veuillez y prêter attention.
Voyons comment utiliser le nœud d'élément d'environnement :
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 如果上面没有指定数据库配置的properties文件,那么此处可以这样直接配置 <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test1"/> <property name="username" value="root"/> <property name="password" value="root"/> --> <!-- 上面指定了数据库配置文件, 配置文件里面也是对应的这四个属性 --> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <!-- 我再指定一个environment --> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- 与上面的url不一样 --> <property name="url" value="jdbc:mysql://localhost:3306/demo"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments>
Le nœud d'élément d'environnement peut être configuré avec plusieurs environnements sous-nœuds. Comment? Comprenez-vous?
Si l'environnement de développement de notre système est différent de la base de données utilisée dans l'environnement formel (c'est sûr), alors vous pouvez configurer deux environnements, et les deux identifiants correspondent à l'environnement de développement (dev). et l'environnement formel (final), alors vous pouvez sélectionner l'environnement correspondant en configurant l'attribut par défaut des environnements. Par exemple, si je configure la valeur de l'attribut par défaut des environnements sur dev, alors l'environnement de. dev sera sélectionné. Quant à la manière dont cela est implémenté, le code source sera discuté ci-dessous.
D'accord, je vais vous donner une brève introduction à la configuration des propriétés et des environnements. Ensuite, commençons officiellement à regarder le code source :
La dernière fois, nous avons dit que mybatis. utilise XMLConfigBuilder. La classe analyse le fichier de configuration mybatis, nous allons donc cette fois examiner l'analyse des propriétés et des environnements par XMLConfigBuilder :
XMLConfigBuilder :
1 public class XMLConfigBuilder extends BaseBuilder { 2 3 private boolean parsed; 4 //xml解析器 5 private XPathParser parser; 6 private String environment; 7 8 //上次说到这个方法是在解析mybatis配置文件中能配置的元素节点 9 //今天首先要看的就是properties节点和environments节点 10 private void parseConfiguration(XNode root) { 11 try { 12 //解析properties元素 13 propertiesElement(root.evalNode("properties")); //issue #117 read properties first 14 typeAliasesElement(root.evalNode("typeAliases")); 15 pluginElement(root.evalNode("plugins")); 16 objectFactoryElement(root.evalNode("objectFactory")); 17 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 18 settingsElement(root.evalNode("settings")); 19 //解析environments元素 20 environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 21 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 22 typeHandlerElement(root.evalNode("typeHandlers")); 23 mapperElement(root.evalNode("mappers")); 24 } catch (Exception e) { 25 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 26 } 27 } 28 29 30 //下面就看看解析properties的具体方法 31 private void propertiesElement(XNode context) throws Exception { 32 if (context != null) { 33 //将子节点的 name 以及value属性set进properties对象 34 //这儿可以注意一下顺序,xml配置优先, 外部指定properties配置其次 35 Properties defaults = context.getChildrenAsProperties(); 36 //获取properties节点上 resource属性的值 37 String resource = context.getStringAttribute("resource"); 38 //获取properties节点上 url属性的值, resource和url不能同时配置 39 String url = context.getStringAttribute("url"); 40 if (resource != null && url != null) { 41 throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); 42 } 43 //把解析出的properties文件set进Properties对象 44 if (resource != null) { 45 defaults.putAll(Resources.getResourceAsProperties(resource)); 46 } else if (url != null) { 47 defaults.putAll(Resources.getUrlAsProperties(url)); 48 } 49 //将configuration对象中已配置的Properties属性与刚刚解析的融合 50 //configuration这个对象会装载所解析mybatis配置文件的所有节点元素,以后也会频频提到这个对象 51 //既然configuration对象用有一系列的get/set方法, 那是否就标志着我们可以使用java代码直接配置? 52 //答案是肯定的, 不过使用配置文件进行配置,优势不言而喻 53 Properties vars = configuration.getVariables(); 54 if (vars != null) { 55 defaults.putAll(vars); 56 } 57 //把装有解析配置propertis对象set进解析器, 因为后面可能会用到 58 parser.setVariables(defaults); 59 //set进configuration对象 60 configuration.setVariables(defaults); 61 } 62 } 63 64 //下面再看看解析enviroments元素节点的方法 65 private void environmentsElement(XNode context) throws Exception { 66 if (context != null) { 67 if (environment == null) { 68 //解析environments节点的default属性的值 69 //例如: <environments default="development"> 70 environment = context.getStringAttribute("default"); 71 } 72 //递归解析environments子节点 73 for (XNode child : context.getChildren()) { 74 //<environment id="development">, 只有enviroment节点有id属性,那么这个属性有何作用? 75 //environments 节点下可以拥有多个 environment子节点 76 //类似于这样: <environments default="development"><environment id="development">...</environment><environment id="test">...</environments> 77 //意思就是我们可以对应多个环境,比如开发环境,测试环境等, 由environments的default属性去选择对应的enviroment 78 String id = child.getStringAttribute("id"); 79 //isSpecial就是根据由environments的default属性去选择对应的enviroment 80 if (isSpecifiedEnvironment(id)) { 81 //事务, mybatis有两种:JDBC 和 MANAGED, 配置为JDBC则直接使用JDBC的事务,配置为MANAGED则是将事务托管给容器, 82 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); 83 //enviroment节点下面就是dataSource节点了,解析dataSource节点(下面会贴出解析dataSource的具体方法) 84 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); 85 DataSource dataSource = dsFactory.getDataSource(); 86 Environment.Builder environmentBuilder = new Environment.Builder(id) 87 .transactionFactory(txFactory) 88 .dataSource(dataSource); 89 //老规矩,会将dataSource设置进configuration对象 90 configuration.setEnvironment(environmentBuilder.build()); 91 } 92 } 93 } 94 } 95 96 //下面看看dataSource的解析方法 97 private DataSourceFactory dataSourceElement(XNode context) throws Exception { 98 if (context != null) { 99 //dataSource的连接池 100 String type = context.getStringAttribute("type"); 101 //子节点 name, value属性set进一个properties对象 102 Properties props = context.getChildrenAsProperties(); 103 //创建dataSourceFactory 104 DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); 105 factory.setProperties(props); 106 return factory; 107 } 108 throw new BuilderException("Environment declaration requires a DataSourceFactory."); 109 } 110 }
/** * 这个类解析${}这种形式的表达式 */public class PropertyParser { public static String parse(String string, Properties variables) { VariableTokenHandler handler = new VariableTokenHandler(variables); GenericTokenParser parser = new GenericTokenParser("${", "}", handler); return parser.parse(string); } private static class VariableTokenHandler implements TokenHandler { private Properties variables; public VariableTokenHandler(Properties variables) { this.variables = variables; } public String handleToken(String content) { if (variables != null && variables.containsKey(content)) { return variables.getProperty(content); } return "${" + content + "}"; } } }