代理模式
引言
代理模式是一种结构型设计模式,核心在于通过代理对象控制对目标对象的访问,为其提供额外的功能或限制,如权限检查、延迟加载或日志记录。它如同对象的“代言人”,在不修改原对象代码的前提下,优雅地扩展行为。代理模式强调控制与增强,特别适合需要隔离访问、优化性能或添加横切逻辑的场景,宛若为对象披上一层智能外衣。
实际开发中的用途
在实际开发中,代理模式广泛应用于权限控制、远程调用、缓存管理及性能优化。例如,在Web应用中,代理可用于拦截请求以验证用户权限;在分布式系统中,代理可封装远程服务调用,屏蔽网络细节;而在ORM框架中,代理实现延迟加载,减少数据库查询开销。代理模式通过解耦客户端与目标对象,降低了直接访问的复杂性,提升了系统的安全性、可维护性和性能,特别适合企业级应用的模块化设计。
Spring 源码中的应用
Spring 框架中,代理模式在 AOP(面向切面编程) 的实现中体现得淋漓尽致。Spring AOP 通过动态代理(基于 JDK 动态代理或 CGLIB)为目标 Bean 添加横切逻辑(如日志、事务管理),无需修改原始代码。核心类 ProxyFactoryBean
和 DefaultAopProxyFactory
负责创建代理对象,封装切面逻辑。
以下是 Spring 源码的典型片段(DefaultAopProxyFactory.java
):
// Spring 框架中的 DefaultAopProxyFactory
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !hasNoUserSuppliedProxyInterfaces(config)) {
// 使用 JDK 动态代理(基于接口)
return new JdkDynamicAopProxy(config);
} else {
// 使用 CGLIB 代理(基于类)
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
}
return new ObjenesisCglibAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
// 检查是否无用户提供的代理接口
return config.getProxiedInterfaces().length == 0;
}
}
这段代码展示了 Spring AOP 如何通过代理模式创建代理对象。详细分析如下:
- 代理模式的体现:
DefaultAopProxyFactory
决定使用 JDK 动态代理(基于接口)或 CGLIB 代理(基于类)创建代理对象,代理对象包装目标 Bean,拦截方法调用以应用切面逻辑(如事务、日志)。- 客户端通过代理对象调用目标方法,无需感知切面逻辑,符合代理模式的“控制访问”目标。
- 关键方法解析:
createAopProxy
根据配置选择代理类型(JDK 或 CGLIB)。JDK 动态代理适用于实现接口的 Bean,CGLIB 适用于无接口的类。- 代理对象内部持有目标对象引用(通过
AdvisedSupport
配置),并在方法调用时插入切面逻辑(如MethodInterceptor
)。
- 解耦与扩展性:
- 代理模式解耦了客户端与目标 Bean,切面逻辑(如事务管理)与业务逻辑分离,遵循“开闭原则”。
- Spring AOP 支持动态添加或移除切面,客户端代码无需修改即可启用新功能(如性能监控)。
- 实际问题解决:
- 解决了横切关注点(如日志、事务)的代码分散问题,通过代理集中管理,提升代码复用性。
- 支持延迟加载(如 Spring 的
@Lazy
注解),通过代理延迟目标 Bean 的初始化,优化启动性能。
ProxyFactoryBean
和 DefaultAopProxyFactory
的实现使 Spring AOP 成为企业级应用的核心组件,广泛用于事务管理、日志记录和安全控制,显著提升了代码的模块化与可维护性。
代理模式在 MyBatis 中的用途
MyBatis 是一个轻量级持久化框架,其核心优势在于通过 Mapper 接口以声明式方式操作数据库。代理模式在 MyBatis 中用于为 Mapper 接口动态生成代理对象,这些代理对象拦截接口方法调用,自动完成 SQL 查询、参数绑定和结果映射。代理模式如同 MyBatis 的“幕后导演”,隐藏了复杂的 JDBC 操作,让开发者专注于业务逻辑,显著提升开发效率和代码简洁性。
在 MyBatis 中,代理模式主要解决以下问题:
- 简化数据库操作:开发者无需手动编写 JDBC 代码(如获取连接、执行语句、处理结果集),只需定义 Mapper 接口和对应的 SQL 语句。
- 解耦业务与持久化逻辑:通过代理,业务代码仅依赖接口,无需关心底层 SQL 执行或数据库驱动。
- 动态 SQL 执行:代理对象根据接口方法和 XML/注解配置,动态解析 SQL、绑定参数并映射结果。
- 扩展性与可维护性:代理模式支持热加载配置、插件拦截等功能,便于扩展(如分页、日志)。
例如,在一个用户管理模块中,开发者只需定义 UserMapper
接口和方法(如 selectById
),MyBatis 的代理机制自动将方法调用转换为 SQL 执行,屏蔽了底层的复杂性。
MyBatis 中代理模式的实现原理
MyBatis 使用 JDK 动态代理 为 Mapper 接口生成实现。核心流程如下:
- Mapper 接口注册:MyBatis 在启动时扫描 Mapper 接口(如
UserMapper
),将其注册到MapperRegistry
。 - 代理对象创建:通过
MapperProxyFactory
为每个 Mapper 接口生成代理对象,代理对象实现接口并拦截方法调用。 - 方法拦截:代理对象将方法调用委托给
MapperProxy
,后者根据方法签名查找对应的MappedStatement
(SQL 配置),执行 SQL 并处理结果。 - SQL 执行与结果映射:通过
SqlSession
执行 SQL,结合ResultMap
将查询结果映射为 Java 对象。
以下从源码角度详细分析。
源码分析:MyBatis 代理模式的实现
MyBatis 的代理模式核心逻辑集中在 org.apache.ibatis.binding
包中,以下是关键类和方法的源码分析(基于 MyBatis 3.5.x)。
1. Mapper 接口注册
MyBatis 在启动时通过 Configuration
加载 Mapper 接口,注册到 MapperRegistry
。以下是 MapperRegistry
的关键代码:
// org.apache.ibatis.binding.MapperRegistry
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
// 注册 Mapper 接口
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// 解析 Mapper 注解或 XML 配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
// 获取 Mapper 代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
分析:
addMapper
将 Mapper 接口注册到knownMappers
,创建对应的MapperProxyFactory
。MapperAnnotationBuilder
解析接口方法上的注解(如@Select
)或 XML 配置(如select
标签),生成MappedStatement
。getMapper
通过MapperProxyFactory
创建代理对象,供客户端使用。
2. 代理对象生成
MapperProxyFactory
负责生成 Mapper 接口的代理对象,核心方法是 newInstance
:
// org.apache.ibatis.binding.MapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 使用 JDK 动态代理生成代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 创建 MapperProxy 作为 InvocationHandler
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
return newInstance(mapperProxy);
}
}
分析:
newInstance
使用java.lang.reflect.Proxy
创建 JDK 动态代理,代理对象实现mapperInterface
(如UserMapper
)。MapperProxy
作为InvocationHandler
,处理所有方法调用。
3. 方法拦截与执行
MapperProxy
是代理模式的核心,拦截 Mapper 接口方法并执行 SQL:
// org.apache.ibatis.binding.MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = new ConcurrentHashMap<>();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 处理 Object 方法(如 toString)
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 获取或缓存 MapperMethodInvoker
final MapperMethodInvoker invoker = cachedInvoker(method);
return invoker.invoke(proxy, method, args, sqlSession);
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
return methodCache.computeIfAbsent(method, m -> {
// 创建 PlainMethodInvoker 或其他类型
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
});
}
}
分析:
invoke
拦截 Mapper 接口方法调用,委托给MapperMethodInvoker
。MapperMethod
封装方法签名与MappedStatement
的映射,负责解析 SQL、绑定参数和执行查询。methodCache
缓存方法调用逻辑,提升性能。
4. SQL 执行与结果映射
MapperMethod
的 execute
方法协调 SQL 执行:
// org.apache.ibatis.binding.MapperMethod
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else {
result = executeForOne(sqlSession, args);
}
break;
case INSERT:
result = executeForUpdate(sqlSession, args);
break;
// 其他 case(如 UPDATE、DELETE)
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
return result;
}
}
分析:
SqlCommand
从Configuration
获取MappedStatement
,包含 SQL 和参数配置。execute
根据方法类型(SELECT
、INSERT
等)调用SqlSession
的相应方法(如selectList
、insert
)。- 结果映射通过
ResultMap
或实体类注解完成(如DefaultResultSetHandler
处理)。
代码使用案例:MyBatis 代理模式应用
以下是一个基于 MyBatis 的案例,展示代理模式如何为 UserMapper
接口生成实现。
// User 实体类
public class User {
private Long id;
private String username;
// Getter 和 Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}
// UserMapper 接口
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);
}
// MyBatis 配置和主程序
public class MyBatisDemo {
public static void main(String[] args) {
// 加载 MyBatis 配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = MyBatisDemo.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 获取 Mapper 代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 调用代理方法
User user = mapper.selectById(1L);
System.out.println("查询用户: " + user.getUsername());
} catch (IOException e) {
e.printStackTrace();
}
}
}
mybatis-config.xml(简化的配置文件):
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.example.UserMapper"/>
</mappers>
</configuration>
案例说明:
UserMapper
定义了selectById
方法,通过@Select
注解指定 SQL。- MyBatis 在启动时为
UserMapper
生成代理对象,拦截selectById
调用。 - 代理对象通过
SqlSession
执行 SQL,并将结果映射为User
对象。 - 客户端只需调用接口方法,无需关心 JDBC 或结果映射,体现了代理模式的透明性。
总结
代理模式是一种结构型设计模式,通过代理对象控制对目标对象的访问,在不修改原有代码的前提下添加横切逻辑、延迟加载或简化复杂操作。MyBatis 深度运用代理模式,通过 JDK 动态代理为核心 Mapper 接口生成动态实现,巧妙控制数据库访问,屏蔽 SQL 执行与结果映射的复杂性。代理模式在此如精密门卫,MapperProxy 和 MapperProxyFactory 协作拦截接口方法调用,动态映射至 SQL,赋予接口声明式的简洁性与执行的高效性。这种代理机制不仅简化开发,还确保高内聚与低耦合,极大提升框架扩展性。无论单机还是 Spring 集成,MyBatis 的代理模式都展现了优雅与灵活,堪称持久化框架典范,启发开发者在架构设计中活用代理模式打造健壮系统。# 代理模式
引言
代理模式(Proxy Pattern)是一种结构型设计模式,核心在于通过代理对象控制对目标对象的访问,为其提供额外的功能或限制,如权限检查、延迟加载或日志记录。它如同对象的“代言人”,在不修改原对象代码的前提下,优雅地扩展行为。代理模式强调控制与增强,特别适合需要隔离访问、优化性能或添加横切逻辑的场景,宛若为对象披上一层智能外衣。
实际开发中的用途
在实际开发中,代理模式广泛应用于权限控制、远程调用、缓存管理及性能优化。例如,在Web应用中,代理可用于拦截请求以验证用户权限;在分布式系统中,代理可封装远程服务调用,屏蔽网络细节;而在ORM框架中,代理实现延迟加载,减少数据库查询开销。代理模式通过解耦客户端与目标对象,降低了直接访问的复杂性,提升了系统的安全性、可维护性和性能,特别适合企业级应用的模块化设计。
Spring 源码中的应用
Spring 框架中,代理模式在 AOP(面向切面编程) 的实现中体现得淋漓尽致。Spring AOP 通过动态代理(基于 JDK 动态代理或 CGLIB)为目标 Bean 添加横切逻辑(如日志、事务管理),无需修改原始代码。核心类 ProxyFactoryBean
和 DefaultAopProxyFactory
负责创建代理对象,封装切面逻辑。
以下是 Spring 源码的典型片段(DefaultAopProxyFactory.java
):
// Spring 框架中的 DefaultAopProxyFactory
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !hasNoUserSuppliedProxyInterfaces(config)) {
// 使用 JDK 动态代理(基于接口)
return new JdkDynamicAopProxy(config);
} else {
// 使用 CGLIB 代理(基于类)
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
}
return new ObjenesisCglibAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
// 检查是否无用户提供的代理接口
return config.getProxiedInterfaces().length == 0;
}
}
这段代码展示了 Spring AOP 如何通过代理模式创建代理对象。详细分析如下:
- 代理模式的体现:
DefaultAopProxyFactory
决定使用 JDK 动态代理(基于接口)或 CGLIB 代理(基于类)创建代理对象,代理对象包装目标 Bean,拦截方法调用以应用切面逻辑(如事务、日志)。- 客户端通过代理对象调用目标方法,无需感知切面逻辑,符合代理模式的“控制访问”目标。
- 关键方法解析:
createAopProxy
根据配置选择代理类型(JDK 或 CGLIB)。JDK 动态代理适用于实现接口的 Bean,CGLIB 适用于无接口的类。- 代理对象内部持有目标对象引用(通过
AdvisedSupport
配置),并在方法调用时插入切面逻辑(如MethodInterceptor
)。
- 解耦与扩展性:
- 代理模式解耦了客户端与目标 Bean,切面逻辑(如事务管理)与业务逻辑分离,遵循“开闭原则”。
- Spring AOP 支持动态添加或移除切面,客户端代码无需修改即可启用新功能(如性能监控)。
- 实际问题解决:
- 解决了横切关注点(如日志、事务)的代码分散问题,通过代理集中管理,提升代码复用性。
- 支持延迟加载(如 Spring 的
@Lazy
注解),通过代理延迟目标 Bean 的初始化,优化启动性能。
ProxyFactoryBean
和 DefaultAopProxyFactory
的实现使 Spring AOP 成为企业级应用的核心组件,广泛用于事务管理、日志记录和安全控制,显著提升了代码的模块化与可维护性。
代理模式在 MyBatis 中的用途
MyBatis 是一个轻量级持久化框架,其核心优势在于通过 Mapper 接口以声明式方式操作数据库。代理模式在 MyBatis 中用于为 Mapper 接口动态生成代理对象,这些代理对象拦截接口方法调用,自动完成 SQL 查询、参数绑定和结果映射。代理模式如同 MyBatis 的“幕后导演”,隐藏了复杂的 JDBC 操作,让开发者专注于业务逻辑,显著提升开发效率和代码简洁性。
在 MyBatis 中,代理模式主要解决以下问题:
- 简化数据库操作:开发者无需手动编写 JDBC 代码(如获取连接、执行语句、处理结果集),只需定义 Mapper 接口和对应的 SQL 语句。
- 解耦业务与持久化逻辑:通过代理,业务代码仅依赖接口,无需关心底层 SQL 执行或数据库驱动。
- 动态 SQL 执行:代理对象根据接口方法和 XML/注解配置,动态解析 SQL、绑定参数并映射结果。
- 扩展性与可维护性:代理模式支持热加载配置、插件拦截等功能,便于扩展(如分页、日志)。
例如,在一个用户管理模块中,开发者只需定义 UserMapper
接口和方法(如 selectById
),MyBatis 的代理机制自动将方法调用转换为 SQL 执行,屏蔽了底层的复杂性。
MyBatis 中代理模式的实现原理
MyBatis 使用 JDK 动态代理 为 Mapper 接口生成实现。核心流程如下:
- Mapper 接口注册:MyBatis 在启动时扫描 Mapper 接口(如
UserMapper
),将其注册到MapperRegistry
。 - 代理对象创建:通过
MapperProxyFactory
为每个 Mapper 接口生成代理对象,代理对象实现接口并拦截方法调用。 - 方法拦截:代理对象将方法调用委托给
MapperProxy
,后者根据方法签名查找对应的MappedStatement
(SQL 配置),执行 SQL 并处理结果。 - SQL 执行与结果映射:通过
SqlSession
执行 SQL,结合ResultMap
将查询结果映射为 Java 对象。
以下从源码角度详细分析。
源码分析:MyBatis 代理模式的实现
MyBatis 的代理模式核心逻辑集中在 org.apache.ibatis.binding
包中,以下是关键类和方法的源码分析(基于 MyBatis 3.5.x)。
1. Mapper 接口注册
MyBatis 在启动时通过 Configuration
加载 Mapper 接口,注册到 MapperRegistry
。以下是 MapperRegistry
的关键代码:
// org.apache.ibatis.binding.MapperRegistry
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
this.config = config;
}
// 注册 Mapper 接口
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// 解析 Mapper 注解或 XML 配置
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
// 获取 Mapper 代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
分析:
addMapper
将 Mapper 接口注册到knownMappers
,创建对应的MapperProxyFactory
。MapperAnnotationBuilder
解析接口方法上的注解(如@Select
)或 XML 配置(如select
标签),生成MappedStatement
。getMapper
通过MapperProxyFactory
创建代理对象,供客户端使用。
2. 代理对象生成
MapperProxyFactory
负责生成 Mapper 接口的代理对象,核心方法是 newInstance
:
// org.apache.ibatis.binding.MapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 使用 JDK 动态代理生成代理对象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 创建 MapperProxy 作为 InvocationHandler
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
return newInstance(mapperProxy);
}
}
分析:
newInstance
使用java.lang.reflect.Proxy
创建 JDK 动态代理,代理对象实现mapperInterface
(如UserMapper
)。MapperProxy
作为InvocationHandler
,处理所有方法调用。
3. 方法拦截与执行
MapperProxy
是代理模式的核心,拦截 Mapper 接口方法并执行 SQL:
// org.apache.ibatis.binding.MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = new ConcurrentHashMap<>();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 处理 Object 方法(如 toString)
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
// 获取或缓存 MapperMethodInvoker
final MapperMethodInvoker invoker = cachedInvoker(method);
return invoker.invoke(proxy, method, args, sqlSession);
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
return methodCache.computeIfAbsent(method, m -> {
// 创建 PlainMethodInvoker 或其他类型
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
});
}
}
分析:
invoke
拦截 Mapper 接口方法调用,委托给MapperMethodInvoker
。MapperMethod
封装方法签名与MappedStatement
的映射,负责解析 SQL、绑定参数和执行查询。methodCache
缓存方法调用逻辑,提升性能。
4. SQL 执行与结果映射
MapperMethod
的 execute
方法协调 SQL 执行:
// org.apache.ibatis.binding.MapperMethod
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else {
result = executeForOne(sqlSession, args);
}
break;
case INSERT:
result = executeForUpdate(sqlSession, args);
break;
// 其他 case(如 UPDATE、DELETE)
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
return result;
}
}
分析:
SqlCommand
从Configuration
获取MappedStatement
,包含 SQL 和参数配置。execute
根据方法类型(SELECT
、INSERT
等)调用SqlSession
的相应方法(如selectList
、insert
)。- 结果映射通过
ResultMap
或实体类注解完成(如DefaultResultSetHandler
处理)。
代码使用案例:MyBatis 代理模式应用
以下是一个基于 MyBatis 的案例,展示代理模式如何为 UserMapper
接口生成实现。
// User 实体类
public class User {
private Long id;
private String username;
// Getter 和 Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
}
// UserMapper 接口
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);
}
// MyBatis 配置和主程序
public class MyBatisDemo {
public static void main(String[] args) {
// 加载 MyBatis 配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = MyBatisDemo.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 获取 Mapper 代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 调用代理方法
User user = mapper.selectById(1L);
System.out.println("查询用户: " + user.getUsername());
} catch (IOException e) {
e.printStackTrace();
}
}
}
mybatis-config.xml(简化的配置文件):
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.example.UserMapper"/>
</mappers>
</configuration>
案例说明:
UserMapper
定义了selectById
方法,通过@Select
注解指定 SQL。- MyBatis 在启动时为
UserMapper
生成代理对象,拦截selectById
调用。 - 代理对象通过
SqlSession
执行 SQL,并将结果映射为User
对象。 - 客户端只需调用接口方法,无需关心 JDBC 或结果映射,体现了代理模式的透明性。
总结
代理模式是一种结构型设计模式,通过代理对象控制对目标对象的访问,在不修改原有代码的前提下添加横切逻辑、延迟加载或简化复杂操作。MyBatis 深度运用代理模式,通过 JDK 动态代理为核心 Mapper 接口生成动态实现,巧妙控制数据库访问,屏蔽 SQL 执行与结果映射的复杂性。代理模式在此如精密门卫,MapperProxy 和 MapperProxyFactory 协作拦截接口方法调用,动态映射至 SQL,赋予接口声明式的简洁性与执行的高效性。这种代理机制不仅简化开发,还确保高内聚与低耦合,极大提升框架扩展性。无论单机还是 Spring 集成,MyBatis 的代理模式都展现了优雅与灵活,堪称持久化框架典范,启发开发者在架构设计中活用代理模式打造健壮系统。
(对您有帮助 && 觉得我总结的还行) -> 受累点个免费的赞👍,谢谢