一、Spring面试题
文章目录
- 一、Spring面试题
- 二、Spring MVC
- 三、Spring Boot
- 1..Spring Boot的自动装配原理
- 2.Spring Boot有多少种启动方式?
- 3.说说你对Spring Boot注解的了解
- 4.什么是Spring Boot?
- 5.SpringBoot Starter的工作原理
- 6.Spring Boot的启动流程
- 7.Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
- 8.SpringBoot 实现热部署有哪几种方式?
- 9.你如何理解 Spring Boot 配置加载顺序?
- 10.spring boot 核心的两个配置文件:
- 11.Spring Boot 中如何解决跨域问题 ?
- 12.Spring Boot 中的监视器是什么?
- 13.Spring Boot 中如何实现定时任务 ?
- 14.Packages打包与Install打包的区别
- 四、Spring Cloud
- 五、MyBatis
- 1.什么是Mybatis
- 2.MyBatis的优缺点
- 2.Hibernate 和 MyBatis 的区别
- 3.ORM是什么
- 4.为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
- 5.==MyBatis==编程步骤是什么样的?
- 6.请说说MyBatis的工作原理
- 7.什么是DBMS
- 8.**Mybatis都有哪些Executor执行器?它们之间的区别是什么?**
- 9.Mybatis中如何指定使用哪一种Executor执行器?
- 10.#{}和${}的区别
- 11.在mapper中如何传递多个参数*(四个)
- 12.如何获取生成的主键
- 13.Mapper 编写有哪几种方式?
- 14.什么是MyBatis的接口绑定?有哪些实现方式?
- 15.这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗
- 16.简述Mybatis的Xml映射文件和==Mybatis内部数据结构==之间的映射关系?
- ==17.==了解MyBatis==缓存==机制吗?
- 六、MySQL
- 八、设计模式
- 九、Dubbo
- 十.Zookeeper
- 十一、Redis
- 十二、MongoDB
- 十三、RabbitMQ面试题
- 十四、Java基础、
- 十四、Java基础、
1.Spring的Bean的生命周期==
Spring中的bean的生命周期主要包含四个阶段**:实例化Bean --> Bean属性填充 --> 初始化Bean -->销毁Bean**
- Bean的定义;就是从xml或注解定位资源加载读取bean的元信息并定义成一个BeanDefinition对象
- **Bean的注册:**将BeanDefinition对象根据相应的规则放到缓存池中
- Bean的实例化
- Bean的属性填充(依赖注入)
- Bean的初始化
- Bean的销毁(关闭Tomcat的情况下)
**1.**首先是实例化Bean,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚末初始化的依赖时,容器就会调用doCreateBean()方法进行实例化,实际上就是通过反射的方式创建出一个bean对象
Bean实例创建出来后,
**2.接着就是给这个Bean对象进行属性填充,**也就是注入这个Bean依赖的其它bean对象
属性填充完成后,
3.进行初始化Bean操作,初始化阶段又可以分为几个步骤:**
3.1执行Aware接口的方法**
Spring会检测该对象是否实现了xxxAware接口,通过Aware类型的接口,可以让我们拿到Spring容器的些资源。如实现
BeanNameAware接口可以获取到BeanName,实现BeanFactoryAware接口可以获取到工厂对象BeanFactory等
**3.2执行BeanPostProcessor的前置处理方法postProcessBeforelnitialization(),**对Bean进行一些自定义的前置处理
判断Bean是否实现了InitializingBean接口,如果实现了,将会执行lnitializingBean的afterPropertiesSet()初始化方法;
3.3执行用户自定义的初始化方法,如init-method等;
3.4执行BeanPostProcessor的后置处理方法postProcessAfterinitialization()
3.5初始化完成后,Bean就成功创建了,之后就可以使用这个Bean,
4.当Bean不再需要时,会进行销毁操作,(3)
首先判断Bean是否实现了DestructionAwareBeanPostProcessor接口,如果实现了,则会执行DestructionAwareBeanPostProcessor后置处理器的销毁回调方法
其次会判断Bean是否实现了DisposableBean接口,如果实现了将会调用其实现的destroy()方法
最后判断这个Bean是否配置了dlestroy-method等自定义的销毁方法,如果有的话,则会自动调用其配置的销毁方法;
2.Spring事务传播机制:7种
参考答案
当我们调用一个业务方法时,它的内部可能会调用其他的业务方法,以完成一个完整的业务操作。这种业务方法嵌套调用的时候,如果这两个方法都是要保证事务的,那么就要通过Spring的事务传播机制控制当前事务如何传播到被嵌套调用的业务方法中。
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时如何进行传播,如下表:
**1.REQUIRED(Spring默认的事务传播类型 required:需要、依赖、依靠):**如果当前没有事务,则自己新建一个事务,如果当前存在事务则加入这个事务
当A调用B的时候:如果A中没有事务,B中有事务,那么B会新建一个事务;如果A中也有事务、B中也有事务,那么B会加入到A中去,变成一个事务,这时,要么都成功,要么都失败。(假如A中有2个SQL,B中有两个SQL,那么这四个SQL会变成一个SQL,要么都成功,要么都失败)
2.SUPPORTS(supports:支持;拥护):当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败),如果A中没有事务,那么B就以非事务方式运行(执行完直接提交);
**3.MANDATORY(mandatory:强制性的)😗*当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败);如果A中没有事务,B中有事务,那么B就直接抛异常了,意思是B必须要支持回滚的事务中运行
**4.REQUIRES_NEW(requires_new:需要新建)😗*创建一个新事务,如果存在当前事务,则挂起该事务。
B会新建一个事务,A和B事务互不干扰,他们出现问题回滚的时候,也都只回滚自己的事务;
**5.NOT_SUPPORTED(not supported:不支持)😗*以非事务方式执行,如果当前存在事务,则挂起当前事务
被调用者B会以非事务方式运行(直接提交),如果当前有事务,也就是A中有事务,A会被挂起(不执行,等待B执行完,返回);A和B出现异常需要回滚,互不影响
6.NEVER(never:从不): 如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。就是B从不以事务方式运行
A中不能有事务,如果没有,B就以非事务方式执行,如果A存在事务,那么直接抛异常
**7.NESTED(nested:嵌套的)**嵌套事务:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
如果A中没有事务,那么B创建一个事务执行,如果A中也有事务,那么B会会把事务嵌套在里面。
什么叫嵌套呢?
A是父事务,B是子事务
事务传播类型 | 说明 |
---|---|
PROPAGATION_REQUIRED(propagatioin_required) | 如果当前没有事务,则新建一个事务;如果已存在一个事务,则加入到这个事务中。这是最常见的选择。 |
PROPAGATION_SUPPORTS(supports支持) | 支持当前事务,如果当前没有事务,则以非事务方式执行。 |
PROPAGATION_MANDATORY(mandatory:强制性的) | 使用当前的事务,如果当前没有事务,则抛出异常。 |
PROPAGATION_REQUIRES_NEW(requires_new ) | 新建事务,如果当前存在事务,则把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED(not_supported) | 以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。 |
PROPAGATION_NEVER(never) | 以非事务方式执行操作,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED() | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 |
什么叫嵌套呢?
A是父事务,B是子事务
NESTED嵌套事务 和REQUIRES_NEW的区别
REQUIRES_NEw是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEw情况下,原有事务回滚,不会影响新开启的事务。
和REQUIRED的区别
REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响
3.Spring Boot的自动装配原理
最终答案;
使用Spring Boot时,我们只需引入对应的Starters,Spring Boot启动时便会自动加载相关依赖,配置相应的初始化参数,以最快捷、简单的形式对第三方软件进行集成,这便是Spring Boot的自动配置功能。Spring Boot实现该运作机制锁涉及的核心部分如下图所示:
- @SpringBootConfiguration: 标记启动类为一个spring配置类
- @EnableAutoConfiguration: 实现自动装配的核心注解(重头戏)
- @ComponentScan: 扫描启动类所在的包以及子包下所有标记为Bean的组件并注册到IOC容器中
4.Spring Boot有多少种启动方式?
启动一个SpringBoot项目有四种方式,其中三种是借助 SpringBoot 的内置Tomcat 容器,一种是借助外部 Tomcat(SpringBoot使用的是
一、基于main函数运行
1.基于@SpringBootApplication的注解运行
@SpringBootApplication
public class MainApplication{
public static void main(String[] args){
SpringApplication.run(MainApplication.class,args);
}
}
2.基于@EnableAutoConfiguration+@ComponentScan(basePages = “com”)注解运行
//@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.example")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3.基于Controller及@EnableAutoConfiguration+@ComponentScan(basePages = “com”)注解运行
@EnableAutoConfiguration
@RestController
@ComponentScan(basePackages = "com.example")
public class MainController{
public static void main(String[] args){
SpringApplication.run(MainController.class,args);
}
}
二、打包方式运行
4.打包方式运行-war-jar
打包成war包:改主函数、禁用(内置Tomcat)spring-boot-starter-tomcat,改配置文件-war
java -jar ./spring-boot-system-0.0.1-SNAPSHOT.jar
三、使用Maven或gradle插件运行
5.Spring是如何简化开发的?
Spring通过以下四种策略来简化开发:
- 基于POJO的轻量级和最小侵入编程
Spring框架不强迫应用程序实现Spring提供的规范接口或者继承Spring的规范类。可能你的类里面使用了Spring的注解,但是去掉注解,它仍然是一个普通的Java类。
延伸:
POJO(Plain Ordinary Java Object),简单的Java对象。POJO的内在含义是指那些没有任何继承、没有实现任何接口,更没有被其它框架侵入的java对象。
如果在使用某种框架时**,需要继承框架提供的类或者要实现框架提供的接口**,那就说明这个框架是侵入式的。程序对框架产生了依赖,当去除框架时,程序无法正常运行,这就是我们所说的重量级框架。
举例:
当我们使用Struts2框架时,Action需要继承Struts2的一个基类,这样我的代码就被侵入了。(且基类中的部分内容和方法与我的设计内容无关)
- 通过依赖注入和面向接口实现松耦合(扩展:面向接口编程含义在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的)
Java程序是由多个类组成的,要实现某个业务逻辑,需要类与类之间协作完成,这就产生了依赖关系。比如ClassA中用到ClassB中的方法,那么就要在ClassA中new一个ClassB的对象。每个对象都要负责管理与自己协作的对象的引用,这将会导致程序的高度耦合。
采用依赖注入技术之后,只需要在ClassA中定义一个私有的ClassB,不需要直接new得到该对象,而是通过IOC容器来将ClassB对象在外部new出来并注入到ClassA里的引用中。而具体获取的方法、对象被获取时的状态由配置文件来指定。
- 基于AOP和管理进行声明式编程
面向切面编程能够允许在遍布应用各处的功能分离出来形成可重用的组件。例如:常见的日志、事务管理和安全等系统服务。通过AOP编程,能够使上述的服务模块化,并以声明的方式将他们应用到需要的组件中去。
- 通过切面和模板减少样式代码,如**RedisTemplate,xxxTemplate、RestTemplate
我们都使用过JDBC编程,在操作数据库时,其中有大量重复的样板式代码。(包括建立连接,释放等) spring为我们提供了JdbcTemplate来消除样板式代码。并且spring也集成了Hibernate、mybatis以及JPA来简化我们的开发
6.spring事务的实现方式
spring框架提供了两种事务实现方式:编程式事务、声明式事务
编程式事务:在代码中进行事务控制。优点:精度高。缺点:代码耦合度高
声明式事务:通过==@Transactional==注解实现事务控制
spring事务的底层原理
事务的操作本来应该由数据库进行控制,但是为了方便用户进行业务逻辑的控制,spring对事务功能进行了扩展实现。一般我们很少使用编程式事务,更多的是使用@Transactional注解实现。当使用了@Transactional注解后事务的自动功能就会关闭,由spring帮助实现事务的控制。
Spring的事务管理是通过AOP代理实现的,对被代理对象的每个方法进行拦截,在方法执行前启动事务,在方法执行完成后根据是否有异常及异常的类型进行提交或回滚。
Spring AOP动态代理机制:
Spring在运行期间会为目标对象生成一个代理对象,并在代理对象中实现对目标对象的增强。
SpringAOP通过两种动态代理机制,实现对目标对象执行横向植入的。
代理技术 描述
JDK动态代理
这是Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用这种方式,在接口的代理实例中织入代码。
CGLib动态代理
采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,Spring AOP就会采用这种方式,在子类实例中织入代码。
原理:当在某个类或者方法上使用@Transactional注解后,spring会基于该类生成一个代理对象,并将这个代理对象作为bean。当调用这个代理对象的方法时,如果有事务处理,则会先关闭事务的自动功能,然后执行方法的具体业务逻辑,如果业务逻辑没有异常,那么代理逻辑就会直接提交,如果出现任何异常,那么直接进行回滚操作。当然我们也可以控制对哪些异常进行回滚操作。
7.Spring中Bean的作用域
默认情况下,Bean在Spring容器中是单例的,我们可以通过@Scope注解修改Bean的作用域。该注解有如下5个取值,它们代表了Bean的5种不同类型的作用域:
类型 | 说明 |
---|---|
singleton(单例bean) | 在Spring容器中仅存在一个实例,即Bean以单例的形式存在。 |
prototype | 每次调用getBean()时,都会执行new操作,返回一个新的实例。 |
request | 每次HTTP请求都会创建一个新的Bean。 |
session | 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。 |
globalSession | 同一个全局的Session共享一个Bean,一般用于Portlet环境。 |
什么是Spring?
Spring框架是一个开放源代码的J2EE应用程序框架,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。提供了功能强大IOC、AOP及Web MVC等功能,其中
Spring 框架有七个模块组成,这7个模块(或组件)均可以单独存在,也可以与其他一个或者多个模块联合使用,主要功能表现如下:
1.Spring 核心容器(Core):提供Spring框架基本功能。核心容器的主要组件是BeanFactory,他是工厂模式的实现。**BeanFactory 使用控制反转(IoC)模式将应用程序的配置和依赖性规范与实际的应用代码程序分开。
2.Spring AOP : 通过配置管理特性,Spring AOP 模块直接面向封面的编程功能集成到了Spring 框架中,所以可以很容易的使Spring 框架管理的任何对象支持AOP。Spring AOP 模块为基于Spring 的应用程序中的对象提供了事务管理服务。通过使用Spring AOP ,不用依赖于EJB(Enterprise Java Beans)组件,就可以将声明性事务管理集成到应用程序中。
**3.Spring ORM:**Spring 框架集成了若干ORM 框架,从而提供了ORM的对象关系工具,其中包括JDO、Hibernate、MyBatis和TopLink。所有这些都遵从S人频繁的通用事务和DAO 异常层结构。
4.Spring DAO: JDBC DAO 抽象层提供了有意义的议程层次的结构,可用该结构来管理议程处理和不同数据供应商抛出的议程错误信息。异常层次结构简化了错误处理,并且大大的降低了需要编写的异常代码数量(例如,打开和关比链接)。Spring DAO 的面向JDBC 的异常遵从通用的DAO 异常层结构。
5.Spring WEB: W**EB 上下文模块简历在上下文模块(Context)的基础之上,为基于 WEB 服务的应用程序提供了上下文的服务。**所以Spring 框架支持Jakarta Struts 的集成。Web 模块还简化了处理多部分请求及将请求参数绑定到域对象的工作。
**6.Spring 上下文(Context):**Spring 上下文是一个配置文件,向Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化校验和调度功能。
7.Spring MVC: Spring 的MVC 框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳的大量视图技术,包括JSP、Velocity、Tiles、iText和Pol。
8. Spring有什么优势
1、方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
2、方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
3、降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
4、方便程序的测试
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
5、AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
6、声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程
9.简述Spring的IOC
容器概念、控制反转、依赖注入
可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度
IOC容器:
实际上就是个(理解成)Map<key,value>,里面存的是各种对象(如在xml里配置的bean节点,@repository、@service、@controller、@component),在项目启动的时候会读取配置文件中的bean节点,根据全限定类名使用反射创建对象放到map里,扫描到上述注解的类还是通过反射创建对象放到map里。这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入(@autowrise,resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。
控制反转
没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点得到时候,自己必须主动去创建对象B或者使用已经创建的对象B,无论是创建还是使用B,控制权都在自己手上。
引入IOC容器后,对象A与对象B之间失去直接联系,当对象运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
通过对比:对象A获得依赖对象B的过程,由主动行为变成了被动行为,控制权颠倒过来了,这个就是“控制反转”的由来。
**全部对象的控制权全部上缴给“第三方”IOC容器,**所以IOC容器成了整个系统的关键核心,它起到了一种类似“**粘合剂”**的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“**粘合剂”**的由来。
依赖注入
获得依赖对象的过程被反转了。控制被反转后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
在具体的实现中,主要由三种注入方式:
-
构造方法注入
就是被注入对象可以在它的构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。然后,IoC Service Provider会检查被注入的对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
-
setter方法注入
通过setter方法,可以更改相应的对象属性。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。
-
接口注入
相对于前两种注入方式来说,接口注入没有那么简单明了。被注入对象如果想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。相对于前两种依赖注入方式,接口注入比较死板和烦琐。
10.简述Spring的AOP
AOP(面向切面编程):将程序中交叉业务逻辑,(如安全、日志、事务等等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
比如@Transational事务处理
11.谈一下Spring是如何解决循环依赖的?
spring内部有三级缓存(bean):
分析上图发现
这样就解决Setter循环依赖的问题了。
————————————————
二、Spring MVC
1.什么是Spring MVC?
- Spring MVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块。
- 是Spring对Web框架的一个解决方案,提供了一个总的前端控制器Servlet,用来接收请求,然后定义了一套路由策略(url对handler的映射)及适配执行handle,将handle结果使用视图解析技术生成视图展现给前端。
- 主要由DispatcherServlet、处理器映射、处理器(控制器)、视图解析器、视图组成
2.Spring MVC的工作流程
注解+适配器+controller+适配器+
1、 用户发送请求至前端控制器DispatcherServlet。
2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、 DispatcherServlet调用HandlerAdapter处理器适配器。
5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、 Controller执行完成返回ModelAndView。
7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9、 ViewReslover解析后返回具体View。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、 DispatcherServlet响应用户。
3.常用的Spring MVC的注解
@RequestMapping:
作用:该注解的作用处理请求地址映射的,也就是说将其中的处理器方法映射到url路径上。
属性:
- method:是让你指定请求的method的类型,比如常用的有get和post。
- value:是指请求的实际地址,如果是多个地址就用{}来指定就可以啦。
- produces:指定返回的内容类型,当request请求头中的Accept类型中包含指定的类型才可以返回的。
- consumes:指定处理请求的提交内容类型,比如一些json、html、text等的类型。
- headers:指定request中必须包含那些的headed值时,它才会用该方法处理请求的。
- params:指定request中一定要有的参数值,它才会使用该方法处理请求。
@RequestParam:
作用:是将请求参数绑定到你的控制器的方法参数上,是Spring MVC中的接收普通参数的注解。
属性:
- value是请求参数中的名称。
- required是请求参数是否必须提供参数,它的默认是true,意思是表示必须提供。
@RequestBody:
作用:如果作用在方法上,就表示该方法的返回结果是直接按写入的Http responsebody中(一般在异步获取数据时使用的注解)。
属性:required,是否必须有请求体。它的默认值是true,在使用该注解时,值得注意的当为true时get的请求方式是报错的,如果你取值为false的话,get的请求是null。
@PathVariable:
作用:该注解是用于绑定url中的占位符,但是注意,spring3.0以后,url才开始支持占位符的,它是Spring MVC支持的rest风格url的一个重要的标志。
4.SpringMVC有哪些优点?
1.SpringMVC本身是与Spring框架结合而成的,它同时拥有Spring的优点(例如依赖注入DI和切面编程AOP等)。
2.SpringMVc提供强大的约定大于配置的契约式编程支持,即提供一种软件设计范式,减少软件开发人员做决定的次数,开发人员仅需规定应用中不符合约定的部分。
3.支持灵活的URL到页面控制器的映射。
4.可以方便地与其他视图技术(JSP、FreeMarker等)进行整合。由于SpringMVC的模型数据往往是放置在Map数据结构中的,因此其可以很方便地被其他框架引用。
5.拥有十分简洁的异常处理机制。
6.可以十分灵活地实现数据验证、格式化和数据绑定机制,可以使用任意对象进行数据绑定操作。
支持RestFul风格。
5.介绍一下Spring MVC的拦截器
拦截器会对处理器进行拦截,这样通过拦截器就可以增强处理器的功能。Spring MVC中,所有的拦截器都需要实现HandlerInterceptor接口,该接口包含如下三个方法:preHandle()、postHandle()、afterCompletion()。
这些方法的执行流程如下图:
通过上图可以看出,Spring MVC拦截器的执行流程如下:
- 执行preHandle方法,它会返回一个布尔值。如果为false,则结束所有流程,如果为true,则执行下一步。
- 执行处理器逻辑,它包含控制器的功能。
- 执行postHandle方法。
- 执行视图解析和视图渲染。
- 执行afterCompletion方法。
Spring MVC拦截器的开发步骤如下:
-
开发拦截器:
实现handlerInterceptor接口,从三个方法中选择合适的方法,实现拦截时要执行的具体业务逻辑。
-
注册拦截器:
定义配置类,并让它实现WebMvcConfigurer接口,在接口的addInterceptors方法中,注册拦截器,并定义该拦截器匹配哪些请求路径。
三、Spring Boot
1…Spring Boot的自动装配原理
整个自动装配的过程是:Spring Boot通过@EnableAutoConfiguration注解开启自动配置,加载spring.factories中注册的各种AutoConfiguration类,当某个AutoConfiguration类满足其注解@Conditional指定的生效条件(Starters提供的依赖、配置或Spring容器中是否存在某个Bean等)时,实例化该AutoConfiguration类中定义的Bean(组件等),并注入Spring容器,就可以完成依赖框架的自动配置。
最终答案;
使用Spring Boot时,我们只需引入对应的Starters,Spring Boot启动时便会自动加载相关依赖,配置相应的初始化参数,以最快捷、简单的形式对第三方软件进行集成,这便是Spring Boot的自动配置功能。Spring Boot实现该运作机制锁涉及的核心部分如下图所示:
- @SpringBootConfiguration: 标记启动类为一个spring配置类
- @EnableAutoConfiguration: 实现自动装配的核心注解(重头戏)
- @ComponentScan: 扫描启动类所在的包以及子包下所有标记为Bean的组件并注册到IOC容器中
2.Spring Boot有多少种启动方式?
启动一个SpringBoot项目有四种方式,其中三种是借助 SpringBoot 的内置Tomcat 容器,一种是借助外部 Tomcat(SpringBoot使用的是
一、基于main函数运行
1.基于@SpringBootApplication的注解运行
@SpringBootApplication
public class MainApplication{
public static void main(String[] args){
SpringApplication.run(MainApplication.class,args);
}
}
2.基于@EnableAutoConfiguration+@ComponentScan(basePages = “com”)注解运行
//@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.example")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3.基于Controller及@EnableAutoConfiguration+@ComponentScan(basePages = “com”)注解运行
@EnableAutoConfiguration
@RestController
@ComponentScan(basePackages = "com.example")
public class MainController{
public static void main(String[] args){
SpringApplication.run(MainController.class,args);
}
}
二、打包方式运行
4.打包方式运行-war-jar
打包成war包:改主函数、禁用(内置Tomcat)spring-boot-starter-tomcat,改配置文件-war
java -jar ./spring-boot-system-0.0.1-SNAPSHOT.jar
三、使用Maven或gradle插件运行
3.说说你对Spring Boot注解的了解
参考答案
@SpringBootApplication注解:
在Spring Boot入口类中,唯一的一个注解就是@SpringBootApplication。它是Spring Boot项目的核心注解,用于开启自动配置,准确说是通过该注解内组合的@EnableAutoConfiguration开启了自动配置。
@EnableAutoConfiguration注解:
@EnableAutoConfiguration的主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的Bean。自动配置通常是基于项目classpath中引入的类和已定义的Bean来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的jar包中。
@Import注解:
@EnableAutoConfiguration的关键功能是通过**@Import注解导入的ImportSelector来完成的**。从源代码得知@Import(AutoConfigurationImportSelector.class)是@EnableAutoConfiguration注解的组成部分,也是自动配置功能的核心实现者。
@Conditional注解:
@Conditional注解是由Spring 4.0版本引入的新特性,可根据是否满足指定的条件来决定是否进行Bean的实例化及装配,比如,设定当类路径下包含某个jar包的时候才会对注解的类进行实例化操作。总之,就是根据一些特定条件来控制Bean实例化的行为。
@Conditional衍生注解:
在Spring Boot的autoconfigure项目中提供了各类基于@Conditional注解的衍生注解,它们适用不同的场景并提供了不同的功能。通过阅读这些注解的源码,你会发现它们其实都组合了@Conditional注解,不同之处是它们在注解中指定的条件(Condition)不同。
- @ConditionalOnBean:在容器中有指定Bean的条件下。
- @ConditionalOnClass:在classpath类路径下有指定类的条件下。
- @ConditionalOnCloudPlatform:当指定的云平台处于active状态时。
- @ConditionalOnExpression:基于SpEL表达式的条件判断。
- @ConditionalOnJava:基于JVM版本作为判断条件。
- @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置。
- @ConditionalOnMissingBean:当容器里没有指定Bean的条件时。
- @ConditionalOnMissingClass:当类路径下没有指定类的条件时。
- @ConditionalOnNotWebApplication:在项目不是一个Web项目的条件下。
- @ConditionalOnProperty:在指定的属性有指定值的条件下。
- @ConditionalOnResource:类路径是否有指定的值。
- @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个或者有多个但是指定了首选的Bean时。
- @ConditionalOnWebApplication:在项目是一个Web项目的条件下。
4.什么是Spring Boot?
-
Spring Boot 是 Spring 开源组织下的子项目,主要是简化了使用 Spring 的难度,简省了繁重的配置,就是”约定大于配置“,提供了各种启动器,以达到快速构建项目、预置三方配置、开箱即用的目的。Spring Boot有如下的优点:
-
可以快速构建项目;
-
可以对主流开发框架的无配置集成;
-
项目可独立运行,无需外部依赖Servlet容器;
-
可以极大地提高开发、部署效率
5.SpringBoot Starter的工作原理
在sprinBoot启动时由**@SpringBootApplication注解会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建spring容器中的bean,并且进行自动配置把bean注入SpringContext**中 //(SpringContext是Spring的配置文件
6.Spring Boot的启动流程
参考答案
首先,Spring Boot项目创建完成会默认生成一个名为 Application 的入口类,我们是通过该类的main方法启动Spring Boot项目的。在main方法中,通过SpringApplication的静态方法,即run方法进行SpringApplication类的实例化操作*,然后再针对实例化对象调用另外一个run方法来完成整个项目的初始化和启动。
SpringApplication调用的run方法的大致流程,如下图:
其中,SpringApplication在run方法中重点做了以下操作:
- 获取监听器和参数配置;
- 打印Banner信息;
- 创建并初始化容器;
- 监听器发送通知。
当然,除了上述核心操作,run方法运行过程中还涉及启动时长统计、异常报告、启动日志、异常处理等辅助操作。比较完整的流程,可以参考如下源代码:
public ConfigurableApplicationContext run(String... args) { // 创建StopWatch对象,用于统计run方法启动时长。
7.Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作为日志框架,如果你使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架,但是不管是那种日志框架他都支持将配置文件输出到控制台或者文件中。
8.SpringBoot 实现热部署有哪几种方式?
- 热部署就是可以不用重新运行SpringBoot项目可以实现操作后台代码自动更新到以运行的项目中
- 主要有两种方式:
- Spring Loaded
- Spring-boot-devtools
9.你如何理解 Spring Boot 配置加载顺序?
- 在 Spring Boot 里面,可以使用以下几种方式来加载配置。
- 1.properties文件;
- 2.YAML文件;
- 3.系统环境变量;
- 4.命令行参数;等等
10.spring boot 核心的两个配置文件:
- bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud 配置就会使用这个文件。且 boostrap 里面的属性不能被覆盖;
- application (. yml 或者 . properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。
11.Spring Boot 中如何解决跨域问题 ?
- 跨域可以在前端通过 JSONP 来解决,但是 **JSONP 只可以发送 GET 请求,无法发送其他类型的请求,**在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
}
12.Spring Boot 中的监视器是什么?
- Spring boot actuator 是 spring 启动框架中的重要功能之一。Spring boot 监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为 HTTP URL 访问的REST 端点来检查状态。
13.Spring Boot 中如何实现定时任务 ?
- 在 Spring Boot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的 @Scheduled 注解,另一-个则是使用第三方框架 Quartz。
- 使用 Spring 中的 @Scheduled 的方式主要通过 @Scheduled 注解来实现。
14.Packages打包与Install打包的区别
Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。
- 清理(clean):删除以前的编译结果,为重新编译做好准备
- 编译(compile):将Java 源程序编译为字节码文件
- 测试(test):针对项目中的关键点进行测试,确保项目在迭代开发过程中关键点的正确性
- 报告():在每一次测试后以标准的格式记录和展示测试结果
- 打包(package):将一个包含诸多文件的工程封装为一个压缩文件用于安装或部署。Java 工程对应 jar 包,Web工程对应 war 包。
- 安装(install):在 Maven 环境下特指将打包的结果——jar 包或 war 包安装到本地仓库中。
- 部署(deploy):将打包的结果部署到远程仓库或将 war 包部署到服务器上运行。
四、Spring Cloud
1.什么是微服务?
微服务架构就是将单体的应用程序分成多个应用程序,这多个应用程序就成为微服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。
2.Spring Cloud 是什么
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
3.SpringCloud的优缺点
**优点:
1.耦合度比较低。不会影响其他模块的开发。
2.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。
3.配置比较简单,基本用注解就能实现,不用使用过多的配置文件。
4.==微服务跨平台的,==可以用任何一种语言开发。
5.每个微服务可以有自己的独立的数据库也有用公共的数据库。
6.直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。
缺点:
1.**部署比较麻烦,**给运维工程师带来一定的麻烦。
2.针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。
3.系统集成测试比较麻烦
4.性能的监控比较麻烦。【最好开发一个大屏监控系统】
总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。
4.SpringBoot和SpringCloud的区别?
1.SpringBoot专注于快速方便的开发单个个体微服务。
2.SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,
为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
**3.**SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系
**4.**SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
5.SpringCloud由什么组成
这就有很多了,我讲几个开发中最重要的
Spring Cloud Eureka:服务注册与发现
Spring Cloud Zuul:服务网关
Spring Cloud Ribbon:客户端负载均衡
Spring Cloud Feign:声明性的Web服务客户端
Spring Cloud Hystrix:断路器
Spring Cloud Config:分布式统一配置管理
等20几个框架,开源一直在更新
6.Spring Cloud 和dubbo区别?
(1)服务调用方式:dubbo是RPC springcloud Rest Api
(2)注册中心:dubbo 是zookeeper springcloud是eureka,也可以是zookeeper
(3)服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。
五、MyBatis
1.什么是Mybatis
MyBatis 是一款的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO映射成数据库中的记录。
2.MyBatis的优缺点
- 基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用
- 与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
- 很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)
- 提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护
- 能够与Spring很好的集成
缺点
- SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求
- SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
2.Hibernate 和 MyBatis 的区别
相同点
- 都是对jdbc的封装,都是持久层的框架,都用于dao层的开发。
不同点
- 映射关系
- MyBatis 是一个半自动映射的框架,配置Java对象与sql语句执行结果的对应关系,多表关联关系配置简单
- Hibernate 是一个全表映射的框架,配置Java对象与数据库表的对应关系,多表关联关系配置复杂
3.ORM是什么
- ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。
4.为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
- Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
- 而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
5.MyBatis编程步骤是什么样的?
- 1、 创建SqlSessionFactory
- 2、 通过SqlSessionFactory创建SqlSession
- 3、 通过sqlsession执行数据库操作
- 4、 调用session.commit()提交事务
- 5、 调用session.close()关闭会话
6.请说说MyBatis的工作原理
- 在学习 MyBatis 程序之前,需要了解一下 MyBatis 工作原理,以便于理解程序。MyBatis 的工作原理如下图
- 读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
- 加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
- **构造会话工厂:**通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
- 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
- Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
- **MappedStatement 对象:**在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
- **输入参数映射:**输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
- 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是**基本数据类型和 POJO 类型。**输出结果映射过程类似于 JDBC 对结果集的解析过程。
7.什么是DBMS
- DBMS:数据库管理系统(database management system)是一种操纵和管理数据库的大型软件,用于建立、使用和维护数zd据库,简称dbms。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过dbms访问数据库中的数据,数据库管理员也通过dbms进行数据库的维护工作。它可使多个应用程序和用户用不同的方法在同时版或不同时刻去建立,修改和询问数据库。**DBMS提供数据定义语言DDL(Data Definition Language)与数据操作语言DML(**Data Manipulation Language),供用户定义数据库的模式结构与权限约束,实现对数据的追加权、删除等操作。
8.Mybatis都有哪些Executor执行器?它们之间的区别是什么?
- Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
- SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
- ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
- BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
9.Mybatis中如何指定使用哪一种Executor执行器?
- 在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。
- 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
10.#{}和${}的区别
- #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。
- Mybatis在处理**#{}时,#{}传入参数是以字符串传入**,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
- #{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入
- #{} 的变量替换是在DBMS 中;${} 的变量替换是在 DBMS 外
11.在mapper中如何传递多个参数*(四个)
方法1:顺序传参法
public User selectUser(String name, int deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{0} and dept_id = #{1}
</select>
复制代码
- #{}里面的数字代表传入参数的顺序。
- 这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
方法2:@Param注解传参法
public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
复制代码
- #{}里面的名称对应的是注解@Param括号里面修饰的名称。
- 这种方法在参数不多的情况还是比较直观的,(推荐使用)。
方法3:Map传参法
public User selectUser(Map<String, Object> params);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
复制代码
- #{}里面的名称对应的是Map里面的key名称。
- 这种方法适合传递多个参数,且参数易变能灵活传递的情况。(推荐使用)。
方法4:Java Bean传参法
public User selectUser(User user);
<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
复制代码
- #{}里面的名称对应的是User类里面的成员属性。
- 这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方便,推荐使用。(推荐使用)。
12.如何获取生成的主键
-
新增标签中添加:keyProperty=" ID " 即可
<insert id="insert" useGeneratedKeys="true" keyProperty="userId" > insert into user( user_name, user_password, create_time) values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP}) </insert>
13.Mapper 编写有哪几种方式?
-
**第一种:接口实现类继承 SqlSessionDaoSupport:**使用此种方法需要编写mapper 接口,mapper 接口实现类、mapper.xml 文件。
-
在 sqlMapConfig.xml 中配置 mapper.xml 的位置
<mappers> <mapper resource="mapper.xml 文件的地址" /> <mapper resource="mapper.xml 文件的地址" /> </mappers> 复制代码
-
定义 mapper 接口
-
实现类集成 SqlSessionDaoSupport
mapper 方法中可以 this.getSqlSession()进行数据增删改查。
-
spring 配置
<bean id=" " class="mapper 接口的实现"> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean> 复制代码
-
-
第二种:使用 org.mybatis.spring.mapper.MapperFactoryBean:
-
在 sqlMapConfig.xml 中配置 mapper.xml 的位置,如果 mapper.xml 和mappre 接口的名称相同且在同一个目录,这里可以不用配置
-
定义 mapper 接口:
<mappers> <mapper resource="mapper.xml 文件的地址" /> <mapper resource="mapper.xml 文件的地址" /> </mappers> 复制代码
-
mapper.xml 中的 namespace 为 mapper 接口的地址
-
mapper 接口中的方法名和 mapper.xml 中的定义的 statement 的 id 保持一致
-
Spring 中定义
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="mapper 接口地址" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> 复制代码
-
-
第三种:使用 mapper 扫描器:
-
mapper.xml 文件编写:
mapper.xml 中的 ==namespace ==为 mapper 接口的地址;
mapper 接口中的方法名和 mapper.xml 中的定义的 statement 的 id 保持一致;
如果将 mapper.xml 和 mapper 接口的名称保持一致则不用在 sqlMapConfig.xml中进行配置。
-
定义 mapper 接口:
注意 mapper.xml 的文件名和 mapper 的接口名称保持一致,且放在同一个目录
-
配置 mapper 扫描器:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="mapper 接口包地址 "></property> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> 复制代码
-
使用扫描器后从 spring 容器中获取 mapper 的实现对象。
-
14.什么是MyBatis的接口绑定?有哪些实现方式?
- 接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
- 接口绑定有两种实现方式
- 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
- 通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。\
15.这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗
- Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
- Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
16.简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系?
- 答:Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。在Xml映射文件中,
<parameterMap>
标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。<resultMap>
标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个**<select>
、<insert>
、<update>
、<delete>
标签均会被解析为MappedStatement对象**,标签内的sql会被解析为BoundSql对象
17.了解MyBatis缓存机制吗?
参考答案
MyBatis的缓存分为一级缓存和二级缓存。
一级缓存:
一级缓存也叫本地缓存,它默认会启用,并且不能关闭。一级缓存存在于SqlSession的生命周期中,即它是SqlSession级别的缓存。在同一个 SqlSession 中查询时,MyBatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个Map对象中。如果同一个SqlSession 中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当Map 缓存对象中己经存在该键值时,则会返回缓存中的对象。
二级缓存:
二级缓存存在于SqlSessionFactory 的生命周期中,即它是SqlSessionFactory级别的缓存。若想使用二级缓存,需要在如下两处进行配置。
在MyBatis 的全局配置settings 中有一个参数cacheEnabled,这个参数是二级缓存的全局开关,默认值是==true ==,初始状态为启用状态。
MyBatis 的二级缓存是和命名空间绑定的,即二级缓存需要配置在Mapper.xml 映射文件中。在保证二级缓存的全局配置开启的情况下,给Mapper.xml 开启二级缓存只需要在Mapper. xml 中添加如下代码:
<cache />
二级缓存具有如下效果:
- 映射语句文件中的所 有SELECT 语句将会被缓存。
- 映射语句文件中的所有时INSERT 、UPDATE 、DELETE 语句会刷新缓存。
- 缓存会使用Least Rece ntly U sed ( LRU ,最近最少使用的)算法来收回。
- 根据时间表(如no Flush Int erv al ,没有刷新间隔),缓存不会以任何时间顺序来刷新。
- 缓存会存储集合或对象(无论查询方法返回什么类型的值)的1024 个引用。
- 缓存会被视为read/write(可读/可写)的,意味着对象检索不是共享的,而且****可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
六、MySQL
1.什么是MySQL?
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。
2.数据库经常使用的函数
count(*/column):返回行数
sum(column): 返回指定列中唯一值的和
max(column):返回指定列或表达式中的数值最大值
min(column):返回指定列或表达式中的数值最小值
avg(column):返回指定列或表达式中的数值平均值
date(Expression): 返回指定表达式代表的日期值
3.mysql有哪些数据类型
分类 | 类型名称 | 说明 |
---|---|---|
整数类型 | tinyInt | 很小的整数(8位二进制) |
整数类型 | smallint | 小的整数(16位二进制) |
整数类型 | mediumint | 中等大小的整数(24位二进制) |
整数类型 | int(integer) | 普通大小的整数(32位二进制) |
小数类型 | float | 单精度浮点数 |
小数类型 | double | 双精度浮点数 |
小数类型 | decimal(m,d) | 压缩严格的定点数 |
日期类型 | year | YYYY 1901~2155 |
日期类型 | time | HH:MM:SS -838:59:59~838:59:59 |
日期类型 | date | YYYY-MM-DD 1000-01-01~9999-12-3 |
日期类型 | datetime | YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00~ 9999-12-31 23:59:59 |
日期类型 | timestamp | YYYY-MM-DD HH:MM:SS 19700101 00:00:01 UTC~2038-01-19 03:14:07UTC |
文本、二进制类型 | CHAR(M) | M为0~255之间的整数 |
文本、二进制类型 | VARCHAR(M) | M为0~65535之间的整数 |
文本、二进制类型 | TINYBLOB | 允许长度0~255字节 |
文本、二进制类型 | BLOB | 允许长度0~65535字节 |
文本、二进制类型 | MEDIUMBLOB | 允许长度0~167772150字节 |
文本、二进制类型 | LONGBLOB | 允许长度0~4294967295字节 |
文本、二进制类型 | TINYTEXT | 允许长度0~255字节 |
文本、二进制类型 | TEXT | 允许长度0~65535字节 |
文本、二进制类型 | MEDIUMTEXT | 允许长度0~167772150字节 |
文本、二进制类型 | LONGTEXT | 允许长度0~4294967295字节 |
文本、二进制类型 | VARBINARY(M) | 允许长度0~M个字节的变长字节字符串 |
文本、二进制类型 | BINARY(M) | 允许长度0~M个字节的定长字节字符串 |
4.MyISAM与InnoDB区别
比较 MyISAM Innodb 存储结构 每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB 存储空间 MyISAM可被压缩,存储空间较小 InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引 可移植性、备份及恢复 由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作 免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了 文件格式 数据和索引是分别存储的,数据 .MYD
,索引.MYI
数据和索引是集中存储的, .ibd
记录存储顺序 按记录插入顺序保存 按主键大小有序插入 外键 不支持 支持 事务 不支持 支持 锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) 表级锁定 行级锁定、表级锁定,锁定力度小并发能力高 SELECT MyISAM更优 – INSERT、UPDATE、DELETE – InnoDB更优 select count(*) myisam更快,因为myisam内部维护了一个计数器,可以直接调取。 索引的实现方式 B+树索引,myisam 是堆表 B+树索引,Innodb 是索引组织表 哈希索引 不支持 支持 全文索引 支持 不支持
MyISAM索引与InnoDB索引的区别?
- InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
- InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
- MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
- InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。
5.什么是索引?
- 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
- 索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
- 更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
6.什么是数据库事务?
- 事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。
7.事务的四大特性(ACID)
**原子性(Atomicity):**事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立
环境下运行。
持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
8.并发事务问题 脏读、不可重复读、幻读
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JLckcdez-1660268383669)(java%E9%9D%A2%E8%AF%95%E9%A2%98-%E9%9C%80%E8%A6%81%E8%83%8C%E7%9A%84%E5%86%85%E5%AE%B9.assets/image-20220803161755111.png)]
9.什么是事务的隔离级别?MySQL的默认隔离级别是什么?
为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
SQL 标准定义了四个隔离级别:
- READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
- READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE(==可串行化=): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
注意:
- 这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的=== READ_COMMITTED ===隔离级别
- 事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
- 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
- InnoDB 存储引擎在 分布式事务 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
10.SQL语句主要分为哪几类(DQMC)
-
数据定义语言DDL(Data Ddefinition Language)CREATE,DROP,ALTER
主要为以上操作 即对逻辑结构等有操作的,其中包括表结构,视图和索引。
-
**数据查询语言DQL(Data Query Language)**SELECT
这个较为好理解 即查询操作,以select关键字。各种简单查询,连接查询等 都属于DQL。
-
**数据操纵语言DML(Data Manipulation Language)**INSERT,UPDATE,DELETE
主要为以上操作 即对数据进行操作的,对应上面所说的查询操作 DQL与DML共同构建了多数初级程序员常用的增删改查操作。而查询是较为特殊的一种 被划分到DQL中。
-
**数据控制功能DCL(Data Control Language)**GRANT,REVOKE,COMMIT,ROLLBACK
主要为以上操作 即对数据库安全性完整性等有操作的,可以简单的理解为权限控制等
11.MySQL中
#七、数据结构与算法基础结构及方法
1.栈
栈是一种基于先进后出(FILO)的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出
的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一
个数据被第一个读出来)。
我们称数据进入到栈的动作为压栈,数据从栈中出去的动作为弹栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bfW0Rium-1660268383669)(java%E9%9D%A2%E8%AF%95%E9%A2%98-%E9%9C%80%E8%A6%81%E8%83%8C%E7%9A%84%E5%86%85%E5%AE%B9.assets/image-20220805090058012.png)]
1.2 栈Stack方法
package java.util;
public class Stack<E> extends Vector<E> {
/**
* 无参构造函数
*/
public Stack() {
}
/**
* 将元素加入栈顶
*/
public E push(E item) {
addElement(item);
return item;
}
/**
* 获取当前栈顶元素并删除
*/
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
/**
* 获取栈顶元素
*/
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
/**
* 判断当前栈是否为空
*/
public boolean empty() {
return size() == 0;
}
/**
* 查找元素在栈中的位置,由栈低向栈顶数
*/
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
/** 序列化id (serialVersionUID)
1. 序列化ID,相当于身份认证,主要用于程序的版本控制,保持不同版本的兼容性,在程序版本升级时避免程序报出版本不一致的错误。
2.如果定义了private static final long serialVersionUID = 1L,那么如果你忘记修改这个信息,而且你对这个类进行修改的话,这个类也能被进行反序列化,而且不会报错。一个简单的概括就是,如果你忘记修改,那么它是会版本向上兼容的。
3.如果没有定义一个名为serialVersionUID,类型为long的变量,Java序列化机制会根据编译的class自动生成一个serialVersionUID,即隐式声明。这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。此时如果对某个类进行修改的话,那么版本上面是不兼容的,就会出现反序列化报错的情况。*/
private static final long serialVersionUID = 1224463164541339165L;
}
2.队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
package java.util;
/**
* 队列
*/
public interface Queue<E> extends Collection<E> {
/**
-add()方法可能无法插入元素,而是抛出一个IllegalStateException异常
*/
boolean add(E e);
/**
将指定的元素插入此队列(如果立即可行且不会违反容量限制),插入成功返回true,否则返回false。当使用有容量限制的队列时
*/
boolean offer(E e);
/**
获取并移出此队列的头,如果此队列为空则抛出NoSuchElementException异常
*/
E remove();
/**
获取并移出队列的头,如果次队列为空,则返回null。
*/
E poll();
/**
元素
*/
E element();
/**
*获取但是不移除
*/
E peek();
}
八、设计模式
1.什么是设计模式?
- **设计模式,**是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
2.设计模式的分类(5(创建)-7(结构)-11(行为))
3.设计模式的六大原则
3.1开放封闭原则(Open Close Principle)
- 原则思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
- 描述:一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
- 优点:单一原则告诉我们,每个类都有自己负责的职责,里氏替换原则不能破坏继承关系的体系。
3.2里氏代换原则(Liskov Substitution Principle)
- 原则思想:使用的基类可以在任何地方使用继承的子类,完美的替换基类。
- 大概意思是:子类可以扩展父类的功能,但不能改变父类原有的功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,子类中可以增加自己特有的方法。
- 优点:增加程序的健壮性,即使增加了子类,原有的子类还可以继续运行,互不影响。
3.3.依赖倒转原则(Dependence Inversion Principle)
- 依赖倒置原则的核心思想是面向接口编程.
- 依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,
- 这个是开放封闭原则的基础,具体内容是:对接口编程,依赖于抽象而不依赖于具体。
3.4接口隔离原则(Interface Segregation Principle)
- 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
- 例如:支付类的接口和订单类的接口,需要把这俩个类别的接口变成俩个隔离的接口
3.5迪米特法则(最少知道原则)(Demeter Principle)
- 原则思想:一个对象应当对其他对象有尽可能少地了解,简称类间解耦=
- 大概意思就是一个类尽量减少自己对其他对象的依赖,原则是低耦合,高内聚,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。
- 优点:低耦合,高内聚。
3.6单一职责原则(Principle of single responsibility)
- 原则思想:一个方法只负责一件事情。
- 描述:单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。 这是常识,几乎所有程序员都会遵循这个原则。
- 优点:降低类和类的耦合,提高可读性,增加可维护性和可拓展性,降低可变性的风险。
4…Spring开发中的工厂设计模式
1.Spring IOC
- 看过Spring源码就知道,在Spring IOC容器创建bean的过程是使用了工厂设计模式
- Spring中无论是通过xml配置还是通过配置类还是注解进行创建bean,大部分都是通过简单工厂来进行创建的。
- 当容器拿到了beanName和class类型后,动态的通过反射创建具体的某个对象,最后将创建的对象放到Map中。
2.为什么Spring IOC要使用工厂设计模式创建Bean呢
- 在实际开发中,如果我们A对象调用B,B调用C,C调用D的话我们程序的耦合性就会变高。(耦合大致分为类与类之间的依赖,方法与方法之间的依赖。)
- 在很久以前的三层架构编程时,都是控制层调用业务层,业务层调用数据访问层时,都是是直接new对象,耦合性大大提升,代码重复量很高,对象满天飞
- 为了避免这种情况,Spring使用工厂模式编程,写一个工厂,由工厂创建Bean,以后我们如果要对象就直接管工厂要就可以,剩下的事情不归我们管了。Spring IOC容器的工厂中有个静态的Map集合,是为了让工厂符合单例设计模式,即每个对象只生产一次,生产出对象后就存入到Map集合中,保证了实例不会重复影响程序效率。
5.1.什么是代理模式
- 通过代理控制对象的访问,可以在这个对象调用方法之前、调用方法之后去处理/添加新的功能。(也就是AO的P微实现)
- 代理在原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码,增加新功能,这也和Spring的(面向切面编程)很相似
1.代理模式应用场景
- Spring AOP、日志打印、异常处理、事务控制、权限控制等
2.代理的分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类,也称为Jdk自带动态代理)
- Cglib 、javaassist(字节码操作库)
3.三种代理的区别
- 静态代理:简单代理模式,是动态代理的理论基础。常见使用在代理模式
- jdk动态代理:使用反射完成代理。需要有顶层接口才能使用,常见是mybatis的mapper文件是代理。
- cglib动态代理:也是使用反射完成代理,可以直接代理类(jdk动态代理不行),使用字节码技术,不能对 final类进行继承。(需要导入jar包)
九、Dubbo
1.什么是Dubbo?
- Dubbo 是一款高性能、轻量级的开源 RPC (Remote Procedure Call)框架,提供服务自动注册、自动发现等高效服务治理方案, 可以和 Spring 框架无缝集成。
2.Dubbo 核心功能有哪些?
- Remoting:网络通信框架,提供对多种NIO框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。
- Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
- Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
3.Dubbo核心组件
- Provider:暴露服务的服务提供方
- Consumer:调用远程服务消费方
- Registry:服务注册与发现注册中心
- Monitor:监控中心和访问调用统计
- Container:服务运行容器
4.Dubbo 服务器注册与发现的流程?
- 服务容器Container负责启动,加载,运行服务提供者。
- 服务提供者Provider在启动时,向注册中心注册自己提供的服务。
- 服务消费者Consumer在启动时,向注册中心订阅自己所需的服务。
- 注册中心Registry返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者Consumer,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者Consumer和提供者Provider,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心Monitor。
5.Dubbo 的整体架构设计有哪些分层?
- 接口服务层(Service):该层与业务逻辑相关,根据 provider 和 consumer 的业务设计对应的接口和实现
- 配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心
- 服务代理层(Proxy):服务接口透明代理,生成服务的客户端 Stub 和 服务端的 Skeleton,以 ServiceProxy 为中心,扩展接口为 ProxyFactory
- 服务注册层(Registry):封装服务地址的注册和发现,以服务 URL 为中心,扩展接口为 RegistryFactory、Registry、RegistryService
- 路由层(Cluster):封装多个提供者的路由和负载均衡,并桥接注册中心,以Invoker 为中心,扩展接口为 Cluster、Directory、Router 和 LoadBlancce
- 监控层(Monitor):RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory、Monitor 和 MonitorService
- 远程调用层(Protocal):封装 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为 Protocal、Invoker 和 Exporter
- 信息交换层(Exchange):封装请求响应模式,同步转异步。以 Request 和Response 为中心,扩展接口为 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer
- 网络 传输 层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel、Transporter、Client、Server 和 Codec
- 数据序列化层(Serialize):可复用的一些工具,扩展接口为 Serialization、ObjectInput、ObjectOutput 和 ThreadPool
6.Dubbo 和 Spring Cloud 有什么哪些区别?
- Dubbo 底层是使用 Netty 这样的 NIO 框架,是基于 TCP 协议传输的,配合以 Hession 序列化完成 RPC 通信。
- Spring Cloud 是基于 Http 协议 Rest 接口调用远程过程的通信,相对来说 Http 请求会有更大的报文,占的带宽也会更多。但是 REST 相比 RPC 更为灵活,服务提供方和调用方的依赖只依靠一纸契约,不存在代码级别的强依赖,这在强调快速演化的微服务环境下,显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。
7.Dubbo 有哪些注册中心?
- Multicast 注册中心:Multicast 注册中心不需要任何中心节点,只要广播地址,就能进行服务注册和发现,基于网络中组播传输实现。
- Zookeeper 注册中心:基于分布式协调系统 Zookeeper 实现,采用 Zookeeper 的 watch 机制实现数据变更。
- Redis 注册中心:基于 Redis 实现,采用 key/map 存储,key 存储服务名和类型,map 中 key 存储服务 url,value 服务过期时间。基于 Redis 的发布/订阅模式通知数据变更。
- Simple 注册中心。
- 推荐使用 Zookeeper 作为注册中心
8.Dubbo集群提供了哪些负载均衡策略?
- Random LoadBalance: 随机选取提供者策略,有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀。
- RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是存在请求累积的问题。
- LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收更少的请求。
- ConstantHash LoadBalance: 一致性 Hash 策略,使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提供者的剧烈变动。
9.说说核心的配置有哪些?
标签 | 用途 | 解释 |
---|---|---|
dubbo:service/ | 服务配置 | 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心 |
dubbo:reference/ | 引用配置 | 用于创建一个远程服务代理,一个引用可以指向多个注册中心 |
dubbo:protocol/ | 协议配置 | 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受 |
dubbo:application/ | 应用配置 | 用于配置当前应用信息,不管该应用是提供者还是消费者 |
dubbo:module/ | 模块配置 | 用于配置当前模块信息,可选 |
dubbo:registry/ | 注册中心配置 | 用于配置连接注册中心相关信息 |
dubbo:monitor/ | 监控中心配置 | 用于配置连接监控中心相关信息,可选 |
dubbo:provider/ | 提供方配置 | 当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选 |
dubbo:consumer/ | 消费方配置 | 当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选 |
dubbo:method/ | 方法配置 | 用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息 |
dubbo:argument | 参数配置 | 用于指定方法参数配置 |
十.Zookeeper
1.ZooKeeper 是什么?
-
ZooKeeper 是一个开源的分布式协调服务。它是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
-
ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
-
Zookeeper 保证了如下分布式一致性特性:
(1)顺序一致性
(2)原子性
(3)单一视图
(4)可靠性
(5)实时性(最终一致性)
-
客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了监听器,这个监听器也是由所连接的 zookeeper 机器来处理。对于写请求,这些请求会同时发给其他 zookeeper 机器并且达成一致后,请求才会返回成功。因此,随着 zookeeper 的集群机器增多,读请求的吞吐会提高但是写请求的吞吐会下降。
-
有序性是 zookeeper 中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为 zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个zookeeper 最新的 zxid。
2.Zookeeper 文件系统
- Zookeeper 提供一个多层级的节点命名空间(节点称为 znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。
- Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M
3.客户端注册 Watcher 实现
- (1)调用 getData()/getChildren()/exist()三个 API,传入 Watcher 对象
- (2)标记请求 request,封装 Watcher 到 WatchRegistration
- (3)封装成 Packet 对象,发服务端发送 request
- (4)收到服务端响应后,将 Watcher 注册到 ZKWatcherManager 中进行管理
- (5)请求返回,完成注册。
4.服务端处理 Watcher 实现
-
服务端接收 Watcher 并存储 接收到客户端请求,处理请求判断是否需要注册 Watcher,需要的话将数据节点的节点路径和 ServerCnxn(ServerCnxn 代表一个客户端和服务端的连接,实现了 Watcher 的 process 接口,此时可以看成一个 Watcher 对象)存储在WatcherManager 的 WatchTable 和 watch2Paths 中去。
-
Watcher 触发 以服务端接收到 setData() 事务请求触发 NodeDataChanged 事件为例:
2.1 封装 WatchedEvent 将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个 WatchedEvent 对象
2.2 查询 Watcher 从 WatchTable 中根据节点路径查找 Watcher
2.3 没找到;说明没有客户端在该数据节点上注册过 Watcher
2.4 找到;提取并从 WatchTable 和 Watch2Paths 中删除对应 Watcher(从这里可以看出 Watcher 在服务端是一次性的,触发一次就失效了)
-
调用 process 方法来触发 Watcher 这里 process 主要就是通过 ServerCnxn 对应的 TCP 连接发送 Watcher 事件通知。
5.服务器角色
-
Leader
(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性
(2)集群内部各服务的调度者
-
Follower
(1)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(2)参与事务请求 Proposal 的投票
(3)参与 Leader 选举投票
-
Observer
(1)3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
(2)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(3)不参与任何形式的投票
6.服务器角色
-
Leader
(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性
(2)集群内部各服务的调度者
-
Follower
(1)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(2)参与事务请求 Proposal 的投票
(3)参与 Leader 选举投票
-
Observer
(1)3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
(2)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(3)不参与任何形式的投票
7.服务器角色
-
Leader
(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性
(2)集群内部各服务的调度者
-
Follower
(1)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(2)参与事务请求 Proposal 的投票
(3)参与 Leader 选举投票
-
Observer
(1)3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
(2)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(3)不参与任何形式的投票
8.Zookeeper 和 Dubbo 的关系?
-
Zookeeper的作用: zookeeper用来==注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。当然也可以通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服**务。zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务==对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码的情况通过添加机器来提高运算能力。通过添加新的机器向zookeeper注册服务**,服务的提供者多了能服务的客户就多了。
-
dubbo: 是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。
注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。 -
zookeeper和dubbo的关系: Dubbo 的将注册中心进行抽象,它可以外接不同的存储媒介给注册中心提供服务,有 ZooKeeper,Memcached,Redis 等。
引入了 ZooKeeper 作为存储媒介,也就把 ZooKeeper 的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时候就需要分流,负载均衡就是为了分流而存在的,一个 ZooKeeper 群配合相应的 Web 应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不 够,节点之间的数据和资源需要同步,ZooKeeper 集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向 ZooKeeper 上的指定节点 /dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。 其他特性还有 Mast 选举,分布式锁等。
十一、Redis
1.什么是Redis?
- Redis 是一个使用 C 语言写成的,开源的高性能key-value非关系缓存数据库=。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。Redis的数据都基于缓存的,所以很快,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。Redis也可以实现数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。
2.Redis有哪些优缺点?
- 优点
- 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
- 缺点
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
- Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
3.为什么要用 Redis / 为什么要用缓存
主要从“高性能”和“高并发”这两点来看待这个问题。
- 高性能:
- 假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
- 高并发:
- 直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
4.Redis为什么这么快
- 1**、==完全基于内存==,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1**);
- 2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
- 3、*采用单线程*,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
- 4、使用多路 I/O 复用模型,非阻塞 IO;
- 5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
5.Redis 的持久化机制是什么?各自的优缺点?
- Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:
RDB:是Redis DataBase缩写快照
- RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
-
优点:
1、只有一个文件 dump.rdb,方便持久化。
2、**容灾性好,**一个文件可以保存到安全的磁盘。
3、性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
4.相对于数据集大时**,比 AOF 的启动效率更高。**
-
缺点:
1、**数据安全性低。**RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
2、AOF(Append-only file)持久化方式: 是指所有的命令行记录以 redis 命令请 求协议的格式完全持久化存储)保存为 aof 文件。
AOF:持久化:
- AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
- 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复
-
优点:
1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。
2、通过== append 模式==写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))
-
缺点:
1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集大的时候,比 rdb 启动效率低。
-
俩种持久化的优缺点是什么?
- AOF文件比RDB更新频率高,优先使用AOF还原数据。
- AOF比RDB更安全也更大
- RDB性能比AOF好
- 如果两个都配了优先加载AOF
6.Redis的过期键的删除策略
我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
- 过期策略通常有以下三种:
- **定时过期:**每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
- **惰性过期:**只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
- **定期过期:****每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,**并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。 (expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
- Redis中同时使用了惰性过期和定期过期两种过期策略。
7.Redis key的过期时间和永久有效分别怎么设置?
- expire和persist命令。
8.事务管理(ACID)概述
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 - 一致性(Consistency)
事务前后数据的完整性必须保持一致。 - 隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行 - 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
9.什么是redis穿透?
- 就是用户请求透过redis去请求mysql服务器,导致mysql压力过载。但一个web服务,极容易出现瓶颈的就是mysql,所以才让redis去分担mysql 的压力,所以这种问题是万万要避免的
- 解决方法:
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
10.什么是redis雪崩?
- 就是redis服务由于负载过大而宕机,导致mysql的负载过大也宕机,最终整个系统瘫痪
- 解决方法:
- redis集群,将原来一个人干的工作,分发给多个人干
- 缓存预热(关闭外网访问,先开启mysql,通过预热脚本将热点数据写入缓存中,启动缓存。开启外网服务)
- 数据不要设置相同的生存时间,不然过期时,redis压力会大
11.缓存预热
- 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
- 解决方案
- 直接写个缓存刷新页面,上线时手工操作一下;
- 数据量不大,可以在项目启动的时候自动进行加载;
- 定时刷新缓存;
12.缓存降级
- 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
- 缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
- 在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
- **一般:**比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
- 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
- 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
- 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
- 服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,**对于不重要的缓存数据,可以采取服务降级策略,**例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
13.热点数据和冷数据
- 热点数据,缓存才有价值
- 对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存
- 对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
- 数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
- 那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
14.如何保证缓存与数据库双写时的数据一致性?
- 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?
- 一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况
- 串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
- 还有一种方式就是可能会暂时产生不一致的情况,但是发生的几率特别小,就是先更新数据库,然后再删除缓存。
问题场景 | 描述 | 解决 |
---|---|---|
先写缓存,再写数据库,缓存写成功,数据库写失败 | 缓存写成功,但写数据库失败或者响应延迟,则下次读取(并发读)缓存时,就出现脏读 | 这个写缓存的方式,本身就是错误的,需要改为先写数据库,把旧缓存置为失效;读取数据的时候,如果缓存不存在,则读取数据库再写缓存 |
先写数据库,再写缓存,数据库写成功,缓存写失败 | 写数据库成功,但写缓存失败,则下次读取(并发读)缓存时,则读不到数据 | 缓存使用时,假如读缓存失败,先读数据库,再回写缓存的方式实现 |
需要缓存异步刷新 | 指数据库操作和写缓存不在一个操作步骤中,比如在分布式场景下,无法做到同时写缓存或需要异步刷新(补救措施)时候 | 确定哪些数据适合此类场景,根据经验值确定合理的数据不一致时间,用户数据刷新的时间间隔 |
目前使用的Redis缓存更新策略:
1.查询数据库时:
先查询缓存,缓存中没有再查询数据库,然后把数据写入缓存,然后返回数据
2.修改数据库时:
先修改数据库,然后再删除缓存,确保两者的原子性
十二、MongoDB
1.MongoDB是什么?
MongoDB是由C++语言编写的**,是一个基于分布式文件存储的开源数据库系统。 在高负载的情况下,添加更多的节点,可以保证服务器性能。 MongoDB旨在为WEB应用提供可扩展的高性能数据存储解决方案**。
MongoDB将数据存储为一个文档,数据结构由**键值(key=>value)**对组成。 MongoDB文档类似于JSON对象。字段值可以包含其他文档,数组及文档数组。
redis:数据量较小的更性能操作和运算上
MongoDB:主要解决海量数据的访问效率问题
2.MongoDB有哪些特点?
答:
1.MongoDB是一个==**面向文档存储==**的数据库,操作起来比较简单和容易。
2.你可以在MongoDB记录中设置任何属性的索引 (如: FirstName=“Sameer”,Address=“8 Gandhi Road”)来实现更快的排序。
3.你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
4.如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
5.Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
6.MongoDb使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。 7.Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
8.Map和Reduce。 Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
9.Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
10.GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
11.MongoDB允许在服务端执行脚本, 可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
3.mongoDB与Redis的特点及应用场景
mongoDB
1 特点
(1) 面向集合存储,易存储对象类型的数据
(2) 支持动态查询
(3) 支持完全索引,包含内部对象
(4) 支持复制和故障恢复
(5) 支持多种开发语言
(6) 使用高效的二进制数据存储,包括大型对象(如视频等)
2 适用场景
1)网站实时数据处理。它非常适合实时的插入、更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
2)缓存。由于性能很高,它适合作为信息基础设施的缓存层。在系统重启之后,由它搭建的持久化缓存层可以避免下层的数据源过载。
3)高伸缩性的场景。非常适合由数十或数百台服务器组成的数据库,它的路线图中已经包含对MapReduce引擎的内置支持。
3 不适用的场景如下
1)要求高度事务性的系统。
2)传统的商业智能应用。
3)复杂的跨文档(表)级联查询。
Redis
1 特点
(1) Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
(2) Redis不仅仅支持简单的**key-value类型的数据,**同时还提供list,set,zset,hash等数据结构的存储
(3) Redis支持数据的备份,即master-slave(主从)模式的数据备份
2 优势
(1) 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
(2) 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
(3) 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
(4) 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性
(5) 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不
用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
(6) 使用多路I/O复用模型,非阻塞IO;
3 应用场景
(1) 缓存(数据查询,短连接,新闻内容,商品内容等),使用最多
(2) 聊天室在线好友列表
(3) 任务队列(秒杀,抢购,12306等)
(4) 应用排行榜
(5) 网站访问统计
(6) 数据过期处理(可以精确到毫秒)
(7) 分布式集群架构中的session问题
4.MongoDB 数据库常用操作命令(链接)
https://www.cnblogs.com/jushen7925/p/16070270.html
十三、RabbitMQ面试题
1.Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
2.什么是RabbitMQ
- RabbitMQ是一款开源的消息中间件; 最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦 可以用它来:解耦、异步、削峰。
3.RabbitMQ基本概念
- Broker: 简单来说就是消息队列服务器实体
- Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
- Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
- Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来
- Routing Key: 路由关键字,exchange根据这个关键字进行消息投递
- VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。
- Producer: 消息生产者,就是投递消息的程序
- Consumer: 消息消费者,就是接受消息的程序
- Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路。
4.MQ的优点
- 简答
- 异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
- 应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
- 流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
- 日志处理 - 解决大量日志传输。
- 消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
- 详答
5.解耦、异步、削峰是什么?MQ有什么用?
参考答案
消息队列有很多使用场景,比较常见的有3个:解耦、异步、削峰。
- **解耦:**传统的软件开发模式,各个模块之间相互调用,数据共享,每个模块都要时刻关注其他模块的是否更改或者是否挂掉等等,使用消息队列,可以避免模块之间直接调用,将所需共享的数据放在消息队列中,对于新增业务模块,只要对该类消息感兴趣,即可订阅该类消息,对原有系统和业务没有任何影响,降低了系统各个模块的耦合度,提高了系统的可扩展性。
- **异步:消息队列提供了异步处理机制,**在很多时候应用不想也不需要立即处理消息,允许应用把一些消息放入消息中间件中,并不立即处理它,在之后需要的时候再慢慢处理。
- 削峰:在访问量骤增的场景下,需要保证应用系统的平稳性,但是这样突发流量并不常见,如果以这类峰值的标准而投放资源的话,那无疑是巨大的浪费。使用消息队列能够使关键组件支撑突发访问压力,不会因为突发的超负荷请求而完全崩溃。消息队列的容量可以配置的很大,如果采用磁盘存储消息,则几乎等于“无限”容量,这样一来,高峰期的消息可以被积压起来,在随后的时间内进行平滑的处理完成,而不至于让系统短时间内无法承载而导致崩溃。在电商网站的秒杀抢购这种突发性流量很强的业务场景中,消息队列的强大缓冲能力可以很好的起到削峰作用。
-
例子
-
解耦**:A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃…A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。
就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦。
-
异步:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求。如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms。
-
削峰:减少高峰时期对服务器压力。
6.RabbitMQ的工作模式
一.简单队列模式(即最简单的收发模式)
- 生产者产生消息,将消息放入队列
- 消息的消费者(consumer) 监听 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患 消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失,这里可以设置成手动的ack,但如果设置成手动ack,处理完后要及时发送ack消息给队列,否则会造成内存溢出)。
二.工作队列模式(资源的竞争)
- 消息产生者将消息放入队列消费者可以有多个,消费者1,消费者2同时监听同一个队列,消息被消费。C1 C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息(隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize) 保证一条消息只能被一个消费者使用)。
三.广播模式
- 每个消费者监听自己的队列;
- 生产者将消息发给**broker,由交换机将消息转发到绑定此交换机的每个队列,**每个绑定交换机的队列都将接收到消息。
四.routing路由模式
- 消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;
- 根据业务功能定义路由字符串
- 从系统的代码逻辑中获取对应的功能字符串,将消息任务扔到对应的队列中。
- 业务场景:error 通知;EXCEPTION;错误通知的功能;传统意义的错误通知;客户通知;利用key路由,可以将程序中的错误封装成消息传入到消息队列中,开发者可以自定义消费者,实时接收错误;
五.publish/subscribe发布订阅(共享资源)
- 星号井号代表通配符
- 星号代表多个单词,井号代表一个单词
- 路由功能添加模糊匹配
- 消息产生者产生消息,把消息交给交换机
- 交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费
7.消息怎么路由?
- 消息提供方->路由->一至多个队列消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。通过队列路由键,可以把队列绑定到交换器上。消息到达交换器后,RabbitMQ 会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);
- 常用的交换器主要分为一下三种:
- fanout:如果交换器收到消息,将会广播到所有绑定的队列上
- **direct:**如果路由键完全匹配,消息就被投递到相应的队列
- **topic:**可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符
8.消息基于什么传输?
- 由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。
9.如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消费了消息?
发送方确认模式
- 将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的 ID。
- 一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。
- 如果 RabbitMQ 发生内部错误从而导致消息丢失,会发送一条 nack(notacknowledged,未确认)消息。
- 发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
接收方确认机制
- 消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。
- 这里并没有用到超时机制,RabbitMQ 仅通过 Consumer 的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ 给了 Consumer 足够长的时间来处理消息。保证数据的最终一致性;
十四、Java基础、
1.java反射的作用
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法。在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。 这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
2.哪里会用到反射机制?
jdbc就是典型的反射
这就是反射。如hibernate,struts等框架使用反射实现的。
3.反射的实现方式:
第一步:获取Class对象,有4中方法:
1)Class.forName(“类的路径”);
2)类名.class
3)对象名.getClass()
4)基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象
4.实现Java反射的类:
反射 API 用来生成 JVM 中的类、接口或则对象的信息。
- Class 类:反射的核心类,可以获取类的属性,方法等信息。
- Field 类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
- Method 类: Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
- Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。
5.利用反射动态创建对象实例
Class 对象的 newInstance()
-
使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。 调用 Constructor 对象的 newInstance()
-
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。
换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费
7.消息怎么路由?
- 消息提供方->路由->一至多个队列消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。通过队列路由键,可以把队列绑定到交换器上。消息到达交换器后,RabbitMQ 会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);
- 常用的交换器主要分为一下三种:
- fanout:如果交换器收到消息,将会广播到所有绑定的队列上
- **direct:**如果路由键完全匹配,消息就被投递到相应的队列
- **topic:**可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符
8.消息基于什么传输?
- 由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。
9.如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消费了消息?
发送方确认模式
- 将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的 ID。
- 一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。
- 如果 RabbitMQ 发生内部错误从而导致消息丢失,会发送一条 nack(notacknowledged,未确认)消息。
- 发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
接收方确认机制
- 消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。
- 这里并没有用到超时机制,RabbitMQ 仅通过 Consumer 的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ 给了 Consumer 足够长的时间来处理消息。保证数据的最终一致性;
十四、Java基础、
1.java反射的作用
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法。在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。 这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
2.哪里会用到反射机制?
jdbc就是典型的反射
[外链图片转存中…(img-MosFcMCF-1660268383672)]
这就是反射。如hibernate,struts等框架使用反射实现的。
3.反射的实现方式:
第一步:获取Class对象,有4中方法:
1)Class.forName(“类的路径”);
2)类名.class
3)对象名.getClass()
4)基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象
4.实现Java反射的类:
反射 API 用来生成 JVM 中的类、接口或则对象的信息。
- Class 类:反射的核心类,可以获取类的属性,方法等信息。
- Field 类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
- Method 类: Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
- Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。
5.利用反射动态创建对象实例
Class 对象的 newInstance()
-
使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。 调用 Constructor 对象的 newInstance()
-
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。
[外链图片转存中…(img-IMtMhCdP-1660268383672)]