Mapper 接口动态代理实现原理
public class MyMapperProxy<T> implements InvocationHandler {
private Class<T> mapperInterface;
private SqlSession sqlSession;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 针对不同的 sql 类型,需要调用 sqlSession 的不同方法
// 接口中的参数也有很多情况,这里只考虑没有参数的情况
List<T> result = sqlSession.selectList(mapperInterface.getCanonicalName()
+ "." + method.getName());
// 返回值也有很多情况,这里不做处理直接返回
return result;
}
}
从上面的代理类中可以看到,当调用一个接口的方法时,会先通过接口的全限定名称和当前调用的方法名的组合得到一个方法 id,这个 id 的值就是映射 XML 中 namespace 和具体方法 id 的组合。
该代理类的单元测试方法如下所示:
import com.study.mybatis.config.SpringConfig;
import com.study.mybatis.entity.Country;
import com.study.mybatis.mapper.CountryMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
import java.lang.reflect.Proxy;
import java.util.List;
/**
* @author Zhang B H
* @create 2023-09-30 19:11
*/
@ActiveProfiles("qa")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DynamicProxyTest {
@Autowired
private SqlSessionFactory sqlSessionFactory;
private SqlSession sqlSession;
@Before
public void setUp() throws Exception {
sqlSession = sqlSessionFactory.openSession();
}
@Test
public void test() {
DynamicProxy.MyMapperProxy<CountryMapper> countryMapperProxyHandler = new DynamicProxy.MyMapperProxy<>();
countryMapperProxyHandler.setMapperInterface(CountryMapper.class);
countryMapperProxyHandler.setSqlSession(sqlSession);
CountryMapper countryMapper = (CountryMapper) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class<?>[]{CountryMapper.class},
countryMapperProxyHandler
);
List<Country> countries = countryMapper.selectAll();
Assert.notEmpty(countries, "");
}
@After
public void tearDown() throws Exception {
sqlSession.close();
}
}
Last updated