动态代理在mybatis接口式编程中的应用

来源:互联网 发布:提高智商知乎 编辑:程序博客网 时间:2024/06/12 01:42

上一篇文章着重介绍了jdk动态代理的实现和简要介绍了其实现原理,这一篇文章来介绍一下动态代理在mybatis接口式编程中的应用,希望自己以后也能有机会去写写架构大笑

mybatis通过一个配置文件和一个接口就可以完成sql语句的具体执行,其过程如下:

1.编写一个提供执行SQL的接口

import java.util.List;import java.util.Map;import com.imooc.bean.Member;/** * 与member配置文件相对应的接口 * @author user * */public interface IMember {/** * 根据查询条件查询消息列表 */public List<Member> queryMemberList(Map map);/** * 根据查询条件查询消息列表的条数 */public int count(Member member);/** * 根据查询条件分页查询消息列表 */public List<Member> queryMemberListByPage(Map<String,Object> parameter);}

2.编写供上面接口引用的SQL配置文件

<?xml version="1.0" encoding="UTF-8"?><!--       Copyright 2009-2012 the original author or authors.       Licensed under the Apache License, Version 2.0 (the "License");       you may not use this file except in compliance with the License.       You may obtain a copy of the License at          http://www.apache.org/licenses/LICENSE-2.0       Unless required by applicable law or agreed to in writing, software       distributed under the License is distributed on an "AS IS" BASIS,       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.       See the License for the specific language governing permissions and       limitations under the License.--><!DOCTYPE mapper    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"    "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.dao.IMember">  <resultMap type="com.bean.Member" id="MemberResult">    <id column="MEMBER_ID" jdbcType="BIGINT" property="id"/>    <result column="FULL_NAME" jdbcType="NVARCHAR" property="fullName"/>    <result column="DATE_OF_BIRTH" jdbcType="DATE" property="dateOfBirth"/>  </resultMap>    <select id="queryMemberList" parameterType = "java.util.Map" resultMap="MemberResult">    SELECT TOP ${page.dbNumber} MEMBER_ID,FULL_NAME,DATE_OF_BIRTH FROM     (SELECT ROW_NUMBER() OVER(ORDER BY MEMBER_ID) AS ROWNUMBER,MEMBER_ID,FULL_NAME,DATE_OF_BIRTH     FROM MEMBER    <where>    <if test="member.fullName != null and !"".equals(member.fullName.trim())">AND FULL_NAME = #{member.fullName}</if>    </where>    ) TABLE_MEMBER WHERE ROWNUMBER > ${page.dbIndex}  </select>    <select id="count"  parameterType="com.bean.Member" resultType="int">  SELECT COUNT(*) FROM MEMBER    <where>    <if test="fullName != null and !"".equals(fullName.trim())">AND FULL_NAME LIKE #{fullName}</if>    </where>  </select>    <select id="queryMemberListByPage" parameterType="java.util.Map" resultMap="MemberResult">    SELECT MEMBER_ID,FULL_NAME,DATE_OF_BIRTH FROM MEMBER    <where>    <if test="member.fullName != null and !"".equals(member.fullName.trim())">AND FULL_NAME LIKE #{member.fullName}</if>    </where>    ORDER BY MEMBER_ID  </select>    <delete id="deleteOne" parameterType="int">    DELETE FROM MEMBER WHERE id = #{_parameter}  </delete>    <delete id="deleteBatch" parameterType="java.util.List">    DELETE FROM MEMBER WHERE 1 = 1 AND MEMBER_ID IN (<foreach collection="list" item="item" separator=",">       #{item}       </foreach>   )  </delete></mapper>

3.DAO方法调用接口方法执行SQL

/** * 根据查询条件查询消息列表 */public List<Member> queryMemberList(Map<String,Object> parameter) {DBAccess dbAccess = new DBAccess();List<Member> memberList = new ArrayList<Member>();SqlSession sqlSession = null;try {sqlSession = dbAccess.getSqlSession();// 通过sqlSession执行SQL语句IMember imember = sqlSession.getMapper(IMember.class);memberList = imember.queryMemberList(parameter);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if(sqlSession != null) {sqlSession.close();}}return memberList;}

DAO调用方法在执行sqlSession.getMapper(IMember.class);这句时,mybatis实际帮我们返回了一个IMember接口的具体实例,这里被代理方就是sqlSession,而代理类的类型就是IMember(由参数指定)

1.mybatis在解析配置文件时,根据namespace就可以知道哪些接口可以生成代理对象,并且为这些接口生成一个map,map的key就是具体的class(本例中为IMember.class),value是一个MapperProxyFactory<T>的一个实例,这个实例有一个Class<T>类型的属性(本例中为IMember.class)并且实现了InvocationHandler接口

2.通过跟踪mybatis的源码(下面按顺序列出生成代理实例过程中流转到的语句),发现最终生成代理类实例的语句为下面的第(7)句

(1).sqlSession.getMapper(IMember.class);

(2).configuration.<T>getMapper(type, this);//这里this就是sqlSession

(3).mapperRegistry.getMapper(type, sqlSession);

(4).final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);

(5).mapperProxyFactory.newInstance(sqlSession);

(6). final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);//MapperProxy<T>实现了InvocationHandler接口

(7).(T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);

这里需要注意的是,sqlSession作为被代理对象并没有实现IMember接口,也没有定义任何与IMember相同的方法,但是当我们执行代理类对象的任何方法时,sqlSession都可以被调用。这是因为MapperProxy<T>(可以植入代理行为的InvocationHandler实现)类的invoke方法中并没有使用反射来直接触发被代理方(IMember)方法的执行,而是通过解析传入的被代理方法对象(Method参数)重新分析sqlSession对象完成SQL的执行,而sqlSession作为MapperProxy<T>类的属性,在其执行invoke方法时被直接引用。这就是上一篇文章中结尾处提到了,当被代理对象(目标对象)没有实现jdk要求的接口,但我们仍希望jdk可以帮我们返回一个接口的代理实现对象时,就需要在InvocationHandler实现类的invoke方法中直接调用被代理对象需要执行的方法而不能通过反射来触发,此处被代理对象(目标对象)和InvocationHandler的实现类是耦合的。




0 0
原创粉丝点击