1.什么是建造者模式
1.1 概念定义
建造者模式(也被称为“生成器模式”)将一个复杂对象的构建过程与它的表示分离,从而使得同样的构建过程可以创建不同的表示。建造者模式将一个复杂对象的创建过程分成了 一步步简单的步骤,用户只需要了解复杂对象的类型和内容,而无须关注复杂对象的具体构造过程,帮助用户屏蔽掉了复杂对象内部的具体构建细节。
建造者与工厂的区别:
建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象。
创建对象的力度不同,建造者模式创建复杂的对象,有各种复杂的部件组成,工厂模式创建出来的对象都一样。
关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式中不仅要创建出这个对象,还要知道这个对象有哪些部件组成。
建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。
功能
场景:建造一个复杂的产品时需要很多组件,装配这些子组件有个步骤问题。实际开发中,我们所需要的对象构建时,也非常复杂,有很多步骤需要处理,但是这些步骤的顺序无关紧要。
建造者模式的本质:分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构建出复杂的对象。
这个模式适用于:创建对象需要很多步骤,但是步骤的顺序不一定固定的场景。
这里是将复杂对象的创建和使用进行了分离。
建造者有四个角色
1.产品:要创建的产品类对象。
2.建造者抽象 Builder:建造者的抽象类,规范产品对象的各个组成部分的建造,一般由子类实现具体的建造过程。
3.建造者ConcreteBuilder:具体的Builder类,根据不同的业务逻辑,具体完成各个组成部分的创建。
4.调用者Director:调用具体的建造者,来创建对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或者按某种顺序创建。
1.2 例子
由于实现了构建和装配的解耦,不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法,装配算法的解偶,实现了更好的复用。
实现方法:一个大实体中会有多个组件,为每个组件创建Bean,同时为这个大组件创建由小组件组成的Bean,之后创建两个接口文件,一个是用于描述创建大组件内各个小组件的Builder,另一个是描述将各个小组件组装成大组件的装配过程。之后根据不同情况分别实现这两个接口就行了。
例子:Tom老师的课里有个课程的例子,这里我们再造一个创建并组装一个自己的飞船的例子
public class AirShip {
private OrbitalModule orbitalmodule;
private Engine engine;
private EscapeTower escapeTower;
public OrbitalModule getOrbitalmodule() {
return orbitalmodule;
}
public void setOrbitalmodule(OrbitalModule orbitalmodule) {
this.orbitalmodule = orbitalmodule;
}
public Engine getEngine() {
return this.engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public EscapeTower getEscapeTower() {
return escapeTower;
}
public void setEscapeTower(EscapeTower escapeTower) {
this.escapeTower = escapeTower;
}
}
//分别定义上面大组件使用的各个小组件的Bean
class OrbitalModule {
private String name;
public OrbitalModule(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Engine {
private String name;
public Engine(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class EscapeTower {
private String name;
public EscapeTower(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
定义两个接口,一个用来统一创建各个小组件的Builder,另一个是将小组件组装成大组件。
public interface AirShipBuilder {
OrbitalModule addeOrbitalModule();
Engine addEngine();
EscapeTower addEscapteTower();
}
public interface AirShipDirector {
AirShip directorAirShip();
}
然后分别实现:
public class SxtAirShipBuilder implements AirShipBuilder {
public SxtAirShipBuilder() {
}
public OrbitalModule addOrbitalModule() {
return new OrbitalModule("qingchao轨道舱");
}
public Engine addEngine() {
return new Engine("qingchao发动机");
}
public EscapeTower addEscapteTower() {
return new EscapeTower("qingchao救生塔");
}
}
public class SxtAirShipDirector implements AirShipDirector {
private AirShipBuilder builder;
public SxtAirShipDirector(AirShipBuilder builder) {
this.builder = builder;
}
public AirShip directorAirShip() {
Engine engine = builder.createEngine();
OrbitalModule orbitalModule = builder.createOrbitalModule();
EscapeTower escapeTower = builder.createEscapteTower();
//装配成飞船
AirShip ship=new AirShip();
ship.setEngine(engine);
ship.setEscapeTower(escapeTower);
ship.setOrbitalmodule(orbitalModule);
return ship;
}
}
1.3 链式写法
上面的代码可以修改成如下格式的:
AirShipBuilder builder=new AirShip()
.addEngine(engine)
.addEscapeTower(escapeTower)
.addOrbitalmodule(orbitalModule).build();
此时需要修改代码:
public interface AirShipBuilder {
SxtAirShipBuilder addeOrbitalModule(String module);
SxtAirShipBuilder addEngine(String engine);
SxtAirShipBuilder addEscapteTower(String tower);
}
public class SxtAirShipBuilder implements AirShipBuilder {
private AirShip airShip;
public SxtAirShipBuilder() {
airShip=new AirShip();
}
public SxtAirShipBuilder addeOrbitalModule(String module) {
airShip.setOrbitalmodule(new OrbitalModule(module));
return this;
}
public SxtAirShipBuilder addEngine(String engine) {
airShip.setEngine( new Engine(engine));
return this;
}
public SxtAirShipBuilder addEscapteTower(String tower) {
airShip.setEscapeTower( new EscapeTower(tower));
return this;
}
public AirShip build() {
return this.airShip;
}
}
调用的时候:
public static void main(String[] args) {
AirShipBuilder builder= new SxtAirShipBuilder();
AirShip airShip= builder.addEngine("庆超 发动机").addeOrbitalModule("庆超 轨道舱").addEscapteTower(" 庆超 救生塔").build();
airShip.getEngine();
}
这种情况下director就不需要了。
1.4 建造者模式的优缺点
优点:封装性好,创建和使用分离.
扩展性好,建造类之间独立,一定程度上解耦。
缺点:
1.产生多余的Builder对象
2.产品内部发生变化,建造者都要修改,成本较大。
2.建造者模式在Mybatis读取配置文件中的应用
Mybatis开始工作的入口在哪里呢?
MyBatis 初始化的主要工作是加载井解析 mybatis-config.xml 配置文件、映射配置文件以及相关的注解信息。 MyBatis的初始化入口是 SqlSessionFactoryBuilder.build()方法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >>
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析XML,最终返回一个 DefaultSqlSessionFactory >>
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
SqlSessionFactoryBuilder.build()方法会创建 XMLConfigBuilder 对象来解析mybatis-config.xml 配置文件 ,而 XMLConfigBuilder 是继承自BaseBuilder抽象类BaseBuilder 的子类。
2.1 BaseBuilder
BaseBuilder和其子类结构如图所示:
正如前面所说, MyBatis 的初始化过程使用了建造者模式,这里的 BaseBuilder 抽象类就扮演着建造者接口的角色。
XMLConfigBuilder负责解析mybatis-config.xml文件
XMLMapperBuilder负责解析负责解析mapper映射配置文件,它继承了 BaseBuilder 抽象类,也是具体建造者的角色。
XMLStatementBuilder: 用于SQL节点的解析。这些 SQL节点不同于一般的节点,因此使用独立的方法来解析相关语句。
看一下抽象类BaseBuilder的定义:
public abstract class BaseBuilder {
// MyBatis 初始化过程的核心对象, MyBatis 中几乎全部的配置信息会保存到Configuration 对象中 。 Configuration 对象是在 //MyBatis 初始化过程中创建且是全局唯一的。
protected final Configuration configuration;
//用< typeAliases >标签定义别名,这些定义的别名都会记录在该TypeAliasRegistry 对象中
protected final TypeAliasRegistry typeAliasRegistry;
//使用<typeAliases>标签定义别名,这些定义的别名都会记录在该TypeAliasRegistry 对象中,完成指定数据库类型与 Java //类型的转换,这些 TypeHandler 都会记录在 TypeHandlerRegistry 中
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
2.2 XMLConfigBuilder
XMLConfigBuilder它扮演的是具体建造者的角色。XMLConfigBuilder 主要负责解析 mybatis-config.xml 配置文件.
XMLConfigBuilder.parse()方法是解析 mybatis-config.xml 配置文件的入口,它通过调用
XMLConfigBuilder.parseConfiguration()方法实现整个解析过程
public class XMLConfigBuilder extends BaseBuilder {
//标识是否已经解析过 mybatis-config.xml 配置丈件
private boolean parsed;
//用于解析 mybatis -config.xml 配置文件的 XPathParser 对象
private final XPathParser parser;
//标识<environment>配置的名称,默认读取<environment>标签的 default 属性
private String environment;
//ReflectorFactory 负责创建和缓存 Reflector 对象
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// XPathParser,dom 和 SAX 都有用到 >>
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// 对于全局配置文件各种标签的解析
propertiesElement(root.evalNode("properties"));
// 解析 settings 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 插件
pluginElement(root.evalNode("plugins"));
// 用于创建对象
objectFactoryElement(root.evalNode("objectFactory"));
// 用于对对象进行加工
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工具箱
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// settings 子标签赋值,默认值就是在这里提供的 >>
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 创建了数据源 >>
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析引用的Mapper映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
parseConfigurationO方法的代码还是比较整洁的,我们可以清楚地看到, XMLConfigBuilder将mybatis-config.xml 配置文件中每个节点的解析过程封装成了一个相应的方法。
2.3 XMLMapperBuilder
XMLMapperBuilder 负责解析映射配置文件,它继承了 BaseBuilder 抽 象类,也是具体建造者的角色 。 XMLMapperBuilder.parse()方法是解析映射文件的入口
public class XMLMapperBuilder extends BaseBuilder {
private final XPathParser parser;
private final MapperBuilderAssistant builderAssistant;
private final Map<String, XNode> sqlFragments;
private final String resource;
public void parse() {
// 总体上做了两件事情,对于语句的注册和接口的注册
if (!configuration.isResourceLoaded(resource)) {
// 1、具体增删改查标签的解析。
// 一个标签一个MappedStatement。
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 2、把namespace(接口类型)和工厂类绑定起来,放到一个map。
// 一个namespace 一个 MapperProxyFactory
bindMapperForNamespace();
}
//处理 configurationElement ()方法中解析失败的<resultMap>节点
parsePendingResultMaps();
//处理 configurationElement ()方法中解析失败的<cache-ref>节点
parsePendingCacheRefs();
//处理 configurationElement ()方法中解析失败的 SQL 语句节点
parsePendingStatements();
}
2.4 XMLStatementBuilder
MyBatis 使用 SqISource 接口表示映射文件或注解中定义的 SQL 语句,但它表示的 SQL 语
句是不能直接被数据库执行的,因为其中可能含有动态 SQL 语句相关的节点或是占位符等需要
解析的元素。 SqISource 接口的定义如下:
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
public class XMLStatementBuilder extends BaseBuilder {
private final MapperBuilderAssistant builderAssistant;
private final XNode context;
private final String requiredDatabaseId;
XMLStatementBuilder.parseStatementNode()方法是解析 SQL节点的入口函数,解析就不看了。
上面的接口BoundSql的实现类有:
这里对 SqlSource 接口 的各个实现做简单说明。DynamicSq!Source 负责处理动态 SQL 语句,
RawSqlSource 负责处理静态语句,两者最终都会将处理后的SQL语句封装成 StaticSqlSource返回。 DynamicSq!Source 与 StaticSq!Source 的主要区别是StaticSq!Source 中记录的 SQL 语句中可能含有 “?”占位符 , 但是可以直接提交给数据库执行。DynamicSqlSource 中封装的 SQL语句还需要进行一系列解析,才会最终形成数据库可执行的SQL语句。
从上面的分析我们可以看到解析xml是一个工作量巨大的工作,类型多,方式灵活,设置的内容也不一样,Mybatis通过三个Builder分别解析mybatis-config.xml,Mapper.xml和SQL,每个都是一个构造器。这三者构成了解析配置文件的骨架,具体实现太复杂了,以后再看。