@ -0,0 +1,432 @@
|
||||
# mybatis 反射
|
||||
- Author: [HuiFer](https://github.com/huifer)
|
||||
- Description: 该文介绍 mybatis 反射相关类的源码
|
||||
## addDefaultConstructor
|
||||
|
||||
- mybatis 的反射相关内容在`org.apache.ibatis.reflection` 下存放. 本片主要讲解`org.apache.ibatis.reflection.Reflector`类, 先看一下该类的属性
|
||||
```java
|
||||
public class Reflector {
|
||||
|
||||
/**
|
||||
* 实体类.class
|
||||
*/
|
||||
private final Class<?> type;
|
||||
/**
|
||||
* 可读 属性
|
||||
*/
|
||||
private final String[] readablePropertyNames;
|
||||
/**
|
||||
* 可写 属性值
|
||||
*/
|
||||
private final String[] writablePropertyNames;
|
||||
/**
|
||||
* set 方法列表
|
||||
*/
|
||||
private final Map<String, Invoker> setMethods = new HashMap<>();
|
||||
/**
|
||||
* get 方法列表
|
||||
*/
|
||||
private final Map<String, Invoker> getMethods = new HashMap<>();
|
||||
/**
|
||||
* set 的数据类型
|
||||
*/
|
||||
private final Map<String, Class<?>> setTypes = new HashMap<>();
|
||||
/**
|
||||
* get 的数据类型
|
||||
*/
|
||||
private final Map<String, Class<?>> getTypes = new HashMap<>();
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
private Constructor<?> defaultConstructor;
|
||||
|
||||
/**
|
||||
* 缓存数据, 大写KEY
|
||||
*/
|
||||
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
- 构造方法, 构造方法传入一个类的字节码,在构造方法中设置相关的属性值
|
||||
```java
|
||||
public class Reflector {
|
||||
|
||||
/**
|
||||
* @param clazz 待解析类的字节码
|
||||
*/
|
||||
public Reflector(Class<?> clazz) {
|
||||
type = clazz;
|
||||
// 构造方法
|
||||
addDefaultConstructor(clazz);
|
||||
// get 方法
|
||||
addGetMethods(clazz);
|
||||
// set 方法
|
||||
addSetMethods(clazz);
|
||||
// 字段值
|
||||
addFields(clazz);
|
||||
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
|
||||
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
|
||||
for (String propName : readablePropertyNames) {
|
||||
// 循环操作设置到缓存中,
|
||||
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
|
||||
}
|
||||
for (String propName : writablePropertyNames) {
|
||||
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- `addDefaultConstructor` 方法 , 下面截图内容为JDK8 mybatis中 的内容
|
||||
```java
|
||||
private void addDefaultConstructor(Class<?> clazz) {
|
||||
|
||||
// 获取类里面的所有构造方法
|
||||
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
|
||||
// 过滤得到空参构造 constructor -> constructor.getParameterTypes().length == 0
|
||||
Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
|
||||
.findAny().ifPresent(constructor -> {
|
||||
System.out.println("有空参构造");
|
||||
this.defaultConstructor = constructor;
|
||||
});
|
||||
}
|
||||
```
|
||||
- 创建一个测试类
|
||||
```java
|
||||
public class People {
|
||||
private String name;
|
||||
|
||||
public People() {
|
||||
}
|
||||
|
||||
public People(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "People{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
```java
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
class HfReflectorTest {
|
||||
@Test
|
||||
void getDefaultConstructorTest() throws Exception {
|
||||
Reflector reflector = new Reflector(People.class);
|
||||
// 获取空参构造方法
|
||||
Constructor<?> defaultConstructor = reflector.getDefaultConstructor();
|
||||
People o = (People) defaultConstructor.newInstance();
|
||||
o.setName("hhh");
|
||||
|
||||
System.out.println(o);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 准备工作完成了开始进行 debug , 在`org.apache.ibatis.reflection.Reflector#addDefaultConstructor`这个方法上打上断点
|
||||
|
||||
![1575890354400](/images/mybatis/1575890354400.png)
|
||||
|
||||
观察`constructors`属性存在两个方法,这两个方法就是我在`People`类中的构造方法.
|
||||
|
||||
根据语法内容我们应该对`parameterTypes`属性进行查看
|
||||
|
||||
![1575890475839](/images/mybatis/1575890475839.png)
|
||||
|
||||
可以发现空参构造的`parameterTypes`长度是0.因此可以确认`org.apache.ibatis.reflection.Reflector#addDefaultConstructor`方法获取了空参构造
|
||||
|
||||
- 继续看`org.apache.ibatis.reflection.Reflector#getDefaultConstructor`方法, 该方法是获取构造函数的方法,如果构造函数没有就抛出异常,这也是为什么我们的实体类需要把空参构造写上去的原因。
|
||||
|
||||
```java
|
||||
public Constructor<?> getDefaultConstructor() {
|
||||
if (defaultConstructor != null) {
|
||||
return defaultConstructor;
|
||||
} else {
|
||||
// 如果没有空参构造抛出的异常
|
||||
throw new ReflectionException("There is no default constructor for " + type);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## addGetMethods
|
||||
|
||||
- 该方法获取了所有`get`和`is`开头的方法
|
||||
|
||||
```java
|
||||
private void addGetMethods(Class<?> clazz) {
|
||||
// 反射方法
|
||||
Map<String, List<Method>> conflictingGetters = new HashMap<>();
|
||||
Method[] methods = getClassMethods(clazz);
|
||||
// JDK8 filter 过滤get 开头的方法
|
||||
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
|
||||
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
|
||||
resolveGetterConflicts(conflictingGetters);
|
||||
}
|
||||
```
|
||||
|
||||
- 该方法中依旧使用了JDK8语法通过`m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName())`来判断是否是`get`或·`is`开头的内容
|
||||
|
||||
- 调用`org.apache.ibatis.reflection.property.PropertyNamer`
|
||||
|
||||
```java
|
||||
public static boolean isGetter(String name) {
|
||||
// 在语义上 is 开头的也是get开头的
|
||||
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
|
||||
}
|
||||
```
|
||||
|
||||
- `resolveGetterConflicts`方法后续介绍
|
||||
|
||||
|
||||
|
||||
## getClassMethods
|
||||
|
||||
- `org.apache.ibatis.reflection.Reflector#getClassMethods`,该方法将传入对象的所有可见方法都获取到进行唯一标识处理成一个`Map`对象 添加方法为`org.apache.ibatis.reflection.Reflector#addUniqueMethods`
|
||||
|
||||
```java
|
||||
private Method[] getClassMethods(Class<?> clazz) {
|
||||
// 方法唯一标识: 方法
|
||||
Map<String, Method> uniqueMethods = new HashMap<>();
|
||||
Class<?> currentClass = clazz;
|
||||
while (currentClass != null && currentClass != Object.class) {
|
||||
// getDeclaredMethods 获取 public ,private , protcted 方法
|
||||
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
|
||||
|
||||
// we also need to look for interface methods -
|
||||
// because the class may be abstract
|
||||
// 当前类是否继承别的类(实现接口)如果继承则需要进行操作
|
||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||
for (Class<?> anInterface : interfaces) {
|
||||
// getMethods 获取本身和父类的 public 方法
|
||||
addUniqueMethods(uniqueMethods, anInterface.getMethods());
|
||||
}
|
||||
|
||||
// 循环往上一层一层寻找最后回到 Object 类 的上级为null 结束
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
|
||||
Collection<Method> methods = uniqueMethods.values();
|
||||
|
||||
return methods.toArray(new Method[0]);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
- `org.apache.ibatis.reflection.Reflector#addUniqueMethods`
|
||||
|
||||
```java
|
||||
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
|
||||
for (Method currentMethod : methods) {
|
||||
// 桥接, 具体还不知道
|
||||
// TODO: 2019/12/9 JAVA 桥接方法
|
||||
if (!currentMethod.isBridge()) {
|
||||
// 方法的唯一标识
|
||||
String signature = getSignature(currentMethod);
|
||||
// check to see if the method is already known
|
||||
// if it is known, then an extended class must have
|
||||
// overridden a method
|
||||
if (!uniqueMethods.containsKey(signature)) {
|
||||
uniqueMethods.put(signature, currentMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
- 唯一标识方法`org.apache.ibatis.reflection.Reflector#getSignature`
|
||||
|
||||
```java
|
||||
/**
|
||||
* 方法唯一标识,返回值类型#方法名称:参数列表
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private String getSignature(Method method) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (returnType != null) {
|
||||
sb.append(returnType.getName()).append('#');
|
||||
}
|
||||
sb.append(method.getName());
|
||||
Class<?>[] parameters = method.getParameterTypes();
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
sb.append(i == 0 ? ':' : ',').append(parameters[i].getName());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
- 照旧我们进行 debug 当前方法为`toString`方法
|
||||
|
||||
![1575891988804](/images/mybatis//1575891988804.png)
|
||||
|
||||
从返回结果可以看到`sb.toString`返回的是: `返回值类型#方法名`
|
||||
|
||||
![1575892046692](/images/mybatis//1575892046692.png)
|
||||
|
||||
上图返回结果为`void#setName:java.lang.String` 命名规则:`返回值类型#方法名称:参数列表`
|
||||
|
||||
回过头看看`uniqueMethods`里面是什么
|
||||
|
||||
![1575892167982](/images/mybatis//1575892167982.png)
|
||||
|
||||
方法签名:方法
|
||||
|
||||
目前完成了一部分还有一个继承问题需要debug看一下, 编写一个`Man`继承`People` 还需要实现接口
|
||||
|
||||
```java
|
||||
public class Man extends People implements TestManInterface {
|
||||
@Override
|
||||
public Integer inte() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public String hello() {
|
||||
return "hello";
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
public interface TestManInterface {
|
||||
public Integer inte();
|
||||
}
|
||||
```
|
||||
|
||||
目标明确了就直接在
|
||||
|
||||
![1575892414120](/images/mybatis//1575892414120.png)
|
||||
|
||||
这里打断点了
|
||||
|
||||
![1575892511471](/images/mybatis//1575892511471.png)
|
||||
|
||||
在进入循环之前回率先加载本类的所有可见方法
|
||||
|
||||
```java
|
||||
if (!uniqueMethods.containsKey(signature)) {
|
||||
// 如果存在该方法唯一签名则不添加
|
||||
uniqueMethods.put(signature, currentMethod);
|
||||
}
|
||||
```
|
||||
|
||||
接下来断点继续往下走
|
||||
|
||||
![1575892645405](/images/mybatis//1575892645405.png)
|
||||
|
||||
走到这一步我们来看看`currentClass.getSuperclass()`是不是上一级的类
|
||||
|
||||
![1575892687076](/images/mybatis//1575892687076.png)
|
||||
|
||||
通过断点可见这个`currentClass`现在是`People`类,根据之前所说的最终`uniqueMethods`应该存在父类的方法
|
||||
|
||||
![1575892763661](/images/mybatis//1575892763661.png)
|
||||
|
||||
可以看到父类的方法也都存在了
|
||||
|
||||
## resolveGetterConflicts
|
||||
|
||||
- `org.apache.ibatis.reflection.Reflector#resolveGetterConflicts`
|
||||
|
||||
这个方法解决了`get`方法的冲突问题,同名方法不同返回值
|
||||
|
||||
```java
|
||||
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
|
||||
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
|
||||
Method winner = null;
|
||||
String propName = entry.getKey();
|
||||
boolean isAmbiguous = false;
|
||||
for (Method candidate : entry.getValue()) {
|
||||
if (winner == null) {
|
||||
winner = candidate;
|
||||
continue;
|
||||
}
|
||||
Class<?> winnerType = winner.getReturnType();
|
||||
Class<?> candidateType = candidate.getReturnType();
|
||||
if (candidateType.equals(winnerType)) {
|
||||
if (!boolean.class.equals(candidateType)) {
|
||||
isAmbiguous = true;
|
||||
break;
|
||||
} else if (candidate.getName().startsWith("is")) {
|
||||
winner = candidate;
|
||||
}
|
||||
} else if (candidateType.isAssignableFrom(winnerType)) {
|
||||
// OK getter type is descendant
|
||||
} else if (winnerType.isAssignableFrom(candidateType)) {
|
||||
winner = candidate;
|
||||
} else {
|
||||
isAmbiguous = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
addGetMethod(propName, winner, isAmbiguous);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## addFields
|
||||
|
||||
- `org.apache.ibatis.reflection.Reflector#addFields`
|
||||
|
||||
获取类的所有字段没什么好说的直接递归就可以获取了.
|
||||
|
||||
```jade
|
||||
private void addFields(Class<?> clazz) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
if (!setMethods.containsKey(field.getName())) {
|
||||
// issue #379 - removed the check for final because JDK 1.5 allows
|
||||
// modification of final fields through reflection (JSR-133). (JGB)
|
||||
// pr #16 - final static can only be set by the classloader
|
||||
int modifiers = field.getModifiers();
|
||||
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
|
||||
addSetField(field);
|
||||
}
|
||||
}
|
||||
if (!getMethods.containsKey(field.getName())) {
|
||||
addGetField(field);
|
||||
}
|
||||
}
|
||||
if (clazz.getSuperclass() != null) {
|
||||
addFields(clazz.getSuperclass());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 属性查看
|
||||
|
||||
- 下图为一个类的解析结果
|
||||
|
||||
![1575894218362](/images/mybatis/1575894218362.png)
|
@ -0,0 +1,250 @@
|
||||
# mybatis 日志源码
|
||||
- Author: [HuiFer](https://github.com/huifer)
|
||||
- Description: 该文介绍 mybatis 日志相关源码
|
||||
## 核心类
|
||||
- `org.apache.ibatis.logging.Log`
|
||||
- `org.apache.ibatis.logging.LogFactory`
|
||||
- 多个日志实现
|
||||
- `org.apache.ibatis.logging.log4j2.Log4j2Impl`
|
||||
- `org.apache.ibatis.logging.slf4j.Slf4jLocationAwareLoggerImpl`
|
||||
- ...
|
||||
|
||||
## 源码流程
|
||||
- mybatis 提供了一个日志接口,内容如下.
|
||||
```java
|
||||
/**
|
||||
* mybatis 的日志接口,提供日志级别
|
||||
* <ol>
|
||||
* <li>error</li>
|
||||
* <li>debug</li>
|
||||
* <li>trace</li>
|
||||
* <li>warn</li>
|
||||
* </ol>
|
||||
* <p>通过自己定义的接口来实现各大日志框架的内容达到高可用</p>
|
||||
* @author Clinton Begin
|
||||
*/
|
||||
public interface Log {
|
||||
|
||||
boolean isDebugEnabled();
|
||||
|
||||
boolean isTraceEnabled();
|
||||
|
||||
void error(String s, Throwable e);
|
||||
|
||||
void error(String s);
|
||||
|
||||
void debug(String s);
|
||||
|
||||
void trace(String s);
|
||||
|
||||
void warn(String s);
|
||||
}
|
||||
```
|
||||
- 有了日志接口必然有实现类, mybatis 有`log4j2` 、 `slf4j` 等日志的相关实现 , 下面是`Slf4jImpl`的代码,其他代码也是一样的模式进行初始化就不再重复贴代码了.
|
||||
```java
|
||||
public class Slf4jImpl implements Log {
|
||||
|
||||
private Log log;
|
||||
|
||||
/**
|
||||
* 创建日志实例
|
||||
* @param clazz
|
||||
*/
|
||||
public Slf4jImpl(String clazz) {
|
||||
Logger logger = LoggerFactory.getLogger(clazz);
|
||||
|
||||
if (logger instanceof LocationAwareLogger) {
|
||||
try {
|
||||
// check for slf4j >= 1.6 method signature
|
||||
logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
|
||||
log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
|
||||
return;
|
||||
} catch (SecurityException | NoSuchMethodException e) {
|
||||
// fail-back to Slf4jLoggerImpl
|
||||
}
|
||||
}
|
||||
|
||||
// Logger is not LocationAwareLogger or slf4j version < 1.6
|
||||
log = new Slf4jLoggerImpl(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return log.isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return log.isTraceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, Throwable e) {
|
||||
log.error(s, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String s) {
|
||||
log.error(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String s) {
|
||||
log.debug(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String s) {
|
||||
log.trace(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String s) {
|
||||
log.warn(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
- 通过上述方法来达到统一接口多个实现,这个在开发中也经常使用.多日志的实现方法有了还缺一个创建方法,创建方法由`org.apache.ibatis.logging.LogFactory`提供
|
||||
```java
|
||||
|
||||
/**
|
||||
* <p>日志工厂,实现内容:</p>
|
||||
* <ol>
|
||||
* <li>org.slf4j.Logger 日志框架 slf4j</li>
|
||||
* <li>org.apache.commons.logging.Log 日志框架 apache</li>
|
||||
* <li>org.apache.logging.log4j.Logger 日志框架 log4j2</li>
|
||||
* <li>org.apache.log4j.Logger 日志框架 log4j </li>
|
||||
* <li>java.util.logging.Logger 日志框架,JDK的logger</li>
|
||||
*
|
||||
* </ol>
|
||||
* @author Clinton Begin
|
||||
* @author Eduardo Macarron
|
||||
*/
|
||||
public final class LogFactory {
|
||||
|
||||
/**
|
||||
* Marker to be used by logging implementations that support markers.
|
||||
*/
|
||||
public static final String MARKER = "MYBATIS";
|
||||
|
||||
private static Constructor<? extends Log> logConstructor;
|
||||
|
||||
/**
|
||||
* 日志的实现类的具体选择
|
||||
*/
|
||||
static {
|
||||
// slf4j 日志
|
||||
tryImplementation(LogFactory::useSlf4jLogging);
|
||||
// apache 日志
|
||||
tryImplementation(LogFactory::useCommonsLogging);
|
||||
// log4j2 日志
|
||||
tryImplementation(LogFactory::useLog4J2Logging);
|
||||
// log4 日志
|
||||
tryImplementation(LogFactory::useLog4JLogging);
|
||||
// JDK 日志
|
||||
tryImplementation(LogFactory::useJdkLogging);
|
||||
// 空 日志
|
||||
tryImplementation(LogFactory::useNoLogging);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有化构造方法,这是一个单例
|
||||
*/
|
||||
private LogFactory() {
|
||||
// disable construction
|
||||
}
|
||||
|
||||
public static Log getLog(Class<?> aClass) {
|
||||
return getLog(aClass.getName());
|
||||
}
|
||||
|
||||
public static Log getLog(String logger) {
|
||||
try {
|
||||
return logConstructor.newInstance(logger);
|
||||
} catch (Throwable t) {
|
||||
throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
|
||||
setImplementation(clazz);
|
||||
}
|
||||
|
||||
public static synchronized void useSlf4jLogging() {
|
||||
setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
|
||||
}
|
||||
|
||||
public static synchronized void useCommonsLogging() {
|
||||
setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
|
||||
}
|
||||
|
||||
public static synchronized void useLog4JLogging() {
|
||||
setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
|
||||
}
|
||||
|
||||
public static synchronized void useLog4J2Logging() {
|
||||
setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
|
||||
}
|
||||
|
||||
public static synchronized void useJdkLogging() {
|
||||
setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
|
||||
}
|
||||
|
||||
public static synchronized void useStdOutLogging() {
|
||||
setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
|
||||
}
|
||||
|
||||
public static synchronized void useNoLogging() {
|
||||
setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择具体的日志实现
|
||||
*/
|
||||
private static void tryImplementation(Runnable runnable) {
|
||||
if (logConstructor == null) {
|
||||
try {
|
||||
// run()? 似乎违背了代码的语义, 看静态方法.静态方法多行同类型的操作我认为是一个多线程
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择具体的日志实现
|
||||
*/
|
||||
private static void setImplementation(Class<? extends Log> implClass) {
|
||||
try {
|
||||
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
|
||||
Log log = candidate.newInstance(LogFactory.class.getName());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Logging initialized using '" + implClass + "' adapter.");
|
||||
}
|
||||
logConstructor = candidate;
|
||||
} catch (Throwable t) {
|
||||
throw new LogException("Error setting Log implementation. Cause: " + t, t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
- `LogFactory`是一个单例对象,对外公开`getLog`方法在使用时直接`private static final Log log = LogFactory.getLog(CglibProxyFactory.class);`即可
|
||||
|
||||
- 在 `org.apache.ibatis.session.Configuration` 中可以看到下面这些注册方法
|
||||
```java
|
||||
// 日志实现类
|
||||
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
|
||||
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
|
||||
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
|
||||
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
|
||||
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
|
||||
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
|
||||
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
|
||||
|
||||
```
|
@ -0,0 +1,202 @@
|
||||
# MapperMethod
|
||||
- Author: [HuiFer](https://github.com/huifer)
|
||||
- Description: 该文介绍 mybatis MapperMethod 源码
|
||||
- 源码地址: `org.apache.ibatis.binding.MapperMethod`,核心方法是`execute`
|
||||
|
||||
```java
|
||||
/**
|
||||
* CRUD 不同的执行处理
|
||||
*
|
||||
* @param sqlSession
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public Object execute(SqlSession sqlSession, Object[] args) {
|
||||
Object result;
|
||||
switch (command.getType()) {
|
||||
case INSERT: {
|
||||
Object param = method.convertArgsToSqlCommandParam(args);
|
||||
result = rowCountResult(sqlSession.insert(command.getName(), param));
|
||||
break;
|
||||
}
|
||||
case UPDATE: {
|
||||
Object param = method.convertArgsToSqlCommandParam(args);
|
||||
result = rowCountResult(sqlSession.update(command.getName(), param));
|
||||
break;
|
||||
}
|
||||
case DELETE: {
|
||||
Object param = method.convertArgsToSqlCommandParam(args);
|
||||
result = rowCountResult(sqlSession.delete(command.getName(), param));
|
||||
break;
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
/**
|
||||
* 根据 resultHandler 进行处理
|
||||
*
|
||||
* @param sqlSession
|
||||
* @param args
|
||||
*/
|
||||
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
|
||||
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
|
||||
if (!StatementType.CALLABLE.equals(ms.getStatementType())
|
||||
&& void.class.equals(ms.getResultMaps().get(0).getType())) {
|
||||
throw new BindingException("method " + command.getName()
|
||||
+ " needs either a @ResultMap annotation, a @ResultType annotation,"
|
||||
+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
|
||||
}
|
||||
Object param = method.convertArgsToSqlCommandParam(args);
|
||||
// 判断是否有 RowBounds
|
||||
if (method.hasRowBounds()) {
|
||||
RowBounds rowBounds = method.extractRowBounds(args);
|
||||
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
|
||||
} else {
|
||||
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
- 返回值为多个的情况
|
||||
```java
|
||||
/**
|
||||
* 针对多个查询结果进行 ,转换成不同的 list 或者数组
|
||||
*
|
||||
* @param sqlSession
|
||||
* @param args
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
|
||||
List<E> result;
|
||||
Object param = method.convertArgsToSqlCommandParam(args);
|
||||
if (method.hasRowBounds()) {
|
||||
RowBounds rowBounds = method.extractRowBounds(args);
|
||||
// 直接 list
|
||||
result = sqlSession.selectList(command.getName(), param, rowBounds);
|
||||
} else {
|
||||
result = sqlSession.selectList(command.getName(), param);
|
||||
}
|
||||
// issue #510 Collections & arrays support
|
||||
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
|
||||
if (method.getReturnType().isArray()) {
|
||||
// 转换成 array
|
||||
return convertToArray(result);
|
||||
} else {
|
||||
// 转换成 collection
|
||||
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### convertToArray
|
||||
```java
|
||||
/**
|
||||
* 转换为数组
|
||||
*
|
||||
* @param list 数据库查询结果
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private <E> Object convertToArray(List<E> list) {
|
||||
// 获取返回类型
|
||||
Class<?> arrayComponentType = method.getReturnType().getComponentType();
|
||||
// new 一个 array
|
||||
Object array = Array.newInstance(arrayComponentType, list.size());
|
||||
if (arrayComponentType.isPrimitive()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Array.set(array, i, list.get(i));
|
||||
}
|
||||
return array;
|
||||
} else {
|
||||
// 通过 toArray方法转换
|
||||
return list.toArray((E[]) array);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
### convertToDeclaredCollection
|
||||
```java
|
||||
/**
|
||||
* 转换为不同的list对象
|
||||
*
|
||||
* @param config
|
||||
* @param list 数据库查询结果
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
|
||||
// mybatis ObjectFactory 创建mapper 的返回结果对象
|
||||
Object collection = config.getObjectFactory().create(method.getReturnType());
|
||||
MetaObject metaObject = config.newMetaObject(collection);
|
||||
// metaObject.objectWrapper => CollectionWrapper
|
||||
// MetaObject 对象的 objectWrapper 现在是 CollectionWrapper 它是 Collection 的包装
|
||||
metaObject.addAll(list);
|
||||
return collection;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
- 上述两个为转换的过程,其实质还是在 `org.apache.ibatis.session.SqlSession` 中做执行操作
|
||||
|
||||
|
||||
|
||||
## debug
|
||||
- 修改mapper返回数组对`org.apache.ibatis.binding.MapperMethod#convertToArray`方法进行测试
|
||||
```java
|
||||
HsSell[] list(@Param("ID") Integer id);
|
||||
```
|
||||
|
||||
![image-20191219092442456](/images/mybatis/image-20191219092442456.png)
|
||||
|
||||
- 修改mapper,对`org.apache.ibatis.binding.MapperMethod#convertToDeclaredCollection`进行测试
|
||||
|
||||
```java
|
||||
LinkedList<HsSell> list(@Param("ID") Integer id);
|
||||
```
|
||||
|
||||
|
||||
|
||||
![image-20191219093043035](/images/mybatis/image-20191219093043035.png)
|
||||
|
||||
|
||||
|
@ -0,0 +1,178 @@
|
||||
# MethodSignature
|
||||
- Author: [HuiFer](https://github.com/huifer)
|
||||
- Description: 该文介绍 mybatis MethodSignature 类
|
||||
- `org.apache.ibatis.binding.MapperMethod.MethodSignature`
|
||||
```java
|
||||
/**
|
||||
* 方法签名
|
||||
*/
|
||||
public static class MethodSignature {
|
||||
|
||||
/**
|
||||
* 返回值是否多个
|
||||
*/
|
||||
private final boolean returnsMany;
|
||||
/**
|
||||
* 返回值是不是map
|
||||
*/
|
||||
private final boolean returnsMap;
|
||||
/**
|
||||
* 返回值是否 void
|
||||
*/
|
||||
private final boolean returnsVoid;
|
||||
/**
|
||||
* 返回的是否是一个游标
|
||||
*/
|
||||
private final boolean returnsCursor;
|
||||
/**
|
||||
* 返回值是否是 optional
|
||||
*/
|
||||
private final boolean returnsOptional;
|
||||
/**
|
||||
* 返回类型
|
||||
*/
|
||||
private final Class<?> returnType;
|
||||
/**
|
||||
* map key
|
||||
*/
|
||||
private final String mapKey;
|
||||
private final Integer resultHandlerIndex;
|
||||
private final Integer rowBoundsIndex;
|
||||
/**
|
||||
* 参数解析
|
||||
*/
|
||||
private final ParamNameResolver paramNameResolver;
|
||||
|
||||
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
|
||||
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
|
||||
if (resolvedReturnType instanceof Class<?>) {
|
||||
this.returnType = (Class<?>) resolvedReturnType;
|
||||
} else if (resolvedReturnType instanceof ParameterizedType) {
|
||||
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
|
||||
} else {
|
||||
this.returnType = method.getReturnType();
|
||||
}
|
||||
this.returnsVoid = void.class.equals(this.returnType);
|
||||
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
|
||||
this.returnsCursor = Cursor.class.equals(this.returnType);
|
||||
this.returnsOptional = Optional.class.equals(this.returnType);
|
||||
this.mapKey = getMapKey(method);
|
||||
this.returnsMap = this.mapKey != null;
|
||||
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
|
||||
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
|
||||
this.paramNameResolver = new ParamNameResolver(configuration, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法主要是把方法参数转换为SQL命令参数。
|
||||
*
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public Object convertArgsToSqlCommandParam(Object[] args) {
|
||||
return paramNameResolver.getNamedParams(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有 {@link RowBounds}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasRowBounds() {
|
||||
return rowBoundsIndex != null;
|
||||
}
|
||||
|
||||
public RowBounds extractRowBounds(Object[] args) {
|
||||
return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否uresultHandler
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasResultHandler() {
|
||||
return resultHandlerIndex != null;
|
||||
}
|
||||
|
||||
public ResultHandler extractResultHandler(Object[] args) {
|
||||
return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
|
||||
}
|
||||
|
||||
public String getMapKey() {
|
||||
return mapKey;
|
||||
}
|
||||
|
||||
public Class<?> getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public boolean returnsMany() {
|
||||
return returnsMany;
|
||||
}
|
||||
|
||||
public boolean returnsMap() {
|
||||
return returnsMap;
|
||||
}
|
||||
|
||||
public boolean returnsVoid() {
|
||||
return returnsVoid;
|
||||
}
|
||||
|
||||
public boolean returnsCursor() {
|
||||
return returnsCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* return whether return type is {@code java.util.Optional}.
|
||||
*
|
||||
* @return return {@code true}, if return type is {@code java.util.Optional}
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public boolean returnsOptional() {
|
||||
return returnsOptional;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数名
|
||||
* {@link RowBounds}
|
||||
*
|
||||
* @param method mapper 方法
|
||||
* @param paramType
|
||||
* @return
|
||||
*/
|
||||
private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
|
||||
Integer index = null;
|
||||
// 获取参数类型
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
if (paramType.isAssignableFrom(argTypes[i])) {
|
||||
if (index == null) {
|
||||
index = i;
|
||||
} else {
|
||||
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 {@link MapKey} 注解数据
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
private String getMapKey(Method method) {
|
||||
String mapKey = null;
|
||||
if (Map.class.isAssignableFrom(method.getReturnType())) {
|
||||
final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
|
||||
if (mapKeyAnnotation != null) {
|
||||
mapKey = mapKeyAnnotation.value();
|
||||
}
|
||||
}
|
||||
return mapKey;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@ -0,0 +1,104 @@
|
||||
# sqlCommand
|
||||
- Author: [HuiFer](https://github.com/huifer)
|
||||
- Description: 该文介绍 mybatis sqlCommand类的源码
|
||||
|
||||
- `org.apache.ibatis.binding.MapperMethod.SqlCommand`
|
||||
```java
|
||||
/**
|
||||
* 核心内容: sql id , Sql 类型
|
||||
*/
|
||||
public static class SqlCommand {
|
||||
|
||||
/**
|
||||
* sql id
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* sql 类型select|update|delete|insert|...
|
||||
*/
|
||||
private final SqlCommandType type;
|
||||
|
||||
/**
|
||||
* 根据传递的参数 设置sql的一些属性 , sql id , type .
|
||||
*
|
||||
* @param configuration
|
||||
* @param mapperInterface
|
||||
* @param method
|
||||
*/
|
||||
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
|
||||
// 方法名
|
||||
final String methodName = method.getName();
|
||||
final Class<?> declaringClass = method.getDeclaringClass();
|
||||
// Statement 实质是sql
|
||||
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
|
||||
configuration);
|
||||
if (ms == null) {
|
||||
if (method.getAnnotation(Flush.class) != null) {
|
||||
name = null;
|
||||
type = SqlCommandType.FLUSH;
|
||||
} else {
|
||||
throw new BindingException("Invalid bound statement (not found): "
|
||||
+ mapperInterface.getName() + "." + methodName);
|
||||
}
|
||||
} else {
|
||||
name = ms.getId();
|
||||
type = ms.getSqlCommandType();
|
||||
if (type == SqlCommandType.UNKNOWN) {
|
||||
throw new BindingException("Unknown execution method for: " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SqlCommandType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapperInterface mapper.class
|
||||
* @param methodName 方法名
|
||||
* @param declaringClass 可能是 mapper.class
|
||||
* @param configuration
|
||||
* @return
|
||||
*/
|
||||
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
|
||||
Class<?> declaringClass, Configuration configuration) {
|
||||
// 接口名称+方法名
|
||||
String statementId = mapperInterface.getName() + "." + methodName;
|
||||
if (configuration.hasStatement(statementId)) {
|
||||
// 从 configuration 获取
|
||||
return configuration.getMappedStatement(statementId);
|
||||
} else if (mapperInterface.equals(declaringClass)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// new 一个新的实例
|
||||
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
|
||||
if (declaringClass.isAssignableFrom(superInterface)) {
|
||||
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
|
||||
declaringClass, configuration);
|
||||
if (ms != null) {
|
||||
return ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
![image-20191218191512184](/images/mybatis/image-20191218191512184.png)
|
||||
|
||||
|
||||
|
||||
![image-20191218191550550](/images/mybatis/image-20191218191550550.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 75 KiB |