Mybatis的动态代理实现过程
文章目录
mybatis的单独运行代码如下
public static void main(String[] args) throws IOException {
// 1.读取配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(resourceAsStream);
// 3.使用工厂生产SqlSession对象
SqlSession sqlSession = factory.openSession();
// 4.使用SqlSession创建Service的代理对象
IUserService userService = sqlSession.getMapper(IUserService.class);
// 5.使用代理对象执行方法
List<User> users = userService.findAll();
for (User user : users) {
System.out.println("user = " + user);
}
// 6.释放资源
sqlSession.close();
resourceAsStream.close();
}
首先加载配置文件
可以定位到parseConfiguration方法
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
mapperElement(root.evalNode(“mappers”));
该方法是对全局配置文件中mappers属性的解析,走进去:
mapperParser.parse()方法就是XMLMapperBuilder对Mapper映射器文件进行解析,可与XMLConfigBuilder进行类比
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper")); //解析映射文件的根节点mapper元素
configuration.addLoadedResource(resource);
bindMapperForNamespace(); //重点方法,这个方法内部会根据namespace属性值,生成动态代理类
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
configurationElement(XNode context)方法
该方法主要用于将mapper文件中的元素信息,比如insert、select这等信息解析到MappedStatement对象,并保存到Configuration类中的mappedStatements属性中,以便于后续动态代理类执行CRUD操作时能够获取真正的Sql语句信息
buildStatementFromContext方法就用于解析insert、select这类元素信息,并将其封装成MappedStatement对象,具体的实现细节这里就不细说了。
bindMapperForNamespace()方法
该方法是核心方法,它会根据mapper文件中的namespace属性值,为接口生成动态代理类,这就来到了我们的主题内容——动态代理类是如何生成的。
###动态代理类的生成
private void bindMapperForNamespace() {
//获取mapper元素的namespace属性值
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 获取namespace属性值对应的Class对象
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//如果没有这个类,则直接忽略,这是因为namespace属性值只需要唯一即可,并不一定对应一个XXXMapper接口
//没有XXXMapper接口的时候,我们可以直接使用SqlSession来进行增删改查
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
//如果namespace属性值有对应的Java类,调用Configuration的addMapper方法,将其添加到MapperRegistry中
configuration.addMapper(boundType);
}
}
}
}
这里提到了Configuration的addMapper方法,实际上Configuration类里面通过MapperRegistry对象维护了所有要生成动态代理类的XxxMapper接口信息,可见Configuration类确实是相当重要一类
public class Configuration {
...
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
...
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
...
}
其中两个重要的方法:getMapper()和addMapper()
getMapper(): 用于创建接口的动态类
addMapper(): mybatis在解析配置文件时,会将需要生成动态代理类的接口注册到其中
- Configuration#addMappper()
Configuration将addMapper方法委托给MapperRegistry的addMapper进行的,源码如下:
public <T> void addMapper(Class<T> type) {
// 这个class必须是一个接口,因为是使用JDK动态代理,所以需要是接口,否则不会针对其生成动态代理
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 生成一个MapperProxyFactory,用于之后生成动态代理类
knownMappers.put(type, new MapperProxyFactory<>(type));
//以下代码片段用于解析我们定义的XxxMapper接口里面使用的注解,这主要是处理不使用xml映射文件的情况
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperRegistry内部维护一个映射关系,每个接口对应一个MapperProxyFactory(生成动态代理工厂类)
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
这样便于在后面调用MapperRegistry的getMapper()时,直接从Map中获取某个接口对应的动态代理工厂类,然后再利用工厂类针对其接口生成真正的动态代理类。
- Configuration#getMapper()
Configuration的getMapper()方法内部就是调用MapperRegistry的getMapper()方法,源代码如下:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//根据Class对象获取创建动态代理的工厂对象MapperProxyFactory
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);
}
}
从上面可以看出,创建动态代理类的核心代码就是在MapperProxyFactory.newInstance方法中,源码如下:
protected T newInstance(MapperProxy<T> mapperProxy) {
//这里使用JDK动态代理,通过Proxy.newProxyInstance生成动态代理类
// newProxyInstance的参数:类加载器、接口类、InvocationHandler接口实现类
// 动态代理可以将所有接口的调用重定向到调用处理器InvocationHandler,调用它的invoke方法
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
这里的InvocationHandler接口的实现类是MapperProxy,其中有这样一个方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//如果调用的是Object类中定义的方法,直接通过反射调用即可
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//调用XxxMapper接口自定义的方法,进行代理
//首先将当前被调用的方法Method构造成一个MapperMethod对象,然后掉用其execute方法真正的开始执行。
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
return mapperMethod.execute(sqlSession, args);
这里的execute方法是关键
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()) {
//insert语句的处理逻辑
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
//update语句的处理逻辑
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
//delete语句的处理逻辑
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
//select语句的处理逻辑
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
//调用sqlSession的selectOne方法
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
...
}
资源加载过程
XMLConfigBuilder.parseConfiuration() --> XMLConfigBuilder.mapperElement() --> XMLMapperBuilder.parse() --> XMLMapperBuilder.bindMapperForNamespace() --> Configuration.addMapper() --> MapeprRegistry.addMapper() --> MapperAnnotationBuilder.parse() …
动态代理过程
SqlSession.getMapper() --> MapperRegistry.getMapper() --> MapperProxyFactory.newInstance() --> Proxy.newProxyInstance()
反射过程
MapperProxy.invoke() --> MapperMethod.execute()
参考
http://www.tianshouzhi.com/api/tutorials/mybatis/360