1.Mybatis的一级、二级缓存
MyBatis提供了两种缓存机制来提高查询效率:一级缓存和二级缓存。
一级缓存(Session级别)
- 作用范围:一级缓存是基于
SqlSession
的。这意味着,如果你在同一个SqlSession
中执行两次相同的查询,MyBatis会从第一次查询时创建的缓存中返回结果,而不是再次访问数据库。 - 生命周期:一级缓存的生命周期与
SqlSession
一致。当SqlSession
被关闭或清除时,一级缓存也随之失效。
二级缓存(Mapper级别或全局级别)
- 作用范围:二级缓存是基于
Mapper
的,也就是说,它是跨SqlSession
的。如果不同的SqlSession
执行相同的Mapper和SQL查询,它们可以共享二级缓存中的数据,前提是二级缓存已经被开启并正确配置。 - 生命周期:二级缓存从
SqlSession
开始,直到应用会话结束或缓存被清除。这意味着,二级缓存可以在不同用户或请求间共享数据,从而减少数据库的访问次数,提高应用性能。
工作原理
- 一级缓存:当一个
SqlSession
进行了一次数据库查询后,结果会存储在这个会话的一级缓存中。如果同一个会话中有相同的查询请求,MyBatis将直接从一级缓存返回数据。 - 二级缓存:如果一个查询在一级缓存中没有找到匹配,MyBatis将查找二级缓存。如果结果存在,将直接从二级缓存返回。如果两级缓存都未命中,MyBatis将执行数据库查询,并将结果存入适当的缓存中。
注意事项
- 默认情况下,一级缓存总是开启的,而二级缓存需要显式开启和配置。
- 使用二级缓存时,需要考虑数据的一致性问题。因为数据是跨会话共享的,所以在并发环境下可能会出现数据过时的情况。
- 对于频繁修改的数据,谨慎使用二级缓存,以避免脏读等问题。
通过合理使用和配置这两级缓存,可以在保持数据一致性的同时,显著提升应用的性能。
2.反射机制的应用场景有哪些?
反射机制,在Java中是一种强大的工具,允许程序在运行时访问、检测和修改其自身类的属性和方法。通过反射,可以实现在运行时动态创建对象、调用方法和访问属性,即使这些信息在编译时是未知的。这种机制不仅增加了程序的灵活性,也使得各种通用框架和API的实现成为可能。以下是一些典型的反射机制应用场景:
1. 开发IDE或工具
- 反射用于开发集成开发环境(IDE)和其他开发工具中,比如在调试时动态地查看和修改对象的状态,或是在代码补全时提供方法和属性的列表。
2. 实现依赖注入(DI)
- 许多现代框架(如Spring)使用反射来实现依赖注入。框架可以在运行时读取类的注解和属性,自动将依赖对象注入到其中,而无需手动编写工厂类或创建对象。
3. ORM(对象关系映射)
- ORM框架如Hibernate利用反射将数据库表映射到Java对象。这允许开发者通过操作对象来间接操作数据库中的数据,而不是直接编写SQL语句。
4. 单元测试
- 单元测试框架(如JUnit)使用反射来动态执行测试方法。开发者只需按照一定的规则编写测试方法,测试框架会自动发现并执行这些方法,而不需要手动列出每个测试调用。
5. 动态代理
- 在实现动态代理时,反射机制允许在运行时动态创建一个实现了一组给定接口的新类。这对于实现各种代理模式(如远程方法调用、事务管理等)非常有用。
6. 插件或模块加载
- 应用程序可以使用反射来动态加载外部的插件或模块,这些插件或模块在编译时可能是未知的。这增加了应用程序的扩展性和灵活性。
7. 配置文件解析
- 反射常用于解析XML或JSON等配置文件,动态地创建配置文件中描述的对象和设置其属性值,而无需为每种可能的配置编写大量的解析代码。
8. GUI构建
- 图形用户界面(GUI)构建工具可以使用反射来检查组件类的属性,动态地创建用户界面元素,允许开发者通过配置而非硬编码的方式来设计界面。
小结
反射机制使Java程序具有更高的灵活性和动态性,是许多高级特性和框架实现的基础。但是,也需要注意,不当的使用反射可能会带来性能问题和安全风险,因此在使用时应该权衡其利弊。
3.Java获取反射的三种方法
在Java中,获取类的Class
对象是进行反射操作的第一步。有三种主要方式可以获取到一个类的Class
对象,每种方式适用于不同的场景:
1. 使用Class.forName()
静态方法
这种方法通过类的完全限定名(包括包名)来加载类,并返回类的Class
对象。这是一种动态加载类的方式,常用于配置文件中指定类名的场景,使得你可以在不改变程序代码的情况下更换实现类。
Class<?> cls = Class.forName("java.lang.String");
这个方法抛出ClassNotFoundException
异常,因此需要进行异常处理。
2. 使用.class
语法
如果在编译时已经知道具体的类,可以直接使用.class
语法来获取Class
对象。这种方式不涉及类的动态加载,因此性能较好,但它不如Class.forName()
灵活。
Class<?> cls = String.class;
这种方法不会抛出ClassNotFoundException
异常。
3. 使用类实例的.getClass()
方法
如果你已经有了一个类的实例,可以通过调用该实例的.getClass()
方法来获取Class
对象。这种方式适用于当你有对象实例时,想要动态获取其类型信息的场景。
String str = new String();
Class<?> cls = str.getClass();
这种方式也不会抛出ClassNotFoundException
异常。
总结
Class.forName()
:适用于动态加载类,需要处理ClassNotFoundException
。.class
语法:适用于编译时已知类的情况,性能较好,但不够灵活。.getClass()
方法:适用于已有对象实例,想要获取其Class
对象的场景。
选择哪种方式取决于你的具体需求和场景。在使用反射时,应当注意性能和安全性问题,合理选择获取Class
对象的方法。