public class SqlSession { /** * 动态代理 */ public <T> T getMapper(Class<T> clazz) { //类加载器: 负责加载代理类到内存中 ClassLoader classLoader = SqlSession.class.getClassLoader(); //Class[] interfaces = { UserMapper.class}; Class[] interfaces = {clazz}; T mapperProxy = (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { //在调用UserMaper中的queryAllUser()方法时,代理对象执行invoke方法,返回List @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //创建核心配置类对象 Configuration config = new Configuration(); /******** 解析 @Select注解 *******/ Class clazz = Class.forName(config.getInterName()); Method[] methods = clazz.getMethods(); //遍历数组 for (Method m : methods) { //判断是否有注解 boolean boo = m.isAnnotationPresent(Select.class); //TODO boolean boo2 = m.isAnnotationPresent(ResultType.class); if (boo) { //获取@Select注解对象 Select select = m.getAnnotation(Select.class); //获取属性值 String[] value = select.value();//{"select * from user"} String sql = value[0]; //给Mapper对象中的sql成员变量赋值 config.getMapper().setSql(sql); //TODO ResultType resultType = m.getAnnotation(ResultType.class); String type = resultType.value(); config.getMapper().setResultType(type); } } 为什么这个可以创建一个实现所有接口方法的类,config只有一个怎样记录所有sql,返回值怎样做到可以调用接口中的所有方法
时间: 2025-07-07 19:46:06 浏览: 11
<think>好的,我现在需要回答用户关于Java动态代理和MyBatis中Configuration对象如何记录SQL及返回值类型的问题。首先,我需要回忆动态代理的基本概念,特别是InvocationHandler的作用。动态代理在Java中通常通过Proxy类和InvocationHandler接口实现。当用户调用代理对象的方法时,InvocationHandler的invoke方法会被触发,从而可以拦截并处理这些方法调用。
接下来,用户提到如何通过InvocationHandler实现接口的所有方法。根据引用[2],动态代理的创建需要接口列表,代理对象会实现这些接口。因此,任何对接口方法的调用都会被路由到InvocationHandler的invoke方法。这里需要解释invoke方法如何根据方法名和参数来动态处理不同的方法,可能涉及反射机制,如Method对象的使用。
然后是关于Configuration对象如何记录SQL和返回值类型的问题。MyBatis在启动时会解析Mapper XML文件,这里引用[3]提到mapperElement方法负责解析映射器文件。在这个过程中,每个SQL语句会被解析成MappedStatement对象,并存储在Configuration中。MappedStatement包含SQL语句、参数类型、返回值类型等信息。当执行SQL时,Executor会从Configuration获取对应的MappedStatement,进而执行SQL并处理结果。
另外,引用[1]中的代码片段显示,SimpleExecutor在准备Statement时会使用handler.parameterize方法设置参数,这可能涉及TypeHandler来处理参数和结果映射。TypeHandler负责Java类型和JDBC类型之间的转换,这也是Configuration记录的一部分。
需要确认动态代理在MyBatis中的应用,比如Mapper接口的代理实现。根据引用[4]和[5],MyBatis可能使用JDK动态代理为Mapper接口生成代理对象,当调用接口方法时,InvocationHandler会拦截方法,根据方法名找到对应的SQL语句,并执行数据库操作。
最后,要确保回答结构清晰,分点解释动态代理的原理和Configuration的作用,并引用提供的参考资料来支持每个部分的解释。同时,生成的相关问题需要与主题相关,帮助用户进一步理解。</think>### 动态代理与MyBatis中Configuration机制解析
#### 一、动态代理通过`InvocationHandler`实现接口方法的原理
1. **动态代理核心机制**
Java动态代理通过`Proxy.newProxyInstance()`生成代理对象,代理对象实现了目标接口的所有方法[^2]。当调用接口方法时,实际会转发到`InvocationHandler.invoke()`方法,其流程如下:
```java
public Object invoke(Object proxy, Method method, Object[] args) {
// 拦截方法调用,动态处理逻辑
return result;
}
```
- `Method method`参数表示被调用的接口方法
- `args`参数为方法调用的实际参数
- 通过反射机制`method.invoke(target, args)`可实现原方法调用,但代理场景中通常替换为自定义逻辑(如MyBatis的SQL映射)[^4]
2. **接口方法的统一拦截**
动态代理会为**所有接口方法**生成相同的调用入口,因此`InvocationHandler`需根据方法名、参数类型等条件判断具体执行逻辑。例如MyBatis中,代理`Mapper`接口方法时,会根据方法名定位对应的SQL语句[^3]。
3. **特殊方法处理**
- `Object`类方法(如`toString()`)需特殊处理,避免代理逻辑破坏基础功能[^5]
- 默认方法(Java 8+)通过`method.isDefault()`判断并调用默认实现
---
#### 二、MyBatis中Configuration记录SQL和返回值类型的机制
1. **SQL解析与存储**
MyBatis启动时通过`mapperElement()`解析XML映射文件,将每个SQL语句封装为`MappedStatement`对象,存入`Configuration`的`Map<String, MappedStatement>`结构中[^3]。
```java
// MappedStatement包含的关键属性
public class MappedStatement {
private String id; // 如"com.example.UserMapper.selectById"
private SqlSource sqlSource; // 解析后的SQL模板
private Class<?> resultType; // 返回值类型
private Class<?> parameterType; // 参数类型
}
```
2. **代理方法与SQL的绑定**
当调用`UserMapper.selectById(1)`时:
- 代理对象通过方法全限定名(如`com.example.UserMapper.selectById`)作为key
- 从`Configuration`中获取对应的`MappedStatement`[^1]
- 通过`SqlSession`执行`MappedStatement`中记录的SQL
3. **参数与返回值的类型处理**
- **参数映射**:通过`TypeHandler`将Java对象转换为JDBC参数,例如在`parameterize()`方法中设置PreparedStatement参数
- **返回值映射**:根据`resultType`使用`ResultSetHandler`将查询结果转换为Java对象
---
#### 三、关键代码流程示例(MyBatis执行链路)
```java
// 1. 获取Mapper代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 2. 代理对象方法调用(触发InvocationHandler)
User user = mapper.selectById(1);
// 3. 实际执行过程
public class MapperProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
// 根据方法名获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(method.getName());
// 执行SQL并处理结果
return executor.query(ms, args);
}
}
```
---
阅读全文
相关推荐







