最全java面试题整理(持续更新)

面试 专栏收录该内容
1 篇文章 0 订阅

1. springboot项目和maven项目的区别?

(1)打包方式:传统项目如果需要打成war包,需要在WEB-INF目录结构配置web.xml文件;springboot则不需要
(2)项目启动方式:
传统web项目启动方式:在eclipse和tomcat插件中导入项目,然后启动tomcat,项目也启动了。或者将项目打成war包,放入tomcat中,启动tomcat。
采用springboot项目启动:如下图所示,打开HelloWorldApplication.java(这个类每个项目都有,类名自定),这是一个带有main方法的类,点击main方法,右键run as -java application。
(3)配置文件的区别:
二者的配置文件都是放在src/main/resources下面
传统项目:配置文件较多,必须包含applicationContext.xml这个核心配置文件
采用springboot项目:配置文件较少,相比传统项目,可以说配置文件极少,必须包含application.properties(或者是application.yml)这个配置文件

2. spring mvc 工作流程

1、首先用户发送请求到前端控制器,前端控制器根据请求信息(如 URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;
2、页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,这个对象在 Spring Web MVC 中叫命令对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个 ModelAndView(模型数据和逻辑视图名);
3、前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;
4、前端控制器再次收回控制权,将响应返回给用户。
在这里插入图片描述

3. 请简述一下IO流?

IO流用来处理设备之间的数据传输,Java程序中,对于数据的输入/输出操作 都是以“流”的方式进行的。java.io包下提供了各种“流”类的接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。

对于计算机来说,数据都是以二进制形式读出或写入的。我们可以把文件想象为一个桶,我们可以通过管道将桶里的水抽出来。这里的管道也就相当于Java中的流。流的本质是一种有序的数据集合,有数据源和目的地。
在这里插入图片描述
输入:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出:将程序(内存)数据输出到磁盘、光盘等存储设备中。

流的分类
按照流的方向(输出输入都是站在程序所在内存的角度划分的)
输入流:只能从中读数据
输出流:只能向文件中写数据

在这里插入图片描述
字节流:将数据解释为原始的二进制数据 ,读写均为字节数据,二进制数据不需要编码和解码,比文本Io效率更高,可移植(与主句编码方式无关)

字符流:将数据解释为字符数据,字符流将原始数据解析成一种字符,文本数据存储 依赖文件编码方式,它输入输出需要编码解码。

4. 关系型数据库和非关系型数据库?

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。
当今十大主流的关系型数据库 Oracle,Microsoft SQL Server,MySQL,PostgreSQL,DB2, Microsoft Access, SQLite,Teradata,MariaDB(MySQL的一个分支),SAP

优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL语言通用,可用于复杂查询;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。

缺点:
1、读写性能较差,尤其是海量数据的高效率读写;
2、硬盘I/O要求高:网站的用户并发性非常高,往往达到每秒上万次读写请求,对于传统关系型数据库来说,硬盘I/O是一个很大的瓶颈
3、拓展困难
4、性能欠佳:在关系型数据库中,导致性能欠佳的最主要原因是多表的关联查询,以及复杂的数据分析类型的复杂SQL报表查询。为了保证数据库的ACID特性(原子性、一致性、隔离性、持久性),必须尽量按照其要求的范式进行设计,关系型数据库中的表都是存储一个格式化的数据结构。

非关系型数据库指非关系型的,分布式的,且一般不保证遵循ACID原则的数据存储系统。非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。

优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、查询便捷:可以根据需要去添加自己需要的字段,为了获取用户的不同信息,不像关系型数据库中,要对多表进行关联查询。仅需要根据id取出相应的value就可以完成查询。
3、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
4、高扩展性:Nosql基于键值对,数据之间没有耦合性,所以非常容易水平扩展。关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
5、成本低:nosql数据库部署简单,基本都是开源软件。

缺点:
1、不提供sql支持,学习和使用成本较高;
2、无事务处理;
3、只适合存储一些较为简单的数据,对于需要进行较复杂查询的数据,关系型数据库显的更为合适。
4、不适合持久存储海量数据

常见的非关系型数据库有redis,mongdb,Hbase。

5. MySQL数据库

优化SQL

  1. 选择正确的存储引擎。
    每个引擎都有利有弊,比如MyISAM,适用于大量查询,对大量写操作并不是很好,update一个字段都会把整个表锁起来,而Innodb,对一些小的应用,它比MyISAM慢,但它支持行锁,再写操作的时候,很优秀,它还支持更多的高级应用。
  2. 优化字段的数据类型
    一个原则,越小的越快,如果一个表只有几列,那我们就不用用INT来做主键,可以使用MEDIUMINTSMALLINT或是更小的TINYINT会更经济一些,如果不需要记录时间,使用DATE要比DATETIME好的多,也要留够足够的空间进行扩展。
  3. 为搜索字段添加索引
    索引不一定只添加给主键或唯一的字段,如果在表中有某个字段经常用来做搜索,那就为它建立索引,如果要搜索的字段是大的文本字段,那应该为它建立全文索引。
  4. 避免使用select *因为从数据库读出的数据越多,那么查询就会越慢。如果数据库服务和WEB服务器在不同的机器上的话,还会增加网络传输的负载。即使要查询表的所有字段,也尽量不要用*通配符。
  5. 使用ENUM而不是VARCHAR
    ENUM类型是非常快和紧凑的,它保存的是TINYINT,但外表上显示的是字符串,做一些选项列表很好,比如:性别,民族,部门,状态之类的字段,取值有限而且固定。
  6. 尽可能使用NOT NULL
    NULL其实也需要额外空间的,在进行比较的时候,程序也会变得复杂,并不是并不可以用NULL,在现实的复杂情况下,依然会有些情况需要使用NULL值。
  7. 固定长度的表会更快
    如果表中的所有字段都是固定长度的,那整个表会被认为是“static”或“fixed-lenght”。例如表中没有VARCHARTEXTBLOB,只要表中其中一个字段是这些类型,那么这个表就不是“固定长度静态表”了,这样的话MySQL引擎会用另一种方法来处理。
    固定长度的表也容易被缓存和重建,唯一的副作用就是,固定长度的字段会浪费一些空间,因为固定长度的字段无论用不用,都会分配那么多的空间。

为什么要避免使用join查询
减少消耗。

对于大流量网站,如何解决各页面统计访问量问题

  1. 确认服务器是否能支撑当前访问量
  2. 优化数据库访问
  3. 禁止外部访问,如图片盗链
  4. 控制文件下载
  5. 使用不同主机进行分流
  6. 使用浏览统计软件,了解访问量,有针对性的进行优化

SQL注入的主要特点

  1. 变种极多,攻击简单,危害极大。
  2. 未经授权操作数据库的数据。
  3. 恶意篡改网页。
  4. 网页挂木马。
  5. 私自添加系统账号或是数据库使用者账号。

优化数据库的方法

  1. 选取最适合的字段属性,尽可能减少定义字段宽度,尽量把字段设成NOT NULL
  2. 使用exists替代in,用not exists替代not in
  3. 使用连接(JOIN)来替代子查询。
  4. 适用联合(NUION)来代替手动创建的临时表。
  5. 事务处理。
  6. 锁定表,优化事务处理。
  7. 适当用外键,优化锁定表。
  8. 建立索引。
  9. 优化查询语句。

数据库中的事务是什么

事务作为一个单元的一组有序的数据操作,如果组中的所有操作都完成,则认定事务成功,即使只有一个失败,事务也不成功。如果所有操作完成,事务则进行提交,其修改将作用于所有其他数据库进程。如果一个操作失败,则事务将回滚,该事务所有的操作的影响都会取消。

  • ACID四大特性
  • 原子性:不可分割,事务要么全部被执行,要么全部不执行。
  • 一致性:事务的执行使得数据库从一种正确的状态转换成另一种正确的状态。
  • 隔离性:在事务正确提交前,不允许把该事务对数据的任何改变提供给任何其他事务。
  • 持久性:事务正确提交后,将结果永久保存到数据库中,即使在事务提交后,有了其他故障,事务处理结果也会得到保存。

索引的目的是什么

  1. 快速访问数据表中特定信息,提高检索速度。
  2. 创建唯一性索引,保证每一行数据的唯一性。
  3. 加速表和表之间的连接。
  4. 使用分组和排序子句进行数据检索时,可显著的减少分组和排序的时间。

对SQL语句的优化方法

  1. 避免在索引列上使用计算。
  2. 避免在索引列上使用IS NULLIS NOT NULL
  3. 对查询进行优化,尽量避免全表扫描,首先因该考虑在whereorder by涉及的列上建立索引。
  4. 避免在where子句对字段进行null值判断,这件导致引擎放弃使用索引而进行全表扫描。
  5. 避免在where子句中对字段进行表达式操作,也会导致引擎放弃使用索引而进行全表扫描。

char和varchar的区别

  • char类型的数据列里,每个值都占M个字节,如果长度小于M`,就会在它的右边用空格字符进行补足(在检索操作中填补出来的空格符将会被去掉)。
  • vachar类型的数据列里,每个值只占用刚好够用的字节再加上一个用来记录长度的字节,所以总长度为L+1字节。

SQL问题

  • 脏读

  • 在一个事务处理过程中读取到了另一个未提交事务中的数据。

【例子】

A在一个转账事务中,转了100给B,此时B读到了这个转账的数据,然后做了一些操作(给A发货,或是其他),可是这个时候A的事务并没有提交,如果A回滚了事务,那这就是脏读。

  • 不可重复读

  • 对数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,是由于在查询间隔,被另一个事务修改并提交了。

【示例】

事务A在读取某一数据,而事务B立马修改了这个数据并且提交了事务到数据库,事务A再次读取就得到了不同的结果。发生了不重复读。

  • 幻读

  • 事务非独立执行时发生的一种现象。

【示例】

事务A对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务B又对这个表中插入了一行数据项,这个数据的数值还是“1”并且提给了数据库,如果事务A查看刚刚修改的数据,会发现还有一数据没有修改,而这行数据时事务B中添加的,就像产生的幻觉一样。发生了幻读。

MySQL事务隔离级别

  1. read uncmmited:读到未提交数据
  • 最低级别,无法保证任情况
  1. read commited:读已提交
  • 可避免脏读
  1. repeatable read:可重复读
  • 可避免脏读、不可重复读
  1. serializable:串行事务
  • 可避免脏读、不可重复读、幻读

MySQL默认事务隔离级别为Repeatable Read(可重复读)】

MySQL临时表

什么是临时表:临时表是MySQL用于存储中间结果集的表,临时表只在当前连接可看,当连接关闭时会自动删除表并释放所有空间。

为什么会产生临时表:一般是因为复杂的SQL导致临时表被大量创建

6. MyBatis?

简介
MyBatis 是一款优秀的持久层框架,一个半 ORM(对象关系映射)框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。

为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

JDBC开发存在的问题
频繁创建数据库连接对象、释放,容易造成系统资源浪费,影响系统性能。可以使用连接池解决这个问题。但是使用jdbc需要自己实现连接池。
sql语句定义、参数设置、结果集处理存在硬编码。实际项目中sql语句变化的可能性较大,一旦发生变化,需要修改java代码,系统需要重新编译,重新发布。不好维护。
使用preparedStatement向占有位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
结果集处理存在重复代码,处理麻烦。如果可以映射成Java对象会比较方便。

JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?
1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。

解决:在mybatis-config.xml中配置数据链接池,使用连接池管理数据库连接。

2、Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

解决:将Sql语句配置在XXXXmapper.xml文件中与java代码分离。

3、向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。

解决: Mybatis自动将java对象映射至sql语句。

4、对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

解决:Mybatis自动将sql执行结果映射至java对象。

Mybatis优缺点

优点:
与传统的数据库访问技术相比,ORM有以下优点:

基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用
与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)
提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护
能够与Spring很好的集成

缺点:
SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

MyBatis编程步骤是什么样的?
1、 创建SqlSessionFactory

2、 通过SqlSessionFactory创建SqlSession

3、 通过sqlsession执行数据库操作

4、 调用session.commit()提交事务

5、 调用session.close()关闭会话

MyBatis工作原理图
在这里插入图片描述
(1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。

(2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

(3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

(4) 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。

(5) Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

(6) MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

(7) 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

(8) 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

MyBatis功能架构
在这里插入图片描述

Mybatis的功能架构分为三层:
API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

7. Redis?

介绍
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

nginx: 3-5万/秒
redis: 读: 11.2万/秒 写: 8.6万/秒 平均10万/秒
吞吐量: 50万/秒

redis缓存
说明:通过缓存服务器可以有效的提升用户的访问的效率.
注意事项:
1.缓存的数据结构 应该选用 K-V结构 只要key唯一 那么结果必然相同…
2.缓存中的数据不可能一直存储,需要定期将内存数据进行优化 LRU算法…
3.缓存要求运行速度很快, C语言实现… 运行在内存中.
4.如果缓存运行的数据在内存中,如果断电/宕机,则内存数据直接丢失. 实现内存数据的持久化操作(磁盘).

redis配置
修改redis.conf配置文件
1.修改IP绑定
在这里插入图片描述

2.关闭保护模式
在这里插入图片描述

3.开启后台启动
在这里插入图片描述
springboot整合Redis
添加依赖

 <!--spring整合redis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>

AOP实现redis
步骤:
1).需要自定义注解CacheFind
2).设定注解的参数 key的前缀,数据的超时时间.
3).在方法中标识注解.
4).利用AOP 拦截指定的注解.
5).应该使用Around通知实现缓存业务.

切面业务:

package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import lombok.extern.apachecommons.CommonsLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect //标识我是一个切面
@Component  //交给Spring容器管理
public class CacheAOP {

    @Autowired
    private Jedis jedis;

    /**
     * 注意事项:  当有多个参数时,joinPoint必须位于第一位.
     * 需求:
     *      1.准备key= 注解的前缀 + 用户的参数
     *      2.从redis中获取数据
     *         有: 从缓存中获取数据之后,直接返回值
     *         没有: 查询数据库之后再次保存到缓存中即可.
     *
     * 方法:
     *      动态获取注解的类型,看上去是注解的名称,但是实质是注解的类型. 只要切入点表达式满足条件
     *      则会传递注解对象类型.
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws Throwable {
        Object result = null;   //定义返回值对象
        String preKey = cacheFind.preKey();
        String key = preKey + "::" + Arrays.toString(joinPoint.getArgs());

        //1.校验redis中是否有数据
        if(jedis.exists(key)){
            //如果数据存在,需要从redis中获取json数据,之后直接返回
            String json = jedis.get(key);
            //1.获取方法对象,   2.获取方法的返回值类型
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //2.获取返回值类型
            Class returnType = methodSignature.getReturnType();
            result = ObjectMapperUtil.toObject(json,returnType);
            System.out.println("AOP查询redis缓存!!!");
        }else{
            //代表没有数据,需要查询数据库
            result = joinPoint.proceed();
            //将数据转化为JSON
            String json = ObjectMapperUtil.toJSON(result);
            if(cacheFind.seconds() > 0){
                jedis.setex(key, cacheFind.seconds(), json);
            }else{
                jedis.set(key,json);
            }
            System.out.println("AOP查询数据库!!!");
        }
        return result;
    }
   /* @Around("@annotation(com.jt.anno.CacheFind)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //1.获取目标对象的Class类型
        Class targetClass = joinPoint.getTarget().getClass();
        //2.获取目标方法名称
        String methodName = joinPoint.getSignature().getName();
        //3.获取参数类型
        Object[] argsObj = joinPoint.getArgs();
        Class[]  argsClass = null;
        //4.对象转化为class类型
        if(argsObj.length>0){
           argsClass = new Class[argsObj.length];
            for(int i=0;i<argsObj.length;i++){
                argsClass[i] = argsObj[i].getClass();
            }
        }
        //3.获取方法对象
        Method targetMethod = targetClass.getMethod(methodName,argsClass);

        //4.获取方法上的注解
        if(targetMethod.isAnnotationPresent(CacheFind.class)){
            CacheFind cacheFind = targetMethod.getAnnotation(CacheFind.class);
            String key = cacheFind.preKey() + "::"
                         +Arrays.toString(joinPoint.getArgs());
            System.out.println(key);
        }

        Object object = joinPoint.proceed();
        System.out.println("环绕开始后");
        return object;
    }
*/

   /* //切面 = 切入点表达式 + 通知方法
    //@Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.*)")   // .* 一级包路径   ..* 所有子孙后代包
    //@Pointcut("execution(返回值类型 包名.类名.方法名(参数列表))")
    @Pointcut("execution(* com.jt.service..*.*(..))")
    //注释: 返回值类型任意类型   在com.jt.service下的所有子孙类   以add开头的方法,任意参数类型
    public void pointCut(){

    }*/
    /**
     * 需求:
     *      1.获取目标方法的路径
     *      2.获取目标方法的参数.
     *      3.获取目标方法的名称
     */
   /* @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        String classNamePath = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("方法路径:"+classNamePath);
        System.out.println("方法参数:"+ Arrays.toString(args));
        System.out.println("方法名称:"+methodName);
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("环绕通知开始");
            Object obj = joinPoint.proceed();
            //如果有下一个通知,就执行下一个通知,如果没有就执行目标方法(业务方法)
            System.out.println("环绕通知结束");
            return null;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException(throwable);
        }
    }*/
}

Redis持久化策略
什么是持久化
说明:Redis运行环境在内存中,如果redis服务器关闭,则内存数据将会丢失.
需求: 如何保存内存数据呢?
解决方案: 可以定期将内存数据持久化到磁盘中.

持久化策略规则:
当redis正常运行时,定期的将数据保存到磁盘中,当redis服务器重启时,则根据配置文件中指定的持久化的方式,实现数据的恢复.(读取数据,之后恢复数据.)

RDB模式
RDB模式特点说明
1).RDB模式是Redis默认的策略.
2).RDB模式能够定期(时间间隔)持久化. 弊端:可能导致数据的丢失.
3).RDB模式记录的是内存数据的快照.持久化效率较高. 快照只保留最新的记录.

RDB模式命令
1.save命令: 将内存数据持久化到磁盘中 主动的操作 会造成线程阻塞
2.bgsave命令: 将内存数据采用后台运行的方式,持久化到文件中. 不会造成阻塞.
3.默认的持久化的机制
save 900 1 如果在900秒内,执行了1次更新操作,则持久化一次
save 300 10 如果在300秒内,执行了10次更新操作,则持久化一次
save 60 10000 如果在60秒内,执行了10000次更新操作,则持久化一次

持久化文件配置
指定持久化文件
在这里插入图片描述
指定持久化文件目录
在这里插入图片描述
AOF模式
AOF模式特点
1).AOF模式默认条件下是关闭的.需要手动开启
2).AOF模式记录的是用户的操作过程,所以可以实现实时持久化操作.
3).AOF模式由于记录的是实时的操作过程,所以持久化文件较大.需要定期维护.

启动AOF模式
说明:如果一旦开启AOF模式,则以AOF模式为准.
在这里插入图片描述
持久化操作总结
1.当内存数据允许少量丢失时,采用RDB模式 (快)
2.当内存数据不允许丢失时,采用AOF模式(定期维护持久化文件)
3.一般在工作中采用 RDB+AOF模式共同作用,保证数据的有效性.

内存优化策略
LRU算法
LFU算法
随机算法
TTL算法

配置内存优化策略

1.volatile-lru   在设定了超时时间的数据中,采用lru算法
2.allkeys-lru   在所有的数据中,采用lru算法

问题:
由于缓存失效,导致大量的用户的请求,直接访问数据库服务器.导致负载过高,从而引发整体宕机的风险!!!

缓存穿透
说明: 用户频繁访问数据库中不存在的数据,可能出现缓存穿透的现象.如果该操作是高并发操作,则可能直接威胁数据库服务器.
解决方案:
1.采用IP限流的方式 降低用户访问服务器次数. IP动态代理(1分钟变一次)
2.微服务的处理方式: 利用断路器返回执行的业务数据即可不执行数据库操作 从而保护了数据库.
3.微服务处理方式: API网关设计. 不允许做非法操作

缓存击穿
说明: 由于redis中某个热点数据由于超时/删除等操作造成数据失效.同时用户高并发访问该数据,则可能导致数据库宕机.该操作称之为 缓存击穿.
解决方案: 可以采用多级缓存的设计. 同时数据的超时时间采用随机数的方式.

缓存雪崩
说明: 由于redis内存数据大量失效.导致用户的访问命中率太低.大量的用户直接访问数据库,可能导致数据库服务器宕机. 这种现象称之为缓存雪崩.
解决:
1.采用多级缓存.
2.设定不同的超时时间
3.禁止执行 flushAll等敏感操作.

Redis分片
说明: 如果需要Redis存储海量的内存数据,使用单台redis不能满足用户的需求,所以可以采用Redis分片机制实现数据存储.
注意事项:
如果有多台redis,则其中的数据都是不一样的…
在这里插入图片描述
部署分片:
搭建端口:6379/6380/6381
1.准备三份配置文件6379.conf 6380.conf 6381.conf
2.修改对应的端口号即可
在这里插入图片描述
一致性HASH算法
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。
[1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。

redis分片主要的作用是实现内存数据的扩容.但是如果redis分片中有一个节点宕机,则直接影响所有节点的运行.

Redis哨兵机制
实现策略: 采用Redis哨兵机制实现Redis节点高可用.

删除多余的持久化文件,保存redis配置文件

配置主从:
节点划分: 6379 当主机 6380/81 当从机
命令: 实现主从挂载 slaveof host port
命令: info replication 检查redis节点的状态信息
结论: redis所有节点都可以相同通信.并且路径当前的主从的状态.
数据主从同步的状态,向主机添加数据,从机也同步.

工作原理:
1).当哨兵启动时,会链接redis主节点,同时获取所有节点的状态信息
2).当哨兵连续3次通过心跳检测机制(PING-PONG),如果发现主机宕机,则开始选举.
3).哨兵内部通过随机算法筛选一台从机当选新的主机.其他的节点应该当新主机的从.
在这里插入图片描述
哨兵缺点: 数据没有扩容,哨兵本身没有高可用机制

Redis集群
多个哨兵,多个主从。

哨兵和分片的缺点:
1.分片缺点: 分片的主要的功能是实现内存的扩容的. 但是没有高可用的效果.
2.哨兵缺点: 数据没有扩容,哨兵本身没有高可用机制
需求: 既可以实现内存数据的扩容,同时实现高可用机制(不用第三方).

集群 面试题
1.redis集群中一共可以存储16384个KEY? 不对的
答: 16384只是槽位的数量 只负责规划这个数据归谁管理的问题.至于数据如何存储,是由redis内存决定的.
hash(key1) = 3000,
hash(key2) = 3000;
2.Redis集群中最多可以有多少台主机? 16384台主机.
3.Redis中如果遇到多线程操作,是否有线程安全性问题 ? 没有
因为:redis服务器是单进程单线程操作. 每次操作都是由一个线程执行,所以不会有线程安全性问题.
4.Redis如何实现内存数据的优化? LRU/LFU/随机算法/TTL

8. Ajax原理及实现功能?

什么是ajax
Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML)。
现在允许浏览器与务器通信而无须刷新当前页面的技术到叫做ajax。
Ajax:一种不用刷新整个页面便可与服务器通讯的办法。

Ajax原理
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。

什么是XmlHttpRequest对象
XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步地返回 Web 服务器的响应,并且能够以文本或者一个 DOM 文档的形式返回内容。
尽管名为 XMLHttpRequest,它并不限于和 XML 文档一起使用:它可以接收任何形式的文本文档。
XMLHttpRequest 对象是名为 AJAX的 Web 应用程序架构的一项关键功能。
XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

实现功能
咱们的网页如果想要刷新局部内容。 那么需要重新载入整个网页。用户体验不是很好。 就是为了解决局部刷新的问题。 保持其他部分不动,只刷新某些地方。

使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。

Ajax技术之主要目的在于局部交换客户端及服务器之间的数据。

使用ajax发送数据的步骤
第一步:创建异步对象

var xhr = new XMLHttpRequest();

第二步:设置 请求行 open(请求方式,请求url):

// get请求如果有参数就需要在url后面拼接参数,
// post如果有参数,就在请求体中传递 xhr.open("get","validate.php?username="+name)
xhr.open("post","validate.php");

第三步:设置请求(GET方式忽略此步骤)头:setRequestHeader()

// 1.get不需要设置
// 2.post需要设置请求头:Content-Type:application/x-www-form-urlencoded
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");

第四步:设置请求体 send()

// 1.get的参数在url拼接了,所以不需要在这个函数中设置
// 2.post的参数在这个函数中设置(如果有参数)
xhr.send(null) xhr.send("username="+name);

第五步:让异步对象接收服务器的响应数据

一个成功的响应有两个条件
服务器成功响应了 。
异步对象的响应状态为4(数据解析完毕可以使用了)

9. Spring IOC机制,DI?

什么是Spring
Spring是一个容器,可以接管各个层次的Bean(action/domain/pojo/javabean),并且可以配置bean与bean之间的关系
在java代码里使用bean只需要 用ApplicationContext 的getBean(配置文件里bean的id)方法就可以了。

Spring是依赖反射机制的,

那到底什么是反射机制呢:
反射机制就是利用(dom4j=java反射机制)
userBean ub = Class.forName(com.bean.***)这里是com全路径
所以在Spring配置文件中bean 的id属性和class属性中要写全路径。


IOC是什么

ioc(inverse of control )控制反转:所谓控制反转就是把对象(bean)对象和维护对象(bean)之间的关系的权利转移到Sqring容器中去了(ApplicationContext.xml)而程序本身不在维护了

DI是什么

di(dependency injection)依赖注入:实际上DI和IOC是同一个概念,因为在ApplicationContext.xml配置文件中bean和bean之间通过ref来维护的时候是相互依赖的,所以又叫做依赖注入。也就是控制反转。

因为ApplicationContext是非常消耗内存的,所以必须保证一个项目里只有一个ApplicationContext实例:
那么如何保证这有一个实例呢,就需要把ApplicationContext对象做成单例形式

10. 跨域?

说明:
1.什么叫跨域 浏览器解析Ajax时,发起url请求违反了同源策略,称之为跨域.
2.什么时候用跨域 一般A服务器需要从B服务器中获取数据时,可以采用跨域的方式.
3.什么是JSONP: JSONP是JSON的一种使用模式 利用javaScript中的src属性进行跨域请求.(2.自定义回调函数,3.将返回值进行特殊格式封装)
4.什么是CORS: CORS是当前实现跨域的主流方式,现在所有的主流浏览器都支持,需要在服务器端配置是否允许跨域的配置. 只要配置了(在响应头中添加允许跨域的标识),则同源策略不生效,则可以实现跨域.

同源策略
规定: 如果浏览器的地址与Ajax的请求地址的 协议名称://域名地址:端口号 如果都相同则满足同源策略。浏览器可以正常的解析返回值. 如果三者之间有一个不同,则违反了同源策略.浏览器不会解析返回值.

跨域
由于业务需要,通常A服务器中的数据可能来源于B服务器. 当浏览器通过网址解析页面时,如果页面内部发起ajax请求.如果浏览器的访问地址与Ajax访问地址不满足同源策略时,则称之为跨域请求.
跨域要素:
1.浏览器
2.解析ajax
3.违反了同源策略

JSONP
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
原理:利用JavaScript的src或href属性引入资源。只支持get请求,不支持post请求。
步骤:1.引入javaScript中的src属性可以跨域的访问
2.提前准备一个回调函数 callback()
3.将返回值结果进行特殊的格式封装. callback(JSON数据)
JSONP高级API
前端页面:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSONP测试</title>
<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
<script type="text/javascript">
	$(function(){ //让页面加载完成之后再次执行
		alert("测试访问开始!!!!!")
		$.ajax({
			url:"http://manage.jt.com/web/testJSONP",
			type:"get",				//jsonp只能支持get请求
			dataType:"jsonp",       //dataType表示返回值类型
			jsonp: "callback",    //指定参数名称
			jsonpCallback: "hello",  //指定回调函数名称
			success:function (data){   //data经过jQuery封装返回就是json串
				alert(data.id);
				alert(data.name);
				//转化为字符串使用
				//var obj = eval("("+data+")");
				//alert(obj.name);
			}	
		});	
	})
</script>
</head>
<body>
	<h1>JSON跨域请求测试</h1>
</body>
</html>

后端服务器:

package com.jt.web;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.jt.pojo.ItemDesc;
import com.jt.util.ObjectMapperUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebJSONPController {

    /**
     * 完成JSONP跨域访问
     * url地址: http://manage.jt.com/web/testJSONP?callback=hello&_=1605584709377
     * 参数:    callback 回调函数的名称
     * 返回值:  callback(json)
     */
    @RequestMapping("/web/testJSONP")
    public JSONPObject testJSONP(String callback){
        ItemDesc itemDesc = new ItemDesc();
        itemDesc.setItemId(1000L).setItemDesc("JSONP远程调用!!!");
        JSONPObject jsonpObject = new JSONPObject(callback, itemDesc);
        return jsonpObject;
    }
}

CORS
因为出于安全的考虑, 浏览器不允许Ajax调用当前源之外的资源. 即浏览器的同源策略.
CORS需要浏览器和服务器同时支持。目前,所有主流浏览器都支持该功能,IE浏览器不能低于IE10。在浏览器端, 整个CORS通信过程都是浏览器自动完成,在请求之中添加响应头信息,如果服务器允许执行跨域访问.,则浏览器的同源策略放行.

实现:

package com.jt.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 完成CORS跨域配置: 实现思路在请求的响应头中添加访问信息.
 */
@Configuration
public class CORSConfig implements WebMvcConfigurer{//web项目的全局配置的接口.

    /**
     * 参数介绍:
     *      1.addMapping()  哪些请求可以进行跨域操作
     *        addMapping("/**")   表示所有访问后端的服务器的请求都允许跨域
     *        addMapping("/addUser/**")  表示部分请求可以跨域
     *        /*       只能拦截一级目录
     *        /**      可以拦截多级目录
     *
     *      2.allowedOrigins("*")  允许哪些网站跨域
     *      3.allowCredentials(true)  请求跨域时是否允许携带Cookie/Session相关
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true);
                //.maxAge();       默认30分钟 是否允许跨域请求 30分钟之内不会再次验证
                //.allowedMethods() 允许请求类型
    }
}

11. 对java的理解?为什么选择java?

大多数人选择Java可能只是因为听说Java前景好、Java比较好找工作、Java语言在TIOBE排行榜上一直位于前三等等之类的原因,但是Java具体好在哪里,心里却是没有什么概念的。其实我选择Java也是出于以上的原因,但是现在确实真正地爱上了Java。
那么现在我们来分析下Java的好处究竟在哪里,我们为什么选择Java?​

简单易学
首先Java是一个面向对象的编程语言,容易理解。而且略去了多重加载、指针等难以理解的概念。并且实现了自动垃圾回收,大大简化了程序设计。

其次Java学习资料较多,在官网上可以找到很多Java的学习视频,另外也有一些培训机构提供的有免费的Java视频课程。有这些学习资料,类似高淇300集等入门足矣。

跨平台
跨平台是Java最大的优势。Java运行在JVM(Java虚拟机)上,在任何平台只要安装了JVM。Java就可以运行。它架构在操作系统之上,屏蔽了底层的差异。真正实现了“Write once,run anywhere”一次编译,到处运行。

安全
Java中没有指针,这样就没有办法直接访问内存了。另外Java有垃圾回收机制也不容易出现内存泄露。

多线程
Java内置对多线程的支持,可以方便地在程序中实现多线程的功能。不像其他不支持多线程的语言,需要调用操作系统的多线程功能才能完成多线程的实现。

有丰富的类库
经过10多年的积累和沉淀,出现了很多优秀的开源社区,如Apache和Spring。这些优秀的社区提供了很多非常好的框架,借助这些框架可以使我们不用去关注Java底层的开发,而只需关注业务的实现。

使用广泛
不得不承认上面有一点是对的,Java确实有很多企业在用,而且都是用在大项目上。这就意味着Java方面的工作比较好找,另一点是在做Java开发时如果遇到问题,可以很容易从网上找到解决办法。

12. 线程并发使用,怎样提升访问效率?

首先确认并发访问的瓶颈在哪
1.网络带宽不够
2.web线程连接数不够
3.数据库查询性能达不到

根据不同情况,确认解决办法
1.增加网络带宽,DNS域名解析分发多台服务器
2.负载均衡,前置代理服务器nignx、apache
3.数据库查询优化,读写分离,分表分区

13. IO写入文件具体代码实现?

14. 线程与进程?

进程定义:
进程是具有一定功能的程序在一个数据集合上的运行过程,它是系统进行资源分配和调度管理的一个可并发执行的基本单位。

理解:
1、每个进程拥有独立的代码和数据空间。
2、程序的一次执行,切换的开销大。

特征:
1、动态性(最大的特性):系统创建、调度执行、申请资源被阻塞、完成任务后被撤销。
2、异步性:进程由于使用资源的原因,在管理上使用异步的方式使用处理器和资源。
3、独立性:为了并发。
4、结构特征:一个进程实体有程序、数据和一个PCB(进程控制块构成)

状态转换:
在这里插入图片描述
线程定义:
线程是进程中实施和分派的基本单位。

分类:
用户级线程和核心级线程

特点:
独立调度、抢占式的执行、并发执行、共享进程的资源、切换开销小。

线程池:
创建一个进程的时候,同时创建多个线程,放入缓冲池中,这些线程等待工作,服务器接收到请求时
唤醒一个线程,并将请求交给它,任务完后重新放入线程池中。线程池中如果没有可用的线程,那么服务器就等待直到有线程被释放。

线程状态:
在这里插入图片描述
1.新生状态:
对象一旦建立就处于新生状态(Thread st = new Thread();)。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码
2.就绪状态:
线程建立后处于新生状态,后就调用start()方法进入就绪状态,即线程进入就绪队列等待着Cpu的调度。进入就绪状态不一定会被立即调用,是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
有四种可能进入就绪状态:1.start()方法的调用。2.阻塞状态解除。3.yield()方法调用。4.Jvm中断此线程
3.阻塞状态:
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4.阻塞状态:
当调用sleep(),wait()或同步锁时,线程会进入阻塞状态。run()方法不会再往下执行,在阻塞队列中。等阻塞时间排除,会重新进入就绪状态,重新和新的线程公平的竞争CPU资源。
5.死亡状态:
线程结束运行后,就进入死亡状态,注意不会再进入新生状态。
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

线程创建:

1、public class ThreadCreate extends Thread{
     public void run(){
            for(int i=0;i<50;i++){
                  System.out.println("Thread"+i);//循环50次
            }
     }
}
ThreadCreate threadCreate=new ThreadCreate();//创建一个线程了

2、public class RunnableCreate implements Runnable{
     public void run(){
            for(int i=0;i<50;i++){
                  System.out.println("Thread"+i);//循环50次
            }
     }
}

RunnableCreate  runnableCreate=new RunnableCreate ();

Thread rThread=new Thread(runnableCreate); //创建一个线程

常用的方法:
1、wait():Object方法、notify\notifyAll、释放资源,sleep():Thread类的静态方法、不释放资源。

2、interrupt():切换异常。

3、join():合并线程。当前线程阻塞时,被调用的线程执行。

4、yield():让出cpu资源。

线程同步:
synchronized

1、每个对象都有一个互斥锁、使用synchronized方法后,每个对象自能被一个线程访问。

死锁问题:
1、产生条件(四个必要条件):
互斥:资源被一个线程占用其他线程不能占用

不可剥夺:不能强抢资源,只能能拥有资源的线程释放资源。

请求保持:资源请求者在请求其他资源受阻时,对已经获得的资源不释放,持续占有。

循环等待:P1占有P2资源,P2占有P3资源,P3又占有P1资源。

解决死锁:
打破四个条件的任意一个

线程交互:
1、wait(),notify():

线程调度:
抢占式、高优先级、分时间片、先来先服务。

getPriority()、setPriority()、1-10数字越大优先级越高。

优先级高只是获得处理器资源的机会多并不一定先执行。

线程及进程区别:
1、根本区别:
进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。

2、开销方面:
每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销。

 线程可以看作是轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间的切换开销小。

3、所处环境:
在操作系统中能够同时运行多个进程,在同一进程中能够有多个线程同时运行(CPU调度,每个时间片只有一个线程执行)

4、内存分配:
系统在运行的时候为每个进程分配不同的内存空间,而线程除了CPU,系统是不会在给其分配内存的,那它使用的就是其所属的进程的资源,线程组之间只能共享资源。

5、包含关系:
没有线程的进程可以看成是单线程的、如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线程共同完成的,线程是进程的一部分,所以线程也被称为轻权进程或轻量级进程。

15. yml文件 与properties文件的区别?

区别
1.扩展名不一样
2.语法不一样
3.优先级不一样

yml文件和properties作用
在springboot项目中我们经常看到默认生成的application.properties文件,其实yml文件作用和它一样,他们都是用来修改一些默认的配置值。如Mysql的用户名,密码等。

相互转换
由于properties的写法比较直接不做赘述,着重展示yml文件如何写
yml写法:
1)每一个等级用冒号分隔,然后空行,再使用空格空出来,注意不能使用Tab符号。properties文件每一个点为一级。
2)冒号后面为值时,需要用一个空格隔开。
例子:
application.properties

eureka.instance.hostname=localhost
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false

application.yml

eureka:
    client:
        fetchRegistry: false
        registerWithEureka: false
    instance:
        hostname: localhost

优先级
读取顺序:Springboot项目中properites和yml都存在情况下,优先读取properties文件。

16. Connection与Connections?

Collection 是 java.util 下的接口,它是各种集合的父接口,继承于它的接口主要有 Set 和 List;Collections 是个 java.util 下的类,
是针对集合的帮助类,提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

17. List、Set、Map?

List 是可重复集合,Set 是不可重复集合,这两个接口都实现了 Collection 父接口。
Map 未继承 Collection,而是独立的接口,Map 是一种把键对象和值对象进行映射的集合,它的每一个元素都包含了一对键对象和值对象,Map 中存储的数据是没有顺序的, 其 key 是不能重复的,它的值是可以有重复的。

存储特性
List 以特定索引来存取元素,可以有重复元素。Set 不能存放重复元素(用对象的 equals()
方法来区分元素是否重复)。Map 保存键值对(key-value pair)映射,映射关系可以是一
对一或多对一。Set 和 Map 容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储
的版本理论存取时间复杂度为 O(1),而基于排序树版本的实现在插入或删除元素时会按照
元素或元素的键(key)构成排序树从而达到排序和去重的效果。

LinkedHashMap的概述和使用
LinkedHashMap的概述: Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序LinkedHashMap的特点: 底层的数据结构是链表和哈希表 元素有序 并且唯一
元素的有序性由链表数据结构保证 唯一性由 哈希表数据结构保证
Map集合的数据结构只和键有关

HashMap和Hashtable的区别
HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的。
前者允许null作为Key;后者不允许null作为Key。

HashMap的底层实现
在Java8之前,底层实现是 数组 + 链表 实现,Java8使用了 数组 + 链表 + 红黑树 实现。

ConcurrentHashMap和Hashtable的区别

ConcurrentHashMap结合了HashMap和Hashtable二者的优势。

HashMap没有考虑同步,Hashtable考虑了同步的问题。但是Hashtable在每次同步执行时都要锁住整个结构。

ConcurrentHashMap锁的方式是稍微细粒度的。ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。

ConcurrentHashMap的具体实现
分段加锁,保证了效率和安全性。
该类包含两个静态内部类 HashEntry 和 Segment;前者用来封装映射表的键值对,后者用来充当锁的角色
Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个 HashEntry 数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。

HashMap的长度为什么是2的幂次方?
通过 Key 的 hash 值与 length-1 进行 & 运算,实现了当前 Key 的定位, 2 的幂次方可以减少冲突(碰撞)的次数,提高 HAshMap查询效率。
如果 length 为 2 的次幂 则 length-1 转化为二进制必定是 11111… 的形式,在于 h 的二进制与操作效率会非常的块,而且空间不浪费。
如果 length 不是 2 的次幂,比如 length 为 15,则 length-1 为 14,对应的二进制为1110,在于 h 与操作,最后一位为0, 而 0001, 0011, 0101,1001, 1011, 0111, 1101 这几个位置永远都不能存放元素了,空间浪费相当大。

18. LinkedList、ArraysList、Vector?

ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以
便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等
内存操作,所以索引数据快而插入数据慢,Vector 中的方法由于添加了 synchronized 修饰,
因此 Vector 是线程安全的容器,但性能上较 ArrayList 差,因此已经是 Java 中的遗留容器。
LinkedList 使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形
成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的
利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项
的前后项即可,所以插入速度较快。Vector 属于遗留容器(Java 早期的版本中提供的容器,
除此之外,Hashtable、Dictionary、BitSet、Stack、Properties 都是遗留容器),已经不推荐使
用,但是由于 ArrayList 和 LinkedListed 都是非线程安全的,如果遇到多个线程操作同一个容
器的场景,则可以通过工具类 Collections 中的 synchronizedList 方法将其转换成线程安全的
容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象
来增强实现)。

19. Spring拦截器?

spring拦截器实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter类都可以实现。
主要有三个方法:
preHandle在调用的具体controller之前执行
postHandle在调用controller之后且页面没有渲染之前执行
afterCompletion在页面渲染之后执行。
拦截器的功能类似于filter(过滤器)的功能
spring拦截器底层是反射机制,而filter(拦截器)底层是函数回调
拦截器属于spring管理的范畴类,方便资源的分配和整合其他资源。
filter(拦截器)在命中多个拦截器时,按顺序一个一个往后执行,拦截器中可以做一些权限校验,数据校验转换等事情。
拦截器demo:

public class UserContextInterceptor extends HandlerInterceptorAdapter {
    //拦截器可以使用spring的依赖注入
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //可以在这里处理用户认证
        if (true) {
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            try (PrintWriter writer = response.getWriter()) {
                writer.write("{'message':'认证不通过'}");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return super.preHandle(request, response, handler);
    }
}

20. Spring代理?

静态代理
抽象角色(接口),代理角色以及真实角色都要自己写,在代理可添加额外功能,而真实地角色类只专注写代码。代理类要实现可调用真实角色去完成业务。

动态代理
动态代理在本质上,代理类不用我们来管,我们完全交给工具去自动生成代理类即可。
动态代理一般有两种方式:JDK 动态代理和 CGLIB 动态代理。

JDK动态代理:
相对于静态代理,JDK动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法之前调用InvokeHandler来处理,但是JDK动态代理有个缺憾,或者说特点:JDK实现动态代理需要实现类通过接口定义业务方法。也就是说它始终无法摆脱仅支持interface代理的桎梏。

CGLIB动态代理:
CGLIB采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理

spring AOP中使用了哪种代理:
JDK动态代理和CGLIB动态代理都是实现AOP的基础。
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
如果目标对象没有实现接口,则默认采用的是CGLIB动态代理。
spring会自动在JDK动态代理和CGLIB动态代理之间切换。

21. Java面向对象?

类用于描述客观世界里某一类对象的共同特征,而对象则是类的具体存在。
面向对象是一种思想,能让复杂的问题简单化,让我们角色从执行者变成指挥者,不要知道过程,只要知道结果。(一切皆对象。)

三大特征:封装、继承、多态。

封装

继承

多态

22. Spring AOP?

AOP的实现方式:
公式: AOP(切面) = 通知方法(5种) + 切入点表达式(4种)

通知
1.before通知 在执行目标方法之前执行
2.afterReturning通知 在目标方法执行之后执行
3.afterThrowing通知 在目标方法执行之后报错时执行
4.after通知 无论什么时候程序执行完成都要执行的通知

上述的4大通知类型,不能控制目标方法是否执行.一般用来记录程序的执行的状态.
一般应用与监控的操作.

5.around通知(功能最为强大的) 在目标方法执行前后执行.
因为环绕通知可以控制目标方法是否执行.控制程序的执行的轨迹.

5.3.2 切入点表达式
1.bean(“bean的ID”) 粒度: 粗粒度 按bean匹配 当前bean中的方法都会执行通知.
2.within(“包名.类名”) 粒度: 粗粒度 可以匹配多个类
3.execution(“返回值类型 包名.类名.方法名(参数列表)”) 粒度: 细粒度 方法参数级别
4.@annotation(“包名.类名”) 粒度:细粒度 按照注解匹配

23. Java泛型?

24. JDBC原理?

JDBC(Java DataBase Connectivity)java数据库连接。就是用java语言来操作数据库的一个框架。JDBC是用java语言向数据库发送SQL语句。
JDBC是一套协议,是JAVA开发人员和数据库厂商达成的协议,也就是由Sun定义一组接口,由数据库厂商来实现,并规定了JAVA开发人员访问数据库所使用的方法的调用规范。

实现步骤
1.加载数据库驱动 mysql 驱动:“com.mysql.jdbc.driver”
2.建立数据库连接
3.创建语句对象 Statement statement=connection.createStatement();
4.执行语句 ResultSet resultSet=statement.executeQuery(sql);
5.处理结果集 while( resultSet.next() ) { }
6.关闭连接 对象.close();

25. Dubbo框架?

采用 Zookeeper注册中心
默认使用 NIO Netty 通信框架

Dubbo是阿里开源的基于Java的高性能分布式服务框架。现在已经成为Apache基金会孵化项目。

Dubbo是一个分布式、高性能、透明化的RPC服务框架,提供服务自动注册、自动发现等高校服务治理方案,可以和spring框架无缝集成。

Dubbo 的核心功能
主要就是如下 3 个核心功能:
Remoting:网络通信框架,提供对多种 NIO 框架抽象封装,包括
“同步转异步”和“请求-响应”模式的信息交换方式。
Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多
协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群
支持。
Registry:服务注册,基于注册中心目录服务,使服务消费方能动态
的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少
机器。

Dubbo 服务注册与发现的流程
在这里插入图片描述

  1. Provider(提供者)绑定指定端口并启动服务
  2. 指供者连接注册中心,并发本机 IP、端口、应用信息和提供服务信息
    发送至注册中心存储
  3. Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信
    息至注册中心
  4. 注册中心根据 消费 者所求服务信息匹配对应的提供者列表发送至
    Consumer 应用缓存。
  5. Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调
    用。
  6. Provider 状态变更会实时通知注册中心、在由注册中心实时推送至
    Consumer

Dubbo 集群提供了哪些负载均衡策略

  1. Random LoadBalance: 随机选取提供者策略,有利于动态调整提供
    者权重。截面碰撞率高,调用次数越多,分布越均匀;
  2. RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是
    存在请求累积的问题;
  3. LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收
    更少的请求;
  4. ConstantHash LoadBalance: 一致性 Hash 策略,使相同参数请求
    总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其
    他提供者,避免引起提供者的剧烈变动;
    缺省时为 Random 随机调用

26. 微服务?

什么是微服务
在介绍微服务时,首先得先理解什么是微服务,顾名思义,微服务得从两个方面去理解,什么是"微"、什么是"服务", 微 狭义来讲就是体积小、著名的"2 pizza 团队"很好的诠释了这一解释(2 pizza 团队最早是亚马逊 CEO Bezos提出来的,意思是说单个服务的设计,所有参与人从设计、开发、测试、运维所有人加起来 只需要2个披萨就够了 )。 而所谓服务,一定要区别于系统,服务一个或者一组相对较小且独立的功能单元,是用户可以感知最小功能集。

按照功能模块把系统划分为不同的小模块,而这些小的模块就是一个服务。

微服务与SOA区别
微服务,从本质意义上看,还是 SOA 架构。但内涵有所不同,微服务并不绑定某种特殊的技术,在一个微服务的系统中,可以有 Java 编写的服务,也可以有 Python编写的服务,他们是靠Restful架构风格统一成一个系统的。所以微服务本身与具体技术实现无关,扩展性强。

微服务设计原则
单一职责原则
服务自治原则
轻量级通信原则
接口明确原则

微服务的特性

  1. 每个服务都可以独立运行在自己的进程里
  2. 一系列独立运行的微服务共同构建起了整个系统
  3. 每个服务为独立的业务开发,一个微服务一般完成某个特定的功能,如:订单管理,用户管理等。
  4. 微服务之间通过一些轻量级的通信机制进行通信,如:通过REST API或者RPC的方式进行调用。

微服务的特点
易于开发和维护
启动较快
拒不修改容易部署
技术栈不受限
按需伸缩

微服务的缺点
运维要求高
分布式复杂性
接口调整成本高
重复劳动

1个中心
eureka注册中心

2个基本点
ribbon重试、负载均衡
hystrix降级、熔断

3个工具
feign声明式客户端接口
zuul网关
config配置中心

4个监控
actuator 监控
hystrix dashboard 仪表盘

sleuth 提供日志
zipkin 可视化管理界面

27. Nginx?

Nginx 是高性能的 HTTP 和反向代理的服务器,处理高并发能力是十分强大的,能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数。
​ 说白了Nginx就是在tomcat之前加了一成东西,通过这个东西我们可以控制对tomcat的访问

Nginx服务器的特性
反向代理、负载均衡
嵌入式Per1解释器
动态二进制升级
可用于重新编写url,具有非常好的PCRE支持

反向代理
反向代理位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无需再用户端作任何设定。反向代理服务器通常可用来作为Web加速,就是使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率。

特性

  1. 反向代理服务器位于用户与目标服务器之间
  2. 对于用户而言,以为代理服务器就是真实的服务器
  3. 反向代理机制保护了服务器的真实信息
  4. 反向代理一般称为服务端代理
  5. 在这里插入图片描述
    正向代理
    一个位于客户端和原始服务器之间的服务器。为了从原始服务器取得内容,客户端代理发送一个请求并指定目标(原始服务器),然后向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。

特点

  1. 代理服务器位于用户与真实服务器之间
  2. 客户端非常清楚自己要访问的服务器是谁
  3. 服务器不知道访问自己的服务器是谁,以为只是代理服务访问
  4. 正向代理称之为客户端代理,保护了用户的信息

28. Java分布式?

分布式是一种架构模式,是将公有模块进行提取,构建成单独的模块,部署在不同服务器上进行调用。

特性
可扩展性高
可用性与可靠性高
高性能
一致性

分布式架构基本思想:分拆(系统分拆,子系统分拆,存储分拆,计算分拆);并发(多线程);缓存;在线VS离线;同步VS异步;Push VS Pull;批量;重读轻写VS重写轻读;读写分离(对数据库而言);动静分离(对前端界面而言);冷热分离(对数据备份而言);限流;服务熔断与降级;CAP理论(C:数据一致性;A:稳定性和性能;P:分区容错性);最终一致性

29. 如何设计一个高并发的系统?

  1. 数据库优化,喝的事务隔离级别,SQL语句,索引优化。
  2. 使用缓存,尽量减少数据库IO操作。
  3. 分布式数据库,分布式缓存。
  4. 服务器负载均衡。

30. 递归思想?

在方法内部不断调用方法本身。

31. 线程?

32. JVM内存泄漏和内存溢出?

内存泄漏
内存泄露是指系统中存在无法回收的内存,有时候会造成内存不足或系统崩溃。Java 存在内存泄露。
Java 中的内存泄露当然是指:存在无用但是垃圾回收器无法回收的对象。
而且即使有内存泄露问题存在,也不一定会表现出来。
自己实现堆栈的数据结构时有可能会出现内存泄露。

内存溢出
通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。

33. JSP的九大隐式对象和四个作用域?

九大隐式对象
1:request: 请求对象 在 javax.servlet.ServletRequest 作用域为 Request 来自客服端
的请求,如:FORM 表单中填写的信息,常用的方法有 getParameter,getParamterName
和 getParamterValue 通过表用获取请求对象中包含的参数值。
2:response 表示客服端的响应。
3:pageContext 对象为页面的上下文对象,代表当请运行页面的一些属性。
4:session:对象代码服务器与客服端所建立的会话,比如在写购物,客服轨迹跟踪,
session”是建立在 cookie 的基础之上的。常用方法有 getId,getValues 等。
5:application 对象负责提供应用程序在服务端运行时的一些全局信息,方法有
getMimeType 等。
6:out:与 response 不同,通过 out 对象发送的内容是浏览器需要的显示内容,还可以
直接想客服端编写一个有程序动态生成的 HTML 的文件。
7:page:page 里的变量没法从 index.jsp 传递到 test.jsp。只要页面跳转了,就不见了。
8: exception:他是一个列外的对象,当页面发生了列外,就会会创建该对象。
9:config:是在 servlet 初始化 Servlet 的时候,JSP 引擎向他传递信息用的,此消息包括
Servlet 初始化时所需要的参数。

四个作用域
首先要声明一点,所谓“作用域”就是“信息共享的范围”,也就是说一个信息能够在多大的范围内有效。4个JSP内置对象的作用域分别为:application、session、request、page 。JSP内置对象作用域表如下:

名称 作用域
application 在所有应用程序中有效
session 在当前会话中有效
request 在当前请求中有效
page 在当前页面有效

34. MyCat中间件?

35. 反射?

36. java锁的类型?

分类:
公平锁/非公平锁
可重入锁
独享锁/共享锁
互斥锁/读写锁
乐观锁/悲观锁
分段锁
偏向锁/轻量级锁/重量级锁
自旋锁

公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。

synchronized void setA() throws Exception{
    Thread.sleep(1000);
    setB();
}

synchronized void setB() throws Exception{
    Thread.sleep(1000);
}

上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

独享锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。

对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,当然是独享锁。

互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock

乐观锁/悲观锁
乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。

从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

偏向锁/轻量级锁/重量级锁
锁升级:
这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
典型的自旋锁实现的例子,可以参考自旋锁的实现

37. JVM内存模型

堆、虚拟机栈、本地方法栈、方法区、程序计数器。
其中本地方法栈、虚拟机栈和程序计数器是线程私有的,生命周期随着线程的创建生,随着线程的消亡而结束。
堆和方法区是线程共享的,生命周期随着JVM的启动而创建,JVM的停止而销毁。

38. SpringBoot、Spring和SpringMvc的区别?

spring和springMvc:

  1. spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)等都提供了多种配置解决方案;

  2. springMvc是spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于spring框架中WEB层开发的一部分;

springMvc和springBoot:

  1. springMvc属于一个企业WEB开发的MVC框架,涵盖面包括前端视图开发、文件配置、后台接口逻辑开发等,XML、config等配置相对比较繁琐复杂;

  2. springBoot框架相对于springMvc框架来说,更专注于开发微服务后台接口,不开发前端视图,同时遵循默认优于配置,简化了插件配置流程,不需要配置xml,相对springmvc,大大简化了配置流程;

总结:

  1. Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring的ioc、aop等. ioc 提供了依赖注入的容器, aop解决了面向横切面编程,然后在此两者的基础上实现了其他延伸产品的高级功能;

  2. springMvc是基于Servlet 的一个MVC框架主要解决WEB开发的问题,因为Spring的配置非常复杂,各种XML、JavaConfig、servlet处理起来比较繁琐;

  3. 为了简化开发者的使用,从而创造性地推出了springBoot框架,默认优于配置,简化了springMvc的配置流程;
    但区别于springMvc的是,springBoot专注于微服务方面的接口开发,和前端解耦,虽然springBoot也可以做成springMvc前后台一起开发,但是这就有点不符合springBoot框架的初衷了;

  4. 对于springCloud框架来说,它和springBoot一样,注重的是微服务的开发,但是springCloud更关注的是全局微服务的整合和管理,相当于管理多个springBoot框架的单体微服务;

Spring主要是作为容器,对Bean的管理,核心有IOC反转控制和DI依赖注入,SpringBoot主要是框架的整合,整合基础框架,简化代码和配置。SrpingMvc注重业务的处理。

  • 6
    点赞
  • 0
    评论
  • 5
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页

打赏作者

LeeYu591

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值