前言
学习源码的目的很多,但是最不希望的原因是为了面试,只为在简历上写上一行,看过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;}@Overridepublic SqlSession openSession() {// 实例化SqlSessionreturn openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}}
建造者模式
介绍:属于创建型设计模式,将一个复杂对象的构建与它的表示分离
如果说Factory是工厂模式的特征,那么Builder则是建造者模式的行走江湖的标签,MyBatis中XMLConfigBuilder就使用建造者模式来构建复杂的Configuration对象
// SqlSessionFactoryBuilder类// 省略部分代码...XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);// 调用 #1return build(parser.parse());// 省略部分代码...
// XMLConfigBuilder 类 #1public Configuration parse() {if (parsed) {throw new BuilderException("Each MyXMLConfigBuilder can only be used once.");}parsed = true;// 调用 #2parseConfiguration(parser.evalNode("/configuration"));return configuration;}
// XMLConfigBuilder 类 #2private void parseConfiguration(XNode root) {try {// 从这里开始,这些方法都在XMLConfigBuilder类中,用来构建Configuration对象// 例如调用 #3propertiesElement(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);}}// #3private 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*/@Overridepublic 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的的使用,代理模式也能实现;
上述几个模式平常开发中出了建造者和单例会用到,其它两个暂时还没有什么场景要用到。但不要觉得没用,正如本文开篇讲的那样,设计模式只是解决某种问题的套路、手段。我们没有必要为了用而用,可以作为知识储备,这样下次遇到,就可以炫技啦~ : )
请你相信我所说的都是错的
