mybatis源码解析之Configuration加载(四)
概述
上一篇文章,我们主要讲了datasource的相关内容,那么<environments>标签下的内容就看的差不多了,今天就来看一下在拿到transationManager和datasource之后,mybatis又做了什么事情呢?
Environment类
我们先来看下解析<environments>标签的那段代码:
1 private void environmentsElement(XNode context) throws Exception { 2 if (context != null) { 3 if (environment == null) { 4 environment = context.getStringAttribute("default"); 5 } 6 for (XNode child : context.getChildren()) { 7 String id = child.getStringAttribute("id"); 8 if (isSpecifiedEnvironment(id)) { 9 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); 10 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); 11 DataSource dataSource = dsFactory.getDataSource(); 12 Environment.Builder environmentBuilder = new Environment.Builder(id) 13 .transactionFactory(txFactory) 14 .dataSource(dataSource); 15 configuration.setEnvironment(environmentBuilder.build()); 16 } 17 } 18 } 19 }
我们看一下12-15行代码,12-14行主要就是创建了一个Environment.Builder类,将之前获取到的id,transationManager,datasource放进去,第15行代码,调用build方法获取environment对象,并将其放进configuation中。逻辑上很简单,但是写法上有点奇怪,一下子还不怎么看得懂,连续点了这么多的方法,我们来研究下这么写的好处?
首先我们来看下Environment类的内容:
1 public final class Environment { 2 private final String id; 3 private final TransactionFactory transactionFactory; 4 private final DataSource dataSource; 5 6 public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) { 7 if (id == null) { 8 throw new IllegalArgumentException("Parameter 'id' must not be null"); 9 } 10 if (transactionFactory == null) { 11 throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null"); 12 } 13 this.id = id; 14 if (dataSource == null) { 15 throw new IllegalArgumentException("Parameter 'dataSource' must not be null"); 16 } 17 this.transactionFactory = transactionFactory; 18 this.dataSource = dataSource; 19 } 20 21 public static class Builder { 22 private String id; 23 private TransactionFactory transactionFactory; 24 private DataSource dataSource; 25 26 public Builder(String id) { 27 this.id = id; 28 } 29 30 public Builder transactionFactory(TransactionFactory transactionFactory) { 31 this.transactionFactory = transactionFactory; 32 return this; 33 } 34 35 public Builder dataSource(DataSource dataSource) { 36 this.dataSource = dataSource; 37 return this; 38 } 39 40 public String id() { 41 return this.id; 42 } 43 44 public Environment build() { 45 return new Environment(this.id, this.transactionFactory, this.dataSource); 46 } 47 48 } 49 50 public String getId() { 51 return this.id; 52 } 53 54 public TransactionFactory getTransactionFactory() { 55 return this.transactionFactory; 56 } 57 58 public DataSource getDataSource() { 59 return this.dataSource; 60 } 61 62 }
看第1行代码,这个类被final修饰,这个跟String等不可变类有点像,一旦创建了,就不能再改变了。
这个类有三个类变量,分别是 id,transationfactory,datasource,分别对应于配置文件中的三个配置项(id、transactionManager、DataSource),这几个我们之前已经分析过了,这个几个字段也分别被final修饰,一旦设置,就不能再修改,而且针对这几个字段,类里面只提供了get方法,没有set方法(反正也不能修改),只能够去获取配置设定的值,而不能修改。
在源码里面,我们看到这个类里面有一个静态内部类,Builder,内容如下:
1 public static class Builder { 2 private String id; 3 private TransactionFactory transactionFactory; 4 private DataSource dataSource; 5 6 public Builder(String id) { 7 this.id = id; 8 } 9 10 public Builder transactionFactory(TransactionFactory transactionFactory) { 11 this.transactionFactory = transactionFactory; 12 return this; 13 } 14 15 public Builder dataSource(DataSource dataSource) { 16 this.dataSource = dataSource; 17 return this; 18 } 19 20 public String id() { 21 return this.id; 22 } 23 24 public Environment build() { 25 return new Environment(this.id, this.transactionFactory, this.dataSource); 26 } 27 28 }
这种写法熟悉设计模式的朋友肯定一眼就看出来了,这里使用了构造者模式,将创建Environment 对象的过程分了好几个步骤,先设置id,再设置transactionFactory,接着是dataSource,最后可以调用build方法,返回Environment对象。为什么要用这种写法呢?
我们先来回顾下什么建造者模式:
构建者模式一般用于构建复杂对象时,将复杂对象分割成许多小对象进行分别构建,然后整合在一起形成一个大对象,这样做能很好的规范对象构建的细节过程,这里也是一样的目的,虽然说Environment类的字段较少,但在MyBatis中大量使用构建者模式的基础上,在此处使用构建者模式也无可厚非,而且通过内部类的方式构建,这个Environment对象的创建会在内部类构建方法build()被显式调用时才会在内存中创建,实现了懒加载。这又有点单例模式的意思在内,虽然Mybatis中可创建多个Environment环境,但是在正式运行时,只会存在一个环境,确实是使用内部类实现了懒加载的单例模式。
好了,到这里关于<environments>标签的分析就结束了,后面我们分析下mappers的解析过程。