MyBatis的Mapper代理原理

Mapper代理模式产生的原因:
由于使用MyBatis框架时Mapper接口实现类中存在代码重复,整个操作过程模板重复(加载配置文件/创建SqlSessionFactory/生产SqlSession),故MyBatis推出了基于JDK动态代理自动生成代理Mapper接口实现类,代理类可以完成Mapper中定义的增删改查操作,避免代码重复工作。

MapperRegistry加载原理

在调用SqlSession.getMapper()方法时可以发现底层调用的是mapperRegistry.getMapper(type, sqlSession);那MapperRegistry又是什么呢?MapperRegistry封装了全局配置文件Configuration和
Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
knownMappers 的key是全部加载的Mapper接口,value是每个Mapper接口对于的代理类生成工厂。下面看看MapperRegistry是如何加载的:

1.进入XMLConfigBuilder的mapperXML解析方法mapperElement(XNode parent)方法,在mapper标签为package或者class格式是配置的为Mapper接口的包路径或Mapper接口的全限定名

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        // 扫描Mapper接口方法   
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
         // 扫描Mapper接口方法                   this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

2.进入configuration.addMapper(type)发现调用的是mapperRegistry.addMapper(type)

public <T> void addMapper(Class<T> type) {
		//判断是否是接口
        if (type.isInterface()) {
        	// 判断是否已经扫描过了
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
            	// 为当前Mapper接口创建代理类生成工厂
                this.knownMappers.put(type, new MapperProxyFactory(type));
                // 扫描当前Mapper接口中的注解,并解析后封装到核心配置类Configuration中
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
            // 如果解析注解过程中报错则剔除当前Mapper接口及其代理类生成工厂
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }

3.进入new MapperProxyFactory(type)代理类生成工厂的构造

	// mapper接口类型
 	private final Class<T> mapperInterface;
 	// 已执行方法缓存
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

总结:MapperRegistry在解析Mapper标签是初始化并封装

SqlSession.getMapper()方法原理

1.底层调用的mapperRegistry.getMapper(type, sqlSession);

 	private final Configuration config;
 	// 
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
            // 取到mapper对应的代理类生成工厂后,调用newInstance方法获取mapper代理类的实例
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

2.进入newInstance(sqlSession);方法

MapperProxyFactory.class

private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

protected T newInstance(MapperProxy<T> mapperProxy) {
		//根据JDK动态代理生成mapper的代理类返回
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

  public T newInstance(SqlSession sqlSession) {
  		// MapperProxy实现了InvocationHandler接口的invoke
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        // 调用重载方法
        return this.newInstance(mapperProxy);
    }

代理类方法执行原理(invoke方法剖析)

1.进入MapperProxy类

MapperProxy.class


	// 当前调用getMapper方法的SqlSession
    private final SqlSession sqlSession;
    // 被执行方法所属Mapper接口的类型
    private final Class<T> mapperInterface;
    // 当前mapper已执行方法缓存
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
	        // 被执行方法是否Object的方法如toString/equals等,如果是直接执行原方法
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
			//是否构造方法
            if (this.isDefaultMethod(method)) {
                return this.invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable var5) {
            throw ExceptionUtil.unwrapThrowable(var5);
        }
		// 获取当前方法相关参数
        MapperMethod mapperMethod = this.cachedMapperMethod(method);
        // 执行execute方法
        return mapperMethod.execute(this.sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

2.进入mapperMethod.execute(this.sqlSession, args);方法,根据方法的相关参数及类型判断执行sqlSession的增删改查方法。

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        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);
            }
            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;
        }
    }```

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

躺平程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值