jdk8中接口默认方法,以及在Mybatis中MapperProxy的应用

本文深入探讨了Java 8引入的默认方法概念,解释了其如何实现接口更新的同时保持与旧版本代码的兼容性。文章详细分析了默认方法的编译原理,展示了其在Mybatis框架中的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

默认方法的介绍
自jdk8开始接口中可以声明default方法。
oracle官网jdk各个版本更新信息的地址:https://docs.oracle.com/en/java/javase/index.html

jdk8中对新增的默认方法的介绍:官网介绍连接
Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.

默认方法允许将新功能添加到库的接口,并确保与为这些接口的较早版本编写的代码二进制兼容。

默认方法的原理
default声明的方法编译后实际上是public修饰的非abstract方法

//声明接口
public interface Java8interface {

    int num = 100;

    void Method();

    default void defaultMethod() {
        System.out.println("I am default method!");
    }

}

//反编译结果 
//默认修饰符
//方法:public abstract 
//默认方法:public 
//属性: public static final
public interface Java8interface
{

    public abstract void Method();

    public void defaultMethod()
    {
        System.out.println("I am default method!");
    }

    public static final int num = 100;
}

Method类中对default方法的判断代码

/oracle jdk1.8 Method类中isDefault()方法,判断是否是接口中的默认方法
public boolean isDefault() {
    
    // Default methods are public non-abstract instance methods
    // declared in an interface.
    // 默认方法就是在接口中声明的 public 非 abastract 的实例方法
    
    //运算过程
    //((getModifiers() & (0b000110010000 | 0b0001 | 0b1000)) == 0b0001)
    //(getModifiers() & 0b000110011001) == 0b0001)
    //逻辑与运算结果:如果方法修饰符是public(0b0001) 返回true,否则返回false
    return ((getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) ==
            Modifier.PUBLIC) && getDeclaringClass().isInterface();
    
}

//Modifier类常见的修饰符对应的int值
//public
public static final int PUBLIC           = 0x00000001;//0b0001
//private
public static final int PRIVATE          = 0x00000002;//0b0010
//protected
public static final int PROTECTED        = 0x00000004;//0b0100
//static
public static final int STATIC           = 0x00000008;//0b1000
//final
public static final int FINAL            = 0x00000010;//0b1010
//synchronized
public static final int SYNCHRONIZED     = 0x00000020;//0b00010100
//volatile
public static final int VOLATILE         = 0x00000040;//0b00101000
//transient
public static final int TRANSIENT        = 0x00000080;//0b01010000
//native
public static final int NATIVE           = 0x00000100;//0b01100100
//interface
public static final int INTERFACE        = 0x00000200;//0b11001000
//abstract
public static final int ABSTRACT         = 0x00000400;//0b000110010000
//strictfp
public static final int STRICT           = 0x00000800;//0b001100100000

在Mybatis中Default方法的使用
在源码MapperProxy类中的invoke方法代码如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

	//执行操作之前的判断
    try {
      //判断是否是Object中的方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (method.isDefault()) {//调用Method类中的idDefault方法判断是否是接口中的默认方法
      	//privateLookupInMethod 判断MethodHandles类中是否有privateLookupIn方法,该方法是java9中才有的
        if (privateLookupInMethod == null) {
          return invokeDefaultMethodJava8(proxy, method, args);
        } else {
          return invokeDefaultMethodJava9(proxy, method, args);
        }
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }

	//这个代码的作用暂时没深入研究,有知道的欢迎分享
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    
    //execute执行增删查改操作
    return mapperMethod.execute(sqlSession, args);
  }

MapperProxy 介绍
Mybatis的基本使用

    public static void main(String[] args) throws Exception {
		//1、加载配置文件
        InputStream inputStream  = Resources.getResourceAsStream("mybatis-config.xml");
		//2、获取SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//3、通过SqlSessionFactory 获取SqlSession 
        SqlSession session = sqlSessionFactory.openSession();
		//4、通过SqlSession获取mapper 接口
        EmpMapper mapper = session.getMapper(EmpMapper.class);
		//5、调用mapper接口方法
        List<Emp> empList = mapper.queryEmp();
		//6、输出结果
        for (Emp e:empList
             ) {
            System.out.println(e);
        }
        //7、关闭
        inputStream.close();
        session.close();
    }

问题:
第5步中为什么能调用接口的方法,获取到数据?

原理:
第4步中getMapper方法实际上是返回MapperProxy对象,该对象对mapper接口进行代理(jdk代理、CGlib 等等方式)

<think>嗯,用户现在想了解MyBatis的使用指南或者原理介绍。之前他们问过关于Tomcat配置的问题,但现在转向了MyBatis,可能是在学习Java后端开发,尤其是数据库操作的部分。用户提供的引用内容涵盖了MyBatis-Plus的批量操作、参数映射、逆向工程、缓存机制等,这些都需要整合到回答中。 首先,我需要确定用户的基础。他们可能已经对MyBatis有初步了解,但需要更系统的指导。引用中提到参数映射、缓存、代码生成器等,这些都是MyBatis的核心概念,应该分点详细讲解。同时,用户上次的问题涉及配置,所以可能需要强调配置相关的部分,比如XML配置和注解方式。 要注意用户可能容易混淆的地方,比如#{}和${}的区别,以及参数绑定的方法。引用里提到未使用@Param时参数名的映射规则,这点需要详细说明,避免用户在实际开发中出错。另外,逆向工程和缓存机制也是重点,尤其是结合MyBatis-Plus的功能,可以提升开发效率,这部分应该作为进阶内容介绍。 用户可能的应用场景是快速上手MyBatis并进行优化,因此需要给出实际代码示例,比如基本查询、插入以及使用MyBatis-Plus的例子。同时,性能优化部分,如批处理和一二级缓存,能帮助用户提升应用性能,这也是他们可能关心的点。 需要确保回答结构清晰,分步骤或模块,比如核心概念、使用步骤、进阶功能、原理和常见问题。每个部分都要结合引用中的内容,例如参数映射规则、缓存实现原理等。另外,生成的相关问题要覆盖用户可能进一步探索的方向,如缓存机制、动态SQL、与Plus的区别等。 最后,检查是否符合之前的格式要求:使用中文,行内公式正确,引用标识正确添加。确保回答真实可靠,引用官方文档或常见实践,比如提到性能优化时引用推荐的资料。这样用户能系统地理解MyBatis,并知道如何深入学习和解决问题。</think>我将结合MyBatis核心机制与最佳实践,分模块为您解析其工作原理和使用要点: ### 一、MyBatis核心工作机制 1. **参数映射机制** - 方法参数通过`@Param`注解或默认规则映射到SQL - 未使用注解时: - JDK8+且开启`-parameters`编译参数:直接使用参数名(如`#{id}`) - 其他情况:使用`param1/arg0`格式(如`#{param1}`)[^1] - 示例: ```java // 正确参数引用 @Select("SELECT * FROM user WHERE id=#{id} AND name=#{name}") User getUser(@Param("id") Long userId, @Param("name") String userName); ``` 2. **SQL执行流程** ```mermaid graph TD A[Mapper接口调用] --> B(SqlSession获取) B --> C{是否存在代理对象?} C -->|是| D[执行缓存逻辑] C -->|否| E[创建MapperProxy] E --> F[创建SqlCommand] F --> G[参数映射处理] G --> H[SQL语句解析] H --> I[执行器Executor] I --> J[缓存查询] J --> K{是否命中?} K -->|是| L[返回结果] K -->|否| M[数据库操作] ``` 3. **缓存体系** - 一级缓存(Session级): - 默认开启,同一SqlSession内有效 - 通过`<setting name="localCacheScope" value="STATEMENT"/>`可禁用 - 二级缓存(Namespace级): - 需手动开启`<cache/>`标签 - 跨SqlSession共享,通过序列化实现[^4] ### 二、开发使用指南 #### 基础配置(mybatis-config.xml) ```xml <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 驼峰转换 --> <setting name="defaultExecutorType" value="REUSE"/> <!-- 执行器类型 --> </settings> <typeAliases> <package name="com.example.model"/> <!-- 实体类别名 --> </typeAliases> </configuration> ``` #### 动态SQL实践 ```xml <select id="searchUsers"> SELECT * FROM user <where> <if test="name != null"> AND name LIKE #{name} </if> <choose> <when test="status == 1">AND active = 1</when> <otherwise>AND active = 0</otherwise> </choose> </where> ORDER BY ${orderBy} <!-- 注意${}存在SQL注入风险 --> </select> ``` #### 批量操作优化 ```java // 使用BatchExecutor提升批量插入性能 try(SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = session.getMapper(UserMapper.class); for(int i=0;i<1000;i++){ mapper.insert(userList.get(i)); if(i%200 == 0) session.flushStatements(); // 分批提交 } session.commit(); } ``` ### 三、MyBatis-Plus增强功能 1. **逆向工程应用** ```java AutoGenerator generator = new AutoGenerator(); generator.setDataSource(new DataSourceConfig() .setUrl("jdbc:mysql://localhost:3306/test") .setUsername("root") .setPassword("root")); generator.setPackageInfo(new PackageConfig().setParent("com.example")); generator.execute(); // 自动生成Entity/Mapper/Service ``` 2. **Wrapper条件构造器** ```java QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.select("id","name") .like("name","张") .orderByDesc("create_time"); List<User> users = userMapper.selectList(wrapper); ``` ### 四、性能调优建议 1. 批量操作优先使用`BatchExecutor` 2. 避免N+1查询问题,使用`<collection>`延迟加载 3. 二级缓存建议配合Redis等分布式缓存实现 4. 分页查询推荐使用PageHelper插件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值