MyBatis 与代理模式(Proxy Pattern)详解
代理模式是一种结构型设计模式,它为另一个对象提供一个替身(代理)以控制对这个对象的访问。在 MyBatis 中,代理模式被广泛应用于 Mapper 接口的动态代理 和 延迟加载 两大核心特性,同时插件机制也基于动态代理实现拦截。
一、代理模式概述
意图:为其他对象提供一种代理以控制对这个对象的访问。
参与者:
- Subject:抽象主题,声明真实对象和代理对象的共同接口。
- RealSubject:真实主题,定义真正的业务逻辑。
- Proxy:代理,持有对 RealSubject 的引用,并在调用前后执行附加操作。
分类:
- 静态代理:在编译时确定代理类。
- 动态代理:运行时动态生成代理类(JDK 动态代理或 CGLIB)。
MyBatis 主要使用 JDK 动态代理(Mapper 接口、延迟加载代理、插件拦截链)和 CGLIB(某些延迟加载场景)。
二、MyBatis 中代理模式的典型应用
| Mapper 接口调用 | MapperProxy | SqlSession 的数据库操作 | JDK 动态代理 |
| 延迟加载(关联对象) | 由 ProxyFactory 生成的代理 | 目标实体对象 | Javassist / CGLIB |
| 插件(Interceptor) | 由 Plugin 生成的代理 | Executor, StatementHandler 等 | JDK 动态代理 |
下面重点剖析 Mapper 接口动态代理 和 延迟加载代理。
三、Mapper 接口的动态代理(MapperProxy)
MyBatis 允许开发者只定义 Mapper 接口(如 UserMapper)而不需要编写实现类,调用接口方法时会自动执行对应的 SQL。这完全依赖动态代理。
3.1 UML 类图
#mermaid-svg-EVTSpDO3B94t1o6k{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EVTSpDO3B94t1o6k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EVTSpDO3B94t1o6k .error-icon{fill:#552222;}#mermaid-svg-EVTSpDO3B94t1o6k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EVTSpDO3B94t1o6k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EVTSpDO3B94t1o6k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EVTSpDO3B94t1o6k .marker.cross{stroke:#333333;}#mermaid-svg-EVTSpDO3B94t1o6k svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EVTSpDO3B94t1o6k p{margin:0;}#mermaid-svg-EVTSpDO3B94t1o6k g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-EVTSpDO3B94t1o6k g.classGroup text .title{font-weight:bolder;}#mermaid-svg-EVTSpDO3B94t1o6k .cluster-label text{fill:#333;}#mermaid-svg-EVTSpDO3B94t1o6k .cluster-label span{color:#333;}#mermaid-svg-EVTSpDO3B94t1o6k .cluster-label span p{background-color:transparent;}#mermaid-svg-EVTSpDO3B94t1o6k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EVTSpDO3B94t1o6k .cluster text{fill:#333;}#mermaid-svg-EVTSpDO3B94t1o6k .cluster span{color:#333;}#mermaid-svg-EVTSpDO3B94t1o6k .nodeLabel,#mermaid-svg-EVTSpDO3B94t1o6k .edgeLabel{color:#131300;}#mermaid-svg-EVTSpDO3B94t1o6k .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-EVTSpDO3B94t1o6k .label text{fill:#131300;}#mermaid-svg-EVTSpDO3B94t1o6k .labelBkg{background:#ECECFF;}#mermaid-svg-EVTSpDO3B94t1o6k .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-EVTSpDO3B94t1o6k .classTitle{font-weight:bolder;}#mermaid-svg-EVTSpDO3B94t1o6k .node rect,#mermaid-svg-EVTSpDO3B94t1o6k .node circle,#mermaid-svg-EVTSpDO3B94t1o6k .node ellipse,#mermaid-svg-EVTSpDO3B94t1o6k .node polygon,#mermaid-svg-EVTSpDO3B94t1o6k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EVTSpDO3B94t1o6k .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k g.clickable{cursor:pointer;}#mermaid-svg-EVTSpDO3B94t1o6k g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-EVTSpDO3B94t1o6k g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-EVTSpDO3B94t1o6k .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-EVTSpDO3B94t1o6k .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-EVTSpDO3B94t1o6k .dashed-line{stroke-dasharray:3;}#mermaid-svg-EVTSpDO3B94t1o6k .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-EVTSpDO3B94t1o6k #compositionStart,#mermaid-svg-EVTSpDO3B94t1o6k .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #compositionEnd,#mermaid-svg-EVTSpDO3B94t1o6k .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #dependencyStart,#mermaid-svg-EVTSpDO3B94t1o6k .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #dependencyStart,#mermaid-svg-EVTSpDO3B94t1o6k .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #extensionStart,#mermaid-svg-EVTSpDO3B94t1o6k .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #extensionEnd,#mermaid-svg-EVTSpDO3B94t1o6k .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #aggregationStart,#mermaid-svg-EVTSpDO3B94t1o6k .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #aggregationEnd,#mermaid-svg-EVTSpDO3B94t1o6k .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #lollipopStart,#mermaid-svg-EVTSpDO3B94t1o6k .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k #lollipopEnd,#mermaid-svg-EVTSpDO3B94t1o6k .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-EVTSpDO3B94t1o6k .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-EVTSpDO3B94t1o6k .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EVTSpDO3B94t1o6k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EVTSpDO3B94t1o6k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EVTSpDO3B94t1o6k :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
实现
InvocationHandler
创建
委托
持有
«interface»
UserMapper
+selectById(Long id) : User
+insert(User user) : int
MapperProxy<T>
-SqlSession sqlSession
-Class<T> mapperInterface
-Map<Method, MapperMethod> methodCache
+invoke(Object proxy, Method method, Object[] args) : Object
MapperProxyFactory<T>
-Class<T> mapperInterface
-Map<Method, MapperMethod> methodCache
+newInstance(SqlSession sqlSession) : T
-newInstance(MapperProxy<T> mapperProxy) : T
«interface»
SqlSession
+selectOne(String statement, Object parameter)
+insert(String statement, Object parameter)
+getMapper(Class<T> type) : T

