前言
学习源码的目的很多,但是最不希望的原因是为了面试,只为在简历上写上一行,看过xxx源码。个人认为看源码可以学习到两点:
- 学习架构思想,看框架如何进行抽象,如何设计出优秀的代码,设计模式是怎样应用的
- 了解框架实现原理,以便我们在开发中更好的应用
设计模式,说通俗点就是前人在软件开发设计中遇到的一些问题,把解决方案给总结出来,名为xxx模式,也就是解决问题的一些套路。在我最开始看的是《大话设计模式》书中利用对话式写作方式,引出问题,然后用设计模式去解决问题,虽然通俗易懂,但是工作中一直没机会用到。再后来写的代码多了,复杂的业务场景见多了,有意无意的会用到一些诸如模板,责任链,建造者等模式。再再后来看《Head First设计模式》又清晰了很多,有恍然大悟的那种感觉,发现以前怎么写的那么烂!因此让一个没什么代码量的人看设计模式,估计可能体会不到设计模式的带来的好处。
MyBatis中的设计模式
在看过MyBatis源码后,如果让你设计一个ORM映射框架,估计你也可以,只是代码设计的没那么优雅,性能细节上没有它处理的好,所以让我们看看MyBatis如何使用设计模式让代码变的优雅。
简单工厂模式
介绍:属于创建型设计模式,通过一个工厂方法,根据传入的参数不同,实例化不同的对象
MyBatis中的SqlSessionFactory看名字就是知道是一个工厂模式,并没有复杂的逻辑,根据不同的Configuration我们实例化不同的SqlSession
/**
* 工厂接口
*/
public interface SqlSessionFactory {
SqlSession openSession();
}
/**
* 工厂实现类
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
/**
* 通过不同的configuration创建不同的SqlSession对象,构造器传参
* @param configuration
*/
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
// 实例化SqlSession
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
}
建造者模式
介绍:属于创建型设计模式,将一个复杂对象的构建与它的表示分离
如果说Factory是工厂模式的特征,那么Builder则是建造者模式的行走江湖的标签,MyBatis中XMLConfigBuilder就使用建造者模式来构建复杂的Configuration对象
// SqlSessionFactoryBuilder类
// 省略部分代码...
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 调用 #1
return build(parser.parse());
// 省略部分代码...
// XMLConfigBuilder 类 #1
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each MyXMLConfigBuilder can only be used once.");
}
parsed = true;
// 调用 #2
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// XMLConfigBuilder 类 #2
private void parseConfiguration(XNode root) {
try {
// 从这里开始,这些方法都在XMLConfigBuilder类中,用来构建Configuration对象
// 例如调用 #3
propertiesElement(root.evalNode("properties"));
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"));
settingsElement(settings);
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// root.evalNode("mappers") 拿到mappers标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
// #3
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
// 构建configuration其中的一个属性
configuration.setVariables(defaults);
}
}
单例模式
介绍:属于创建型设计模式,对于整个JVM该对象只提供一个实例
单例模式实现的方法很多,有饿汉式、懒汉式,据说也是面试中问的最多的,MyBatis中的VFS类(虚拟文件系统),是用来提供简单api用来访问资源,用的就是懒汉式。
public abstract class VFS {
// 省略部分代码...
public static VFS getInstance() {
return VFSHolder.INSTANCE;
}
/**
* 获取单例
*/
private static class VFSHolder {
static final VFS INSTANCE = createVFS();
@SuppressWarnings("unchecked")
static VFS createVFS() {
// 省略部分代码...
}
}
// 省略部分代码...
}
代理模式
介绍:属于结构型设计模式,通过一个代理类来代替原对象执行一些操作
代理模式又分为静态代理和动态代理,MyBatis中动态代理可以说是核心功能,是整个框架的的基石。代理模式一般以*Proxy结尾,MyBatis中有MapperProxy,利用了JDK的InvocationHandler接口来实现
/**
* 代理类
*/
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
// 被代理类
private final Class<T> mapperInterface;
/**
* @param mapperInterface 是被代理类
*/
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
/**
*
* @param proxy
* @param method 要代替执行的方法
* @param args 该方法的入参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行SQL操作
return mapperMethod.execute(sqlSession, args);
}
}
小结
总结下这几种使用场景:
- 简单工厂模式,当要频繁获取对象时,可以不用new,而是通过工厂来获得;
- 建造者模式,当有一个复杂的对象,通过构造函数已经不能满足了需求了,可以试试建造者模式;
- 单例模式,系统中你只想new一次,系统共公用一个实例时,就该想到单例模式;
- 代理模式,可以参考拦截器,AOP的的使用,代理模式也能实现;
上述几个模式平常开发中出了建造者和单例会用到,其它两个暂时还没有什么场景要用到。但不要觉得没用,正如本文开篇讲的那样,设计模式只是解决某种问题的套路、手段。我们没有必要为了用而用,可以作为知识储备,这样下次遇到,就可以炫技啦~ : )
请你相信我所说的都是错的