第十五天,7月9日,八股
1、Spring boot中的监视器是什么
监视器(monitor)通常是一种特殊的应用程序或组件,用于监控和管理整个应用程序的运行状态和性能。Springboot提供了一些内置的监视器功能,例如:Springboot Actuator
如何使用Actuator
(1)添加依赖:spring-boot-starter-actuator
(2)properties配置:management.endpoints.web.exposure.include="*"
(3)使用浏览器或者命令行工具访问不同端点即可
# 获取应用的健康状态信息
curl http://localhost:8080/actuator/health# 获取应用的基本信息
curl http://localhost:8080/actuator/info# 获取应用的性能指标
curl http://localhost:8080/actuator/metrics# 追踪 HTTP 请求的处理过程
curl http://localhost:8080/actuator/httptrace# 关闭应用
curl -X POST http://localhost:8080/actuator/shutdown
2、Springboot打成的jar和普通的jar有什么区别
(1)Springboot的jar内置了服务器(如tomcat),可以直接通过java -jar运行;普通jar需要指示jvm从哪个类开始执行
(2)Springboot的jar无法作为普通jar被其他项目依赖
因为(结构不同):普通jar解压后直接就是包,包里就是代码;Springboot的jar解压后,还有一层\BOOT-INF\classes,在此目录下才是代码,所以无法直接被引用
解决办法:可以在pom.xml中添加配置,将Springboot打包成两个jar,一个可执行,一个可引用
3、Spring boot中如何实现定时任务
(1)Timer:实现简单,但是一个timer就会起一个线程,任务多了就会性能下降
TimerTask task = new TimerTask() { run(){实现业务逻辑}};
Timer timer = new Timer("task的类");
timer.schedule(task,1000L);
(2)线程池的scheduleAtFixedRate
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3); executorService.scheduleAtFixedRate(task, 1000L,1000L, TimeUnit.MILLISECONDS);
(3)Spring Task: 是 Spring 提供的轻量级定时任务工具
【1】添加注解:@EnableScheduling
【2】在业务逻辑的方法上添加@Scheduled(cron = "0/1 * * ? * ?")
注意:@Scheduled默认线程池大小是1,如果想增加,可以实现一个SchedulingConfigurer 接口,通过 setPoolSize 增加线程池大小,还可以起线程的名字等
(4)quartz:需要定义job,jobDetail和trigger
(5) xxl-job:添加依赖,添加调度中心的地址,然后使用@XxlJob("myJobHandler"),然后通过网页即可配置定时启动
4、如何理解spring boot的配置加载顺序
(1)配置文件的加载顺序
- 类路径下的配置文件
- 类路径内config子目录的配置文件
- 当前项目根目录下的配置文件
- 当前项目根目录下config子目录的配置文件
. project-sample
├── config
│ ├── application.yml (4)
│ └── src/main/resources
| │ ├── application.yml (1)
| │ └── config
| | │ ├── application.yml (2)
├── application.yml (3)
启动时加载配置文件顺序:1 > 2 > 3 > 4,覆盖顺序为:4 > 3 > 2 > 1。
(2)配置属性的加载顺序(很多参数,此处就简单列举一些)
1、开发者工具 `Devtools` 全局配置参数;
4、命令行指定的参数,如 `java -jar springboot.jar --name="Java技术栈"`;
9、Java系统参数(来源:`System.getProperties()`);
10、操作系统环境变量参数;
12、JAR包外面的配置文件参数(`application-{profile}.properties(YAML)`)13、JAR包里面的配置文件参数(`application-{profile}.properties(YAML)`)14、JAR包外面的配置文件参数(`application.properties(YAML)`)15、JAR包里面的配置文件参数(`application.properties(YAML)`)16、`@Configuration`配置文件上 `@PropertySource` 注解加载的参数;17、默认参数(通过 `SpringApplication.setDefaultProperties` 指定);
数字小的优先级越高,即数字小的会覆盖数字大的参数值【有些参数没有例举】
5、介绍一下mybatis中的工作原理
MyBatis是一个Java持久化框架,它通过使用简单的XML或注解来简化关系型数据库的访问
(1)读取mybatis配置文件:mybatis-config.xml 配置了mybatis的运行环境等信息,如数据库连接信息,别名,映射文件路径(mapper.xml)等
(2)加载映射文件:mapper.xml,就是sql语句
(3)构造会话工厂:通过环境等配置信息,构造会话工厂SqlSessionfactory
(4)创建会话对象:由会话工厂创建SqlSession对象,该对象包含了执行sql语句的所有方法,类似jdbc的Connection
(5)通过Sqlsession对象,获取到映射的sql语句并执行
注意:
第(5)步可以包括2个核心内容:
【1】Executor 执行器(生成动态的sql并执行):在Executor 接口的方法中有一个MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
【2】ResultHandler对象:返回结果,可以自定义返回类型
6、介绍一下mybatis的缓存
在MyBatis中,缓存机制可以避免频繁访问数据库,从而减少查询时间,提高系统性能。
(1)一级缓存是MyBatis的默认缓存机制,作用范围是SqlSession
对象。它存储在SqlSession
实例中,生命周期与SqlSession
一致。当SqlSession
关闭或执行insert
、update
、delete
操作时,一级缓存会失效。
(2)二级缓存作用范围是SqlSessionFactory
实例,即整个数据库环境。它存储在SqlSessionFactory
中,生命周期与SqlSessionFactory
一致。当执行insert
、update
、delete
操作时,二级缓存会失效。
注意:
(1)二级缓存的开启:<setting name="cacheEnabled" value="true"> ,mappe.xml中添加<cache/>
(2)查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭时,一级缓存中的数据才会转到二级缓存中
(3)二级缓存的回收策略:
【1】LRU:默认,最近最少使用,移除最长时间不被使用的对象
【2】FIFO:按缓存的顺序移除他们
【3】SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
【4】WEAK:弱引用,更积极的移除基于垃圾回收器状态和弱引用规则的对象
(4)先查二级再查一级,最后查数据库;因为二级缓存的命中率比一级缓存的命中率高,所以先查二级再查一级效率要好
7、mybatis的分页
(1)RowBounds分页:逻辑分页,通过查询所有的数据到内存,再根据RowBounds参数(当前页和大小)进行获取数据;只能处理数据量较少
(2)添加拦截器(使用分页插件PageHelper)
8、谈谈Sqlsession(DefaultSqlsession)的安全问题
为什么不安全?
原因 1:Connection 本身是线程不安全的。如果多个线程获取到同一个 Connection 进行数据库操作,一个线程正在更新数据,而另一个线程提交了事务,这种情况可能导致数据混乱和丢失。
原因 2:MyBatis 的一级缓存和二级缓存存储使用的都是 HashMap,而 HashMap 本身就不是线程安全的。
因此,每个线程都应该有自己的Sqlsession实例,Sqlsession的实例不是线程安全的,因此不能被共享,所以他的最近作用域是请求或者方法的作用域,不能将Sqlsession实例的引用放在一个类的静态域,甚至一个类的实例变量(成员变量)也不行
解决办法:
方法1:确保每次请求之后都应该关闭,比如方法放到finally中关闭,或者利用try(Sqlsession session = sqlsessionFactory.openSession),try中结束会自动光比
方法2:spring提供了一个sqlsessionTemplate来实现sqlSession的相关定义,然后再sqlsessionTemplate中每个方法都通过sqlsessionProxy来操作,这个是个动态代理对象,通过动态代理来实现相关数据库的操作(为什么使用了代理对象就是安全的?==>保证了每个线程都会获得单独的sqlsession,这样多个线程使用同一个SqlSessionTemplate 执行sql时,最终线程都是使用各自的)
注意:除了SqlSessionTemplate外还有SqlSessionManager 也是安全的,SqlSessionTemplate 和 SqlSessionManager 都持有 SqlSessionFactory,并且都是线程安全的,都通过ThreadLocal来实现SqlSession的生命周期管理