一.MySql篇

1优化相关问题

1.1.MySql中如何定位慢查询?


慢查询的概念:在MySQL中,慢查询是指执行时间超过一定阈值的SQL语句。这个阈值是由long_query_time参数设定的,它的默认值是10秒1。也就是说,如果一条SQL语句的执行时间超过了long_query_time所设定的时间,那么这条SQL语句就被认为是慢查询。

现在的问题就是:出现页面加载过慢,响应时间过长。我怎么确定就是我sql的问题呢?就算是sql的问题,我应该怎么找出执行慢的那条sql语句呢?

方案一:使用开发工具

以Skywalking为例。这个工具,可以直观看到每个接口的执行时间。还可以追踪进接口,详细的看耗时情况,其中也包含了sql的耗时。

方案二:使用MySql自带的慢日志

简单来说,这个日志记录了执行时间较长的sql。

总结:

二.框架篇

1.Spring框架

1.1.Spring框架中的bean默认情况下是单例的吗?


Spring框架中的bean默认情况下是单例的。Spring中有一个注解@Scope,这是用来设置bean的情况。默认情况下设置为singleton(单例的)

1.2.Spring框架中的单例bean是线程安全的吗?


简单来说就是:判断当前成员变量能不能修改。如果是int这种可修改的类型,线程就不是安全的。

如果是,Service、DAO这些无状态类 (没有什么可变状态)就算是线程安全的。

总结:

不是线程安全的,是这样的。 当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多 个线程会并发执行该请求对应的业务逻辑(成员方法),如果该处理逻辑中 有对该单列状态的修改(体现为该单例的成员属性),则必须考虑线程同步 问题。 Spring框架并没有对单例bean 进行任何多线程的封装处理。关于单例 bean的 线程安全和并发问题需要开发者自行去搞定。 比如:我们通常在项目中使用的Spring bean都是不可可变的状态 (比如 Service类和 DAO ) ,所以在某种程度上说 Spring 的单例 bean是线程安全的。 如果你的bean 有多种状态的话(比如 View Model对象),就需要自行保证线 程安全。最浅显的解决办法就是将多态bean 的作用由 singleton ”变更为 “prototype ”。

1.3.什么是AOP?项目中有没有使用AOP?


AOP的概念及常见使用场景: 总结:aop是面向切面编程,在spring中用于将那些与业务无关,但却对多个对象产 生影响的公共行为和逻辑,抽取公共模块复用,降低耦合,一般比如可以做 为公共日志保存,事务处理等。实际项目中使用AOP的例子: 在后台管理系统中,就是使用 aop 来记录了系统的操作日志 主要思路是这样的,使用 aop 中的环绕通知 + 切点表达式,这个表达式就是要找到要记录日志的方法,然后通过环绕通知的参数获取请求方法的参数,比如类信息、方法信息、注解、请求方式等,获取到这些参数以后,保存到数据库。

1.4.Spring中事务是如何实现的?

这里的”保存用户”方法,就是我们要加事务的方法。在该方法上添加@Transactional注解。

总结:

声明式事务管理实际上是基于Spring的AOP(面向切面编程)机制实现的。当你在方法上使用@Transactional注解时,Spring会在运行时创建一个代理对象,这个代理对象会在方法调用前后插入事务管理的代码,从而实现事务的自动管理。

具体来说,当Spring容器在运行时遇到带有@Transactional注解的方法时,它会创建一个代理对象来拦截这个方法。在代理对象拦截方法调用时,Spring会在方法调用前后分别插入开始事务和结束事务的代码。如果方法执行过程中抛出异常,Spring会根据异常类型决定是否回滚事务。

因此,虽然在使用声明式事务管理时,开发者不需要自己编写AOP代码,但是Spring仍然使用了AOP技术来实现事务管理。这也是为什么有时候我们会说声明式事务管理是基于AOP的。

1.5.Spring中业务失效的场景


这种情况下,自己把异常处理了,导致@Transactional没有发现异常,事务没有回滚。

Spring默认只会回滚非检查异常也就是runtime异常。而这里的FileNotFound是一个检查异常,并不能被捕获。

Spring为方法创建代理,添加事务通知,前提条件都是该方法是public修饰的!

1.6.Bean的生命周期


1.BeanDefinition

2.构造函数

在此期间,调用Bean的构造函数,实例化对象 (但是还未赋值!)

3.依赖注入

Spring容器会将Bean的属性设置为Bean定义中指定的值。这个过程也被称为依赖注入,因为Spring容器会自动寻找并注入Bean所依赖的其他Bean。

4.Aware接口

用于增强Bean的功能

如果Bean实现了以Aware结尾的接口,就要重写里面的方法。图中三个接口,就是分别用于在Bean的初始化过程中,用于获取:Bean的名字、Bean的工厂、容器的上下文对象(Spring容器本身)。

5.BeanPostProcessor#before

bean的后置处理器,在初始化方法调用之前执行。

6.初始化方法

1.InitalizingBean接口,实现了该接口,就要实现里面的方法。而在这一步就会执行重写方法。

2.自定义init:在bean中某个方法上标注了@PostConstruct注解。就会在这一步执行这个方法。

7.BeanPostProcessor#before

bean的后置处理器,在初始化方法调用之后执行。

当一个类的功能被增强了使用到了AOP,大概率就是使用后置处理器被增强的。

8.销毁Bean

如果在哪个方法上标注了@PreDestroy方法,Spring容器在关闭前就会调用该方法。

注:

1.Bean的创建和初始化是分开的,第2步是创建bean,3-7是初始化赋值

2.第5步和第7步的两个后置处理器。都是某个类实现了BeanPostProcessor接口,所重写的两个方法。分别在初始化方法前后执行。

测试代码:

第一个类User类,作为我们测试的Bean

import org.springframework.beans.BeansException;import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.BeanFactoryAware;import org.springframework.beans.factory.BeanNameAware;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;@Componentpublic class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {public User() {System.out.println("1.User的构造方法执行了.........");}private String name;@Value("张三")public void setName(String name) {System.out.println("2.依赖注入执行了..........");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("3.BeanNameAware的实现方法执行了.......");}@Overridepublic void setBeanName(String s) {System.out.println("3.BeanNameAware的实现方法执行了.......");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("3.ApplicationContextAware的实现方法执行了.......");}@PostConstructpublic void init() {System.out.println("5.自定义初始化方法执行了..........");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("5.InitializingBean的实现方法执行了..........");}@PreDestroypublic void destroy(){System.out.println("7.destroy方法执行了.........");}}

第二个类:实现了BeanPostProcessor重写了其中方法的类。里面是两个后置处理器。

import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanPostProcessor;import org.springframework.stereotype.Component;@Componentpublic class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("user")) {System.out.println("4.postProcessBeforeInitialization方法执行了 -> user对象初始化方法之前执行");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("user")) {System.out.println("6.postProcessAfterInitialization方法执行了 -> user对象初始化方法之后执行");}return bean;}}

执行结果

destroy方法要在容器关闭时执行

总结:

1.首先会通过一个非常重要的类,叫做BeanDefinition获取bean的定义信息, 这里面就封装了bean的所有信息,比如,类的全路径,是否是延迟加载,是否是单例等等这些信息。

2.在创建bean的时候,第一步是调用构造函数实例化bean

3.第二步是bean的依赖注入,比如一些set方法注入,像平时开发用的@Autowire都是这一步完成。

4.第三步是处理Aware接口,如果某一个bean实现了Aware接口就会重写方法执行。

5.第四步是bean的后置处理器BeanPostProcessor,这个是前置处理器。

6.第五步是初始化方法,比如实现了接口InitializingBean或者自定义了方init-method标签或@PostContruct。

7.第六步是执行了bean的后置处理器BeanPostProcessor,主要是对bean进行增强,有可能在这里产生代理对象。

8.最后一步是销毁bean。