Mybatis的关键注解@Param源码分析
哈哈,又来吹牛批了,现在要出去找工作很多的都离不开Mybatis框架的使用,并且@Param注解的使用就格外的重要,可以指定sql注入是参数的键就是参数的名称了,但是你可能对Mybatis的内部怎么运行好奇,那让我们来看一看@Param的源码内部是怎么运行的
点击进入@Param注解如下,这就是一个标志而已
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Param {
String value();
}
//这就是一个代理对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//这里就是一个三元表达式判断Object与method.getDeclaringClass()的类地址是否相同
//所以走第二条分支cachedInvoker方法惊醒一定处理
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
//执行invoke(proxy, method, args, this.sqlSession);这个方法是被MapperProxy类重写了,所以调用了invoke(proxy, method, args, this.sqlSession)方法
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
//这个方法就是返回一个类的对象
public Class<?> getDeclaringClass() {
return clazz;
}
cachedInvoker方法
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
//执行如果不存在,计算的方法,看一下里面在计算什么,this.methodCache是方法的缓存就是使用过的sql语句缓存在这里节约内存使用
//返回所有value的值,返回都不是默认值,返回到前一个方法
return (MapperMethodInvoker)MapUtil.computeIfAbsent(this.methodCache, method, (m) -> {
if (m.isDefault()) {
try {
return privateLookupInMethod == null ? new DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new DefaultMethodInvoker(this.getMethodHandleJava9(method));
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
throw new RuntimeException(var4);
}
} else {
return new PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
}
});
} catch (RuntimeException var4) {
Throwable cause = var4.getCause();
throw (Throwable)(cause == null ? var4 : cause);
}
}
computeIfAbsent 方法
public class MapUtil {
public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
V value = map.get(key);
if (value != null) {
return value;
} else {
mappingFunction.getClass();
return map.computeIfAbsent(key, mappingFunction::apply);
}
}
PlainMethodInvoker是MapperProxy类的静态内部类,是纯方法调用器
private static class PlainMethodInvoker implements MapperMethodInvoker {
//这里定义了所有的增删查改的所有方法
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
this.mapperMethod = mapperMethod;
}
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
//这个方法使用了sql会话并且调用了his.mapperMethod.execute(sqlSession, args)方法
return this.mapperMethod.execute(sqlSession, args);
}
}
private final MapperMethod mapperMethod;
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
}
MapperMethod类如下
//这个类就是对xml映射文件的一些参数的收集
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public class MapperMethod {
//getMapper就是创建出来的
private final SqlCommand command;
//mapper代理类传过来的
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;
Object param;
switch (this.command.getType()) {
//
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
private Object rowCountResult(int rowCount) {
Object result;
if (this.method.returnsVoid()) {
result = null;
} else if (!Integer.class.equals(this.method.getReturnType()) && !Integer.TYPE.equals(this.method.getReturnType())) {
if (!Long.class.equals(this.method.getReturnType()) && !Long.TYPE.equals(this.method.getReturnType())) {
if (!Boolean.class.equals(this.method.getReturnType()) && !Boolean.TYPE.equals(this.method.getReturnType())) {
throw new BindingException("Mapper method '" + this.command.getName() + "' has an unsupported return type: " + this.method.getReturnType());
}
result = rowCount > 0;
} else {
result = (long)rowCount;
}
} else {
result = rowCount;
}
return result;
}
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(this.command.getName());
if (!StatementType.CALLABLE.equals(ms.getStatementType()) && Void.TYPE.equals(((ResultMap)ms.getResultMaps().get(0)).getType())) {
throw new BindingException("method " + this.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.");
} else {
Object param = this.method.convertArgsToSqlCommandParam(args);
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
sqlSession.select(this.command.getName(), param, rowBounds, this.method.extractResultHandler(args));
} else {
sqlSession.select(this.command.getName(), param, this.method.extractResultHandler(args));
}
}
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
Object param = this.method.convertArgsToSqlCommandParam(args);
List result;
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectList(this.command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(this.command.getName(), param);
}
if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
} else {
return result;
}
}
private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
Object param = this.method.convertArgsToSqlCommandParam(args);
Cursor result;
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectCursor(this.command.getName(), param, rowBounds);
} else {
result = sqlSession.selectCursor(this.command.getName(), param);
}
return result;
}
private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
Object collection = config.getObjectFactory().create(this.method.getReturnType());
MetaObject metaObject = config.newMetaObject(collection);
metaObject.addAll(list);
return collection;
}
private <E> Object convertToArray(List<E> list) {
Class<?> arrayComponentType = this.method.getReturnType().getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (!arrayComponentType.isPrimitive()) {
return list.toArray((Object[])((Object[])array));
} else {
for(int i = 0; i < list.size(); ++i) {
Array.set(array, i, list.get(i));
}
return array;
}
}
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Object param = this.method.convertArgsToSqlCommandParam(args);
Map result;
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectMap(this.command.getName(), param, this.method.getMapKey(), rowBounds);
} else {
result = sqlSession.selectMap(this.command.getName(), param, this.method.getMapKey());
}
return result;
}
public static class SqlCommand {
private final String name;
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String methodName = method.getName();
Class<?> declaringClass = method.getDeclaringClass();
MappedStatement ms = this.resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) == null) {
throw new BindingException("Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName);
}
this.name = null;
this.type = SqlCommandType.FLUSH;
} else {
this.name = ms.getId();
this.type = ms.getSqlCommandType();
if (this.type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + this.name);
}
}
}
public String getName() {
return this.name;
}
public SqlCommandType getType() {
return this.type;
}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
} else {
Class[] var6 = mapperInterface.getInterfaces();
int var7 = var6.length;
for(int var8 = 0; var8 < var7; ++var8) {
Class<?> superInterface = var6[var8];
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
}
public static class ParamMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -2212268410512043556L;
public ParamMap() {
}
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());
} else {
return super.get(key);
}
}
}
}
这是关于传过来的的参数,对参数进行处理的静态内部类MethodSignature方法
public static class MethodSignature {
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final boolean returnsOptional;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
//定义了一个参数解析器的类
private final ParamNameResolver paramNameResolver;
public Object convertArgsToSqlCommandParam(Object[] args) {
//这里调用了参数解析器的一个方法
return this.paramNameResolver.getNamedParams(args);
}
}
这是一个ParamNameResolver类的方法,这里就是参数赋值的地方
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public class ParamNameResolver {
//这里定义了一个参数的前缀,param,所以参数会有key是带param的了
public static final String GENERIC_NAME_PREFIX = "param";
private final boolean useActualParamName;
//sql传过来参数的名字
private final SortedMap<Integer, String> names;
//这里是判断是否使用了@Param注解注释
private boolean hasParamAnnotation;
public ParamNameResolver(Configuration config, Method method) {
this.useActualParamName = config.isUseActualParamName();
Class<?>[] paramTypes = method.getParameterTypes();
Annotation[][] paramAnnotations = method.getParameterAnnotations();
SortedMap<Integer, String> map = new TreeMap();
int paramCount = paramAnnotations.length;
for(int paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
if (!isSpecialParameter(paramTypes[paramIndex])) {
String name = null;
Annotation[] var9 = paramAnnotations[paramIndex];
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
Annotation annotation = var9[var11];
if (annotation instanceof Param) {
this.hasParamAnnotation = true;
name = ((Param)annotation).value();
break;
}
}
if (name == null) {
if (this.useActualParamName) {
name = this.getActualParamName(method, paramIndex);
}
if (name == null) {
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
}
this.names = Collections.unmodifiableSortedMap(map);
}
private String getActualParamName(Method method, int paramIndex) {
return (String)ParamNameUtil.getParamNames(method).get(paramIndex);
}
private static boolean isSpecialParameter(Class<?> clazz) {
return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
}
public String[] getNames() {
return (String[])this.names.values().toArray(new String[0]);
}
//Object[] args 注入过来的sql的真实的值
public Object getNamedParams(Object[] args) {
//对传过来的参数进行赋值
int paramCount = this.names.size();
if (args != null && paramCount != 0) {
if (!this.hasParamAnnotation && paramCount == 1) {
//得到sql的值
Object value = args[(Integer)this.names.firstKey()];
return wrapToMapIfCollection(value, this.useActualParamName ? (String)this.names.get(0) : null);
} else {
Map<String, Object> param = new MapperMethod.ParamMap();
int i = 0;
for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
Map.Entry<Integer, String> entry = (Map.Entry)var5.next();
//如果没有使用@Param注解的话,默认传过来的键位 argx
//这里就是@Param注解传过来的键和@Param传过来的值
param.put(entry.getValue(), args[(Integer)entry.getKey()]);
String genericParamName = "param" + (i + 1);
if (!this.names.containsValue(genericParamName)) {
//这里是paramx键 和param的值 所以有两对
param.put(genericParamName, args[(Integer)entry.getKey()]);
}
}
return param;
}
} else {
return null;
}
}
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
MapperMethod.ParamMap map;
if (object instanceof Collection) {
map = new MapperMethod.ParamMap();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
Optional.ofNullable(actualParamName).ifPresent((name) -> {
map.put(name, object);
});
return map;
} else if (object != null && object.getClass().isArray()) {
map = new MapperMethod.ParamMap();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent((name) -> {
map.put(name, object);
});
return map;
} else {
return object;
}
}
}