前言

学习源码的目的很多,但是最不希望的原因是为了面试,只为在简历上写上一行,看过xxx源码。个人认为看源码可以学习到两点:

  • 学习架构思想,看框架如何进行抽象,如何设计出优秀的代码,设计模式是怎样应用的
  • 了解框架实现原理,以便我们在开发中更好的应用

设计模式,说通俗点就是前人在软件开发设计中遇到的一些问题,把解决方案给总结出来,名为xxx模式,也就是解决问题的一些套路。在我最开始看的是《大话设计模式》书中利用对话式写作方式,引出问题,然后用设计模式去解决问题,虽然通俗易懂,但是工作中一直没机会用到。再后来写的代码多了,复杂的业务场景见多了,有意无意的会用到一些诸如模板,责任链,建造者等模式。再再后来看《Head First设计模式》又清晰了很多,有恍然大悟的那种感觉,发现以前怎么写的那么烂!因此让一个没什么代码量的人看设计模式,估计可能体会不到设计模式的带来的好处。

MyBatis中的设计模式

在看过MyBatis源码后,如果让你设计一个ORM映射框架,估计你也可以,只是代码设计的没那么优雅,性能细节上没有它处理的好,所以让我们看看MyBatis如何使用设计模式让代码变的优雅。

简单工厂模式

介绍属于创建型设计模式,通过一个工厂方法,根据传入的参数不同,实例化不同的对象

MyBatis中的SqlSessionFactory看名字就是知道是一个工厂模式,并没有复杂的逻辑,根据不同的Configuration我们实例化不同的SqlSession

  1. /**
  2. * 工厂接口
  3. */
  4. public interface SqlSessionFactory {
  5. SqlSession openSession();
  6. }
  1. /**
  2. * 工厂实现类
  3. */
  4. public class DefaultSqlSessionFactory implements SqlSessionFactory {
  5. private final Configuration configuration;
  6. /**
  7. * 通过不同的configuration创建不同的SqlSession对象,构造器传参
  8. * @param configuration
  9. */
  10. public DefaultSqlSessionFactory(Configuration configuration) {
  11. this.configuration = configuration;
  12. }
  13. @Override
  14. public SqlSession openSession() {
  15. // 实例化SqlSession
  16. return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  17. }
  18. }

建造者模式

介绍:属于创建型设计模式,将一个复杂对象的构建与它的表示分离

如果说Factory是工厂模式的特征,那么Builder则是建造者模式的行走江湖的标签,MyBatis中XMLConfigBuilder就使用建造者模式来构建复杂的Configuration对象

  1. // SqlSessionFactoryBuilder类
  2. // 省略部分代码...
  3. XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
  4. // 调用 #1
  5. return build(parser.parse());
  6. // 省略部分代码...
  1. // XMLConfigBuilder 类 #1
  2. public Configuration parse() {
  3. if (parsed) {
  4. throw new BuilderException("Each MyXMLConfigBuilder can only be used once.");
  5. }
  6. parsed = true;
  7. // 调用 #2
  8. parseConfiguration(parser.evalNode("/configuration"));
  9. return configuration;
  10. }
  1. // XMLConfigBuilder 类 #2
  2. private void parseConfiguration(XNode root) {
  3. try {
  4. // 从这里开始,这些方法都在XMLConfigBuilder类中,用来构建Configuration对象
  5. // 例如调用 #3
  6. propertiesElement(root.evalNode("properties"));
  7. Properties settings = settingsAsProperties(root.evalNode("settings"));
  8. loadCustomVfs(settings);
  9. loadCustomLogImpl(settings);
  10. typeAliasesElement(root.evalNode("typeAliases"));
  11. pluginElement(root.evalNode("plugins"));
  12. objectFactoryElement(root.evalNode("objectFactory"));
  13. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
  14. reflectorFactoryElement(root.evalNode("reflectorFactory"));
  15. settingsElement(settings);
  16. environmentsElement(root.evalNode("environments"));
  17. databaseIdProviderElement(root.evalNode("databaseIdProvider"));
  18. typeHandlerElement(root.evalNode("typeHandlers"));
  19. // root.evalNode("mappers") 拿到mappers标签
  20. mapperElement(root.evalNode("mappers"));
  21. } catch (Exception e) {
  22. throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  23. }
  24. }
  25. // #3
  26. private void propertiesElement(XNode context) throws Exception {
  27. if (context != null) {
  28. Properties defaults = context.getChildrenAsProperties();
  29. String resource = context.getStringAttribute("resource");
  30. String url = context.getStringAttribute("url");
  31. if (resource != null && url != null) {
  32. throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
  33. }
  34. if (resource != null) {
  35. defaults.putAll(Resources.getResourceAsProperties(resource));
  36. } else if (url != null) {
  37. defaults.putAll(Resources.getUrlAsProperties(url));
  38. }
  39. Properties vars = configuration.getVariables();
  40. if (vars != null) {
  41. defaults.putAll(vars);
  42. }
  43. parser.setVariables(defaults);
  44. // 构建configuration其中的一个属性
  45. configuration.setVariables(defaults);
  46. }
  47. }

单例模式

介绍:属于创建型设计模式,对于整个JVM该对象只提供一个实例

单例模式实现的方法很多,有饿汉式、懒汉式,据说也是面试中问的最多的,MyBatis中的VFS类(虚拟文件系统),是用来提供简单api用来访问资源,用的就是懒汉式。

  1. public abstract class VFS {
  2. // 省略部分代码...
  3. public static VFS getInstance() {
  4. return VFSHolder.INSTANCE;
  5. }
  6. /**
  7. * 获取单例
  8. */
  9. private static class VFSHolder {
  10. static final VFS INSTANCE = createVFS();
  11. @SuppressWarnings("unchecked")
  12. static VFS createVFS() {
  13. // 省略部分代码...
  14. }
  15. }
  16. // 省略部分代码...
  17. }


代理模式

介绍:属于结构型设计模式,通过一个代理类来代替原对象执行一些操作

代理模式又分为静态代理和动态代理,MyBatis中动态代理可以说是核心功能,是整个框架的的基石。代理模式一般以*Proxy结尾,MyBatis中有MapperProxy,利用了JDK的InvocationHandler接口来实现

  1. /**
  2. * 代理类
  3. */
  4. public class MapperProxy<T> implements InvocationHandler, Serializable {
  5. private static final long serialVersionUID = -6424540398559729838L;
  6. // 被代理类
  7. private final Class<T> mapperInterface;
  8. /**
  9. * @param mapperInterface 是被代理类
  10. */
  11. public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
  12. this.sqlSession = sqlSession;
  13. this.mapperInterface = mapperInterface;
  14. this.methodCache = methodCache;
  15. }
  16. /**
  17. *
  18. * @param proxy
  19. * @param method 要代替执行的方法
  20. * @param args 该方法的入参
  21. * @return
  22. * @throws Throwable
  23. */
  24. @Override
  25. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  26. try {
  27. if (Object.class.equals(method.getDeclaringClass())) {
  28. return method.invoke(this, args);
  29. } else if (method.isDefault()) {
  30. return invokeDefaultMethod(proxy, method, args);
  31. }
  32. } catch (Throwable t) {
  33. throw ExceptionUtil.unwrapThrowable(t);
  34. }
  35. final MapperMethod mapperMethod = cachedMapperMethod(method);
  36. // 执行SQL操作
  37. return mapperMethod.execute(sqlSession, args);
  38. }
  39. }

小结

总结下这几种使用场景:

  • 简单工厂模式,当要频繁获取对象时,可以不用new,而是通过工厂来获得;
  • 建造者模式,当有一个复杂的对象,通过构造函数已经不能满足了需求了,可以试试建造者模式;
  • 单例模式,系统中你只想new一次,系统共公用一个实例时,就该想到单例模式;
  • 代理模式,可以参考拦截器,AOP的的使用,代理模式也能实现;

上述几个模式平常开发中出了建造者和单例会用到,其它两个暂时还没有什么场景要用到。但不要觉得没用,正如本文开篇讲的那样,设计模式只是解决某种问题的套路、手段。我们没有必要为了用而用,可以作为知识储备,这样下次遇到,就可以炫技啦~ : )

请你相信我所说的都是错的