在学习mybatis的时候,发现了一个有趣的现象
如果resultType所指向的bean没有set方法时 在ibatis上是不能成功并且报错误 但在mybatis却是可以的
bean类:

sqlmap如下:

测试类代码如下:
- @Test
- public void testNewTable() throws IOException{
- String resource = "sqlMap_config.xml";
- Reader reader = Resources.getResourceAsReader(resource);
- SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
- SqlSessionFactory factory = builder.build(reader);
-
- SqlSession session = factory.openSession();
- List<User> list = session.selectList("NewTable.selectAll");
-
- System.out.println( list.get(0) );
-
- }
这样User类的属性居然是有值的!!!!
这着实让我很诧异 在排除了是我操作的问题的情况下,最终的造成此现象的根源在于mybatis本身。
于是我有了想看mybatis源码的冲动。。。。
2.分析问题
说实话,这是我第一次正儿八经的看框架源码。
一个字:杂啊!!!
但杂而不乱,我debug了好久才找到了根源
类SetFieldInvoke
- public class SetFieldInvoker implements Invoker {
- private Field field;
-
- public SetFieldInvoker(Field field) {
- this.field = field;
- }
-
- public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
- field.set(target, args[0]);
- return null;
- }
-
- public Class<?> getType() {
- return field.getType();
- }
- }
- field.set(target, args[0]);
原来它利用的是反射原理直接操作属性而非set方法
但有一个问题出现了 User类中的属性都是private的。如果 Field 类中没有setAccessible(true)的话,是不会访问到私有属性的。
在SetFieldInvoke方法中并没有执行此方法啊,但结果确实是给私有属性赋值了。
可以说明一点是setAccessible(true)此方法 肯定是执行了。
剩下的问题是此方法在哪里执行了??
于是我搜索文本setAccessible(true)
果然在org.apache.ibatis.reflection.Reflector 中 发现了此方法在调用
- private void addFields(Class<?> clazz) {
- Field[] fields = clazz.getDeclaredFields();
- for (Field field : fields) {
- if (canAccessPrivateMethods()) {
- try {
- field.setAccessible(true);
- } catch (Exception e) {
-
- }
- }
- if (field.isAccessible()) {
- if (!setMethods.containsKey(field.getName())) {
-
-
- addSetField(field);
- }
- if (!getMethods.containsKey(field.getName())) {
- addGetField(field);
- }
- }
- }
- if (clazz.getSuperclass() != null) {
- addFields(clazz.getSuperclass());
- }
- }
简单来说,就是在初始化的时候就将每个属性都
setAccessible(true)
了