二十道面试题每一个题你能讲个十分钟恭喜你在上海至少16k(Java中级开发)

面试题:html

  • HashMap底层实现原理,红黑树,B+树,B树的结构原理,volatile关键字,CAS(比较与交换)实现原理
  • Spring的AOP和IOC是什么?使用场景有哪些?Spring事务,事务的属性,传播行为,数据库隔离级别
  • Spring和SpringMVC,MyBatis以及SpringBoot的注解分别有哪些?SpringMVC的工做原理,SpringBoot框架的优势,MyBatis框架的优势
  • SpringCould组件有哪些,他们的做用是什么?(说七八个)微服务的CAP是什么?BASE是什么?
  • 设计模式(说五六个)
  • Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透
  • 线程是什么,有几种实现方式,它们之间的区别是什么,线程池实现原理,JUC并发包,ThreadLocal与Lock和Synchronize区别
  • 分布式事务(不一样系统之间如何保证数据的一致性(A系统写入数据,B系统由于某些缘由没有写入成功,形成数据不一致))
  • 安全性问题(数据篡改(拿到别人的URL,篡改数据(金额)发送给系统))
  • 索引使用的限制条件,sql优化有哪些,数据同步问题(缓存,数据库数据同步)
  • 初始化Bean对象有几个步骤,它的生命周期
  • JVM内存模型,算法,垃圾回收器,调优,类加载机制(双亲委派),建立一个对象,这个对象在内存中是怎么分配的?
  • 如何设计一个秒杀系统,(高并发高可用分布式集群)
  • 悲观锁,乐观锁,读写锁,行锁,表锁,自旋锁,死锁,分布式锁,线程同步锁,公平锁,非公平锁分别是什么
  • 堆溢出,栈溢出的出现场景以及解决方案
  • 说出几种MQ之间的区别,以及为何使用这种MQ,消息重复发送(幂等性),消息发送失败,消息掉包,长时间收不到消息,发送的消息太大形成接收不成功
  • 单点登陆实现原理
  • 假若有上亿条数据,你如何快速找到其中一条你想要的数据(几种简单的算法)
  • Dubbo的运行原理,支持什么协议,与SpringCould相比它为何效率要高一些,Zookeeper底层原理
  • 假如你带一个团队,让你设计一个系统,你须要考虑哪些

答案:前端

HashMap底层实现原理,红黑树,B+树,B树的结构原理,volatile关键字,CAS(比较与交换)实现原理

首先HashMap是Map的一个实现类,而Map存储形式是键值对(key,value)的。能够当作是一个一个的Entry。Entry所存放的位置是由key来决定的。java

Map中的key是无序的且不可重复的,全部的key能够当作是一个set集合,若是出现Map中的key若是是自定义类的对象,则必须重写hashCode和equals方法,由于若是不重写,使用的是Object类中的hashCode和equals方法,比较的是内存地址值不是比内容。android

Map中的value是无序的可重复的,全部的value能够当作是Collection集合,Map中的value若是是自定义类的对象必须重写equals方法。ios

至于要重写hashCode和equals分别作什么用,拿hashMap底层原理来讲:面试

当咱们向HashMap中存放一个元素(k1,v1),先根据k1的hashCode方法来决定在数组中存放的位置。redis

若是这个位置没有其它元素,将(k1,v1)直接放入Node类型的数组中,这个数组初始化容量是16,默认的加载因子是0.75,也就是当元素加到12的时候,底层会进行扩容,扩容为原来的2倍。若是该位置已经有其它元素(k2,v2),那就调用k1的equals方法和k2进行比较二个元素是否相同,若是结果为true,说明二个元素是同样的,用v1替换v2,若是返回值为false,二个元素不同,就用链表的形式将(k1,v1)存放。算法

不过当链表中的数据较多时,查询的效率会降低,因此在JDK1.8版本后作了一个升级,就是当链表中的元素达到8时,会将链表替换成红黑树,来提升查找效率。由于对于搜索,插入,删除操做多的状况下,使用红黑树的效率要高一些。spring

缘由是由于红黑树是一种特殊的二叉查找树,二叉查找树全部节点的左子树都小于该节点,全部节点的右子树都大于该节点,就能够经过大小比较关系来进行快速的检索。sql

在红黑树上插入或者删除一个节点以后,红黑树就发生了变化,可能不知足红黑树的5条性质,也就再也不是一颗红黑树了,而是一颗普通的树,能够经过左旋和右旋,使这颗树从新成为红黑树。红黑树的5条性质(根节点是黑色,每一个节点是黑色或者是红色,每一个叶子节点是黑色,若是一个节点是红色它的子节点必须是黑色的,从一个节点到该节点的子孙外部节点的全部路径上包含相同数目的黑点)

并且像这种二叉树结构比较常见的使用场景是Mysql二种引擎的索引,Myisam使用的是B树,InnoDB使用的是B+树。

首先B树它的每一个节点都是Key.value的二元组,它的key都是从左到右递增的排序,value中存储数据。这种模式在读取数据方面的性能很高,由于有单独的索引文件,Myisam 的存储文件有三个.frm是表的结构文件,.MYD是数据文件,.MYI是索引文件。不过Myisam 也有些缺点它只支持表级锁,不支持行级锁也不支持事务,外键等,因此通常用于大数据存储。

而后是InnoDB,它的存储文件相比Myisam少一个索引文件,它是以 ID 为索引的数据存储,数据如今都被存在了叶子结点,索引在非叶结点上。而这些节点分散在索引页上。在InnoDB里,每一个页默认16KB,假设索引的是8B的long型数据,每一个key后有个页号4B,还有6B的其余数据,那么每一个页的扇出系数为16KB/(8B+4B+6B)≈1000,即每一个页能够索引1000个key。在高度h=3时,s=1000^3=10亿!!也就是说,InnoDB经过三次索引页的I/O,便可索引10亿的key,而非叶节点这一行存储的索引,数量就多了,I/O的次数就少了。而Myisam在每一个节点都存储数据和索引,这样就减小了每页存储的索引数量。并且InnoDB它还支持行级,表级锁,也支持事务,外键.

 另外对于HashMap实际使用过程当中仍是会出现一些线程安全问题

HashMap是线程不安全的,在多线程环境下,使用Hashmap进行put操做会引发死循环,致使CPU利用率接近100%,并且会抛出并发修改异常,致使缘由是并发争取线程资源,修改数据致使的,一个线程正在写,一个线程过来争抢,致使线程写的过程被其余线程打断,致使数据不一致。

HashTable是线程安全的,只不过实现代价却太大了,简单粗暴,get/put全部相关操做都是synchronized的,这至关于给整个哈希表加了一把大锁。多线程访问时候,只要有一个线程访问或操做该对象,那其余线程只能阻塞,至关于将全部的操做串行化,在竞争激烈的并发场景中性能就会很是差。

为了应对hashmap在并发环境下不安全问题能够使用,ConcurrentHashMap大量的利用了volatile,CAS等技术来减小锁竞争对于性能的影响。

JDK1.7版本中ConcurrentHashMap避免了对全局加锁,改为了局部加锁(分段锁),分段锁技术,将数据分红一段一段的存储,而后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其余段的数据也能被其余线程访问,可以实现真正的并发访问。不过这种结构的带来的反作用是Hash的过程要比普通的HashMap要长。

因此在JDK1.8版本中CurrentHashMap内部中的value使用volatile修饰,保证并发的可见性以及禁止指令重排,只不过volatile不保证原子性,使用为了确保原子性,采用CAS(比较交换)这种乐观锁来解决。

CAS 操做包含三个操做数 —— 内存位置(V)、预期原值(A)和新值(B)。

若是内存地址里面的值和A的值是同样的,那么就将内存里面的值更新成B。CAS是经过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程须要自旋,到下次循环才有可能机会执行。

volatile有三个特性:可见性,不保证原子性,禁止指令重排。

可见性:线程1从主内存中拿数据1到本身的线程工做空间进行操做(假设是加1)这个时候数据1已经改成数据2了,将数据2写回主内存时通知其余线程(线程2,线程3),主内存中的数据1已改成数据2了,让其余线程从新拿新的数据(数据2)。

不保证原子性:线程1从主内存中拿了一个值为1的数据到本身的工做空间里面进行加1的操做,值变为2,写回主内存,而后尚未来得及通知其余线程,线程1就被线程2抢占了,CPU分配,线程1被挂起,线程2仍是拿着原来主内存中的数据值为1进行加1,值变成2,写回主内存,将主内存值为2的替换成2,这时线程1的通知到了,线程2从新去主内存拿值为2的数据。

禁止指令重排:首先指令重排是程序执行的时候不老是从上往下执行的,就像高考答题,能够先作容易的题目再作难的,这时作题的顺序就不是从上往下了。禁止指令重排就杜绝了这种状况。

(通常面试官开始问你会从java基础问起,一问大多数会问到集合这一块,而集合问的较多的是HashMap,这个时候你就能够往这些方向带着面试官问你,并且扩展的深度也够,因此上面的干货够你说个十来分钟吧,第一个问题拿下后,面试官内心至少简单你的基础够扎实,第一印象分就留下了)

Spring的AOP和IOC是什么?使用场景有哪些?Spring事务与数据库事务,传播行为,数据库隔离级别

AOP:面向切面编程。

即在一个功能模块中新增其余功能,比方说你要下楼取个快递,你同事对你说帮我也取一下呗,你就顺道取了。在工做中若是系统中有些包和类中没有使用AOP,例如日志,事务和异常处理,那么就必须在每一个类和方法中去实现它们。 代码纠缠每一个类和方法中都包含日志,事务以及异常处理甚至是业务逻辑。在一个这样的方法中,很难分清代码中实际作的是什么处理。AOP 所作的就是将全部散落各处的事务代码集中到一个事务切面中。

场景

比方说我如今要弄一个日志,记录某些个接口调用的方法时间。使用Aop我能够在这个接口前插入一段代码去记录开始时间,在这个接口后面去插入一段代码记录结束时间。

又或者你去访问数据库,而你不想管事务(太烦),因此,Spring在你访问数据库以前,自动帮你开启事务,当你访问数据库结束以后,自动帮你提交/回滚事务!

异常处理你能够开启环绕通知,一旦运行接口报错,环绕通知捕获异常跳转异常处理页面。

动态代理

Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的所有方法,而且在特定的切点作了加强处理,并回调原对象的方法。它的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理经过反射来接收被代理的类,而且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。若是目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB是一个代码生成的类库,能够在运行时动态的生成某个类的子类,注意,CGLIB是经过继承的方式作的动态代理,所以若是某个类被标记为final,那么它是没法使用CGLIB作动态代理的。

IOC:依赖注入或者叫作控制反转。

正常状况下咱们使用一个对象时都是须要new Object()的。而ioc是把须要使用的对象提早建立好,放到spring的容器里面。

全部须要使用的类都会在spring容器中登记,告诉spring你是个什么东西,你须要什么东西,而后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其余须要你的东西。全部的类的建立、销毁都由 spring来控制,也就是说控制对象生存周期的再也不是引用它的对象,而是spring。DI(依赖注入)其实就是IOC的另一种说法,其实它们是同一个概念的不一样角度描述。

场景:

正常状况下咱们使用一个对象时都是须要new Object() 的。而ioc是把须要使用的对象提早建立好,放到spring的容器里面。须要使用的时候直接使用就行,并且能够设置单例或多例,很是灵活。

咱们在service层想调用另一个service的方法,不须要去new了,直接把它交给spring管理,而后用注解的方式引入就能使用。

IOC三种注入方式

(1)XML:Bean实现类来自第三方类库,例如DataSource等。须要命名空间等配置,例如:context,aop,mvc。

(2)注解:在开发的类使用@Controller,@Service等注解

(3)Java配置类:经过代码控制对象建立逻辑的场景。例如:自定义修改依赖类库。

什么是事务?

事务是访问并可能更新数据库中各类数据项的一个程序执行单元。

Spring事务与数据库事务关系?

Spring的事务是对数据库的事务的封装,最后本质的实现仍是在数据库,假如数据库不支持事务的话,Spring的事务是没有做用的。因此说Spring事务的底层依赖MySQL的事务,Spring是在代码层面利用AOP实现,执行事务的时候使用TransactionInceptor进行拦截,而后处理。本质是对方法先后进行拦截,而后在目标方法开始以前建立或者加入一个事务,执行完目标方法以后根据执行的状况提交或者回滚。

属性(特性)
A(原子性):要么所有完成,要么彻底不起做用
C(一致性):一旦事务完成(无论成功仍是失败),业务处于一致的状态,而不会是部分完成,部分失败。
I(隔离性):多事务会同时处理相同的数据,所以每一个事务都应该与其余事务隔离开来,防止数据损坏。
D(持久性):一旦事务完成,不管发生什么系统错误,它的结果都不该该受到影响,事务的结果被写到持久化存储器中。

什么叫事务传播行为?

传播,至少有两个东西,才能够发生传播。单体不存在传播这个行为。事务传播行为就是当一个事务方法被另外一个事务方法调用时,这个事务方法应该如何进行。

Spring支持7中事务传播行为
propagation_required(须要传播):当前没有事务则新建事务,有则加入当前事务
propagation_supports(支持传播):支持当前事务,若是当前没有事务则以非事务方式执行
propagation_mandatory(强制传播):使用当前事务,若是没有则抛出异常
propagation_nested(嵌套传播):若是当前存在事务,则在嵌套事务内执行,若是当前没有事务,则执行须要传播行为。
propagation_never(毫不传播):以非事务的方式执行,若是当前有事务则抛出异常
propagation_requires_new(传播须要新的):新建事务,若是当前有事务则把当前事务挂起
propagation_not_supported(不支持传播):以非事务的方式执行,若是当前有事务则把当前事务挂起

数据库事务的隔离级别

数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别能够逐个解决脏读、不可重复读、幻读这几类问题。

√: 可能出现 ×: 不会出现

说明 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

注意:咱们讨论隔离级别的场景,主要是在多个事务并发的状况下,所以,接下来的讲解都围绕事务并发。
Read uncommitted 读未提交
公司发工资了,领导把20000元打到廖志伟的帐号上,可是该事务并未提交,而廖志伟正好去查看帐户,发现工资已经到帐,是20000元整,很是高兴。但是不幸的是,领导发现发给廖志伟的工资金额不对,是16000元,因而迅速回滚了事务,修改金额后,将事务提交,最后廖志伟实际的工资只有16000元,廖志伟空欢喜一场。

出现上述状况,即咱们所说的脏读,两个并发的事务,“事务A:领导给廖志伟发工资”、“事务B:廖志伟查询工资帐户”,事务B读取了事务A还没有提交的数据。当隔离级别设置为Read uncommitted时,就可能出现脏读,如何避免脏读,请看下一个隔离级别。

Read committed 读提交
廖志伟拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转帐,把廖志伟工资卡的2000元转到另外一帐户,并在廖志伟以前提交了事务,当廖志伟扣款时,系统检查到廖志伟的工资卡已经没有钱,扣款失败,廖志伟十分纳闷,明明卡里有钱,为什么…

出现上述状况,即咱们所说的不可重复读,两个并发的事务,“事务A:廖志伟消费”、“事务B:廖志伟的老婆网上转帐”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。当隔离级别设置为Read committed时,避免了脏读,可是可能会形成不可重复读。大多数数据库的默认级别就是Read committed,好比Sql Server , Oracle。如何解决不可重复读这一问题,请看下一个隔离级别。

Repeatable read 重复读
当廖志伟拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),廖志伟的老婆就不可能对该记录进行修改,也就是廖志伟的老婆不能在此时转帐。这就避免了不可重复读。廖志伟的老婆工做在银行部门,她时常经过银行内部系统查看廖志伟的信用卡消费记录。有一天,她正在查询到廖志伟当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而廖志伟此时正好在外面胡吃海喝后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction … ),并提交了事务,随后廖志伟的老婆将廖志伟当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,廖志伟的老婆很诧异,觉得出现了幻觉,幻读就这样产生了。当隔离级别设置为Repeatable read时,能够避免不可重复读,但会出现幻读。注:MySQL的默认隔离级别就是Repeatable read。

Serializable 序列化
Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,通常不多使用,在该级别下,事务顺序执行,不只能够避免脏读、不可重复读,还避免了幻像读。

 

Spring和SpringMVC,MyBatis以及SpringBoot的注解分别有哪些?SpringMVC的工做原理,SpringBoot框架的优势,MyBatis框架的优势

Spring注解:

声明bean的注解
@Component 组件,没有明确的角色
@Service 在业务逻辑层使用(service层)
@Repository 在数据访问层使用(dao层)
@Controller 在展示层使用,控制器的声明(C)
注入bean的注解
@Autowired 由Spring提供
@Resource 由JSR-250提供
java配置类相关注解
@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
@Configuration 声明当前类为配置类,其中内部组合了@Component注解,代表这个类是一个bean(类上)
@ComponentScan 用于对Component进行扫描,至关于xml中的(类上)
切面(AOP)相关注解
@Aspect 声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)做为参数。
@After 在方法执行以后执行(方法上) @Before 在方法执行以前执行(方法上) @Around 在方法执行以前与以后执行(方法上)
@PointCut 声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
@Value注解
@Value 为属性注入值 注入操做系统属性@Value("#{systemProperties['os.name']}")String osName;
注入表达式结果@Value("#{ T(java.lang.Math).random() * 100 }") String randomNumber;
注入其它bean属性@Value("#{domeClass.name}")String name;
注入文件资源@Value("classpath:com/hgs/hello/test.txt")String Resource file;
注入网站资源@Value("http://www.cznovel.com")Resource url;
注入配置文件Value("${book.name}")String bookName;
异步相关
@EnableAsync 配置类中,经过此注解开启对异步任务的支持,叙事性AsyncConfigurer接口(类上)
@Async 在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上全部的方法都将异步,须要@EnableAsync开启异步任务)
定时任务相关
@EnableScheduling 在配置类上使用,开启计划任务的支持(类上)
@Scheduled 来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)

SpringMVC注解

@EnableWebMvc 在配置类中开启Web MVC的配置支持,如一些ViewResolver或者MessageConverter等,若无此句,重写WebMvcConfigurerAdapter方法(用于对SpringMVC的配置)。
@Controller 声明该类为SpringMVC中的Controller
@RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)
@ResponseBody 支持将返回值放在response内,而不是一个页面,一般用户返回json数据(返回值旁或方法上)
@RequestBody 容许request的参数在request体中,而不是在直接链接在地址后面。(放在参数前)
@PathVariable 用于接收路径参数,好比@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,便可获取该值,一般做为Restful的接口实现方法。
@RestController 该注解为一个组合注解,至关于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的全部方法都默认加上了@ResponseBody。
@ControllerAdvice 经过该注解,咱们能够将对于控制器的全局配置放置在同一个位置,注解了@Controller的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上, 这对全部注解了 @RequestMapping的控制器内的方法有效。
@ExceptionHandler 用于全局处理控制器里的异常
@InitBinder 用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。
@ModelAttribute 原本的做用是绑定键值对到Model里,在@ControllerAdvice中是让全局的@RequestMapping都能得到在此处设置的键值对。

Mybatis注解:(偷个懒,不使用表格了,嘻嘻)

  • 增删改查:@Insert、@Update、@Delete、@Select、@MapKey、@Options、@SelelctKey、@Param、@InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider

  • 结果集映射:@Results、@Result、@ResultMap、@ResultType、@ConstructorArgs、@Arg、@One、@Many、@TypeDiscriminator、@Case

  • 缓存:@CacheNamespace、@Property、@CacheNamespaceRef、@Flush

SpringBoot注解:

  • @SpringBootApplication:申明让spring boot自动给程序进行必要的配置,这个配置等同于:
  • @Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三个配置。
  • @ResponseBody:表示该方法的返回结果直接写入HTTP response body中,通常在异步获取数据时使用,用于构建RESTful的api。在使用@RequestMapping后,返回值一般解析为跳转路径,加上@esponsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。好比异步获取json数据,加上@Responsebody后,会直接返回json数据。该注解通常会配合@RequestMapping一块儿使用。
  • @Controller:用于定义控制器类,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),通常这个注解在类中,一般方法须要配合注解@RequestMapping。
  • @RestController:用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。
  • @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。
  • @EnableAutoConfiguration:SpringBoot自动配置(auto-configuration):尝试根据你添加的jar依赖自动配置你的Spring应用。例如,若是你的classpath下存在HSQLDB,而且你没有手动配置任何数据库链接beans,那么咱们将自动配置一个内存型(in-memory)数据库”。你能够将@EnableAutoConfiguration或者@SpringBootApplication注解添加到一个@Configuration类上来选择自动配置。若是发现应用了你不想要的特定自动配置类,你能够使用@EnableAutoConfiguration注解的排除属性来禁用它们。
  • @ComponentScan:表示将该类自动发现扫描组件。我的理解至关于,若是扫描到有@Component、@Controller、@Service等这些注解的类,并注册为Bean,能够自动收集全部的Spring组件,包括@Configuration类。咱们常用@ComponentScan注解搜索beans,并结合@Autowired注解导入。能够自动收集全部的Spring组件,包括@Configuration类。咱们常用@ComponentScan注解搜索beans,并结合@Autowired注解导入。若是没有配置的话,Spring Boot会扫描启动类所在包下以及子包下的使用了@Service,@Repository等注解的类。
  • @Configuration:至关于传统的xml配置文件,若是有些第三方库须要用到xml文件,建议仍然经过@Configuration类做为项目的配置主类——能够使用@ImportResource注解加载xml配置文件。
  • @Import:用来导入其余配置类。
  • @ImportResource:用来加载xml配置文件。
  • @Repository:使用@Repository注解能够确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不须要为它们提供XML配置项。
  • @Bean:用@Bean标注方法等价于XML中配置的bean
  • @AutoWired:自动导入依赖的bean。byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它能够对类成员变量、方法及构造函数进行标注,完成自动装配的工做。当加上(required=false)时,就算找不到bean也不报错。
  • @Qualifier:当有多个同一类型的Bean时,能够用@Qualifier(“name”)来指定。与@Autowired配合使用。@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式以下:
  • @Resource(name=”name”,type=”type”):没有括号内内容的话,默认byName。与@Autowired干相似的事。

SpringMVC的工做原理:

SpringBoot框架的优势:

  • --建立独立的 Spring 应用程序 ;
  • --嵌入的 Tomcat 、 Jetty 或者 Undertow,无须部署 WAR 文件:
  • --容许经过 Maven 来根据须要获取 starter;
  • --尽量地自动配置 Spring;
  • --提供生产就绪型功能,如指标、健康检查和外部配置;
  • --绝对没有代码生成,对 XML 没有要求配置 。

MyBatis框架的优势:

  • JDBC相比,减小了50%以上的代码量,消除了JDBC大量冗余的代码,不须要手动开关链接
  • 很好的与各类数据库兼容(由于MyBatis使用JDBC来链接数据库,因此只要JDBC支持的数据库MyBatis都支持,而JDBC提供了可扩展性,因此只要这个数据库有针对Java的jar包就能够就能够与MyBatis兼容),开发人员不须要考虑数据库的差别性。
  • 提供了不少第三方插件(分页插件 / 逆向工程)
  • SQL写在XML里,从程序代码中完全分离,解除sql与程序代码的耦合,便于统一管理和优化,并可重用。
  • 提供映射标签,支持对象与数据库的ORM字段关系映射。

SpringCould组件有哪些,他们的做用是什么(说七八个)?微服务的CAP是什么?BASE是什么?

先讲五大核心组件,(偷个懒,嘻嘻)这里我引用一位大佬讲解的,原文地址是:http://www.javashuo.com/article/p-xrlwzdkw-cx.html

1、业务场景介绍

先来给你们说一个业务场景,假设我们如今开发一个电商网站,要实现支付订单的功能,流程以下:

  • 建立一个订单后,若是用户马上支付了这个订单,咱们须要将订单状态更新为“已支付”
  • 扣减相应的商品库存
  • 通知仓储中心,进行发货
  • 给用户的此次购物增长相应的积分

针对上述流程,咱们须要有订单服务、库存服务、仓储服务、积分服务。整个流程的大致思路以下:

  • 用户针对一个订单完成支付以后,就会去找订单服务,更新订单状态
  • 订单服务调用库存服务,完成相应功能
  • 订单服务调用仓储服务,完成相应功能
  • 订单服务调用积分服务,完成相应功能

至此,整个支付订单的业务流程结束

下图这张图,清晰代表了各服务间的调用过程:

好!有了业务场景以后,我们就一块儿来看看Spring Cloud微服务架构中,这几个组件如何相互协做,各自发挥的做用以及其背后的原理。

2、Spring Cloud核心组件:Eureka

我们来考虑第一个问题:订单服务想要调用库存服务、仓储服务,或者积分服务,怎么调用?

  • 订单服务压根儿就不知道人家库存服务在哪台机器上啊!他就算想要发起一个请求,都不知道发送给谁,有心无力!
  • 这时候,就轮到Spring Cloud Eureka出场了。Eureka是微服务架构中的注册中心,专门负责服务的注册与发现。

我们来看看下面的这张图,结合图来仔细剖析一下整个流程:

如上图所示,库存服务、仓储服务、积分服务中都有一个Eureka Client组件,这个组件专门负责将这个服务的信息注册到Eureka Server中。说白了,就是告诉Eureka Server,本身在哪台机器上,监听着哪一个端口。而Eureka Server是一个注册中心,里面有一个注册表,保存了各服务所在的机器和端口号

订单服务里也有一个Eureka Client组件,这个Eureka Client组件会找Eureka Server问一下:库存服务在哪台机器啊?监听着哪一个端口啊?仓储服务呢?积分服务呢?而后就能够把这些相关信息从Eureka Server的注册表中拉取到本身本地缓存起来。

这时若是订单服务想要调用库存服务,不就能够找本身本地的Eureka Client问一下库存服务在哪台机器?监听哪一个端口吗?收到响应后,紧接着就能够发送一个请求过去,调用库存服务扣减库存的那个接口!同理,若是订单服务要调用仓储服务、积分服务,也是如法炮制。

总结一下:

  • Eureka Client:负责将这个服务的信息注册到Eureka Server中
  • Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号

3、Spring Cloud核心组件:Feign

如今订单服务确实知道库存服务、积分服务、仓库服务在哪里了,同时也监听着哪些端口号了。可是新问题又来了:难道订单服务要本身写一大堆代码,跟其余服务创建网络链接,而后构造一个复杂的请求,接着发送请求过去,最后对返回的响应结果再写一大堆代码来处理吗?

这是上述流程翻译的代码片断,我们一块儿来看看,体会一下这种绝望而无助的感觉!!!

友情提示,前方高能:

看完上面那一大段代码,有没有感到后背发凉、一身冷汗?实际上你进行服务间调用时,若是每次都手写代码,代码量比上面那段要多至少几倍,因此这个事压根儿就不是地球人能干的。

既然如此,那怎么办呢?别急,Feign早已为咱们提供好了优雅的解决方案。来看看若是用Feign的话,你的订单服务调用库存服务的代码会变成啥样?

看完上面的代码什么感受?是否是感受整个世界都干净了,又找到了活下去的勇气!没有底层的创建链接、构造请求、解析响应的代码,直接就是用注解定义一个 FeignClient接口,而后调用那个接口就能够了。人家Feign Client会在底层根据你的注解,跟你指定的服务创建链接、构造请求、发起靕求、获取响应、解析响应,等等。这一系列脏活累活,人家Feign全给你干了。

那么问题来了,Feign是如何作到这么神奇的呢?很简单,Feign的一个关键机制就是使用了动态代理。我们一块儿来看看下面的图,结合图来分析:

  • 首先,若是你对某个接口定义了@FeignClient注解,Feign就会针对这个接口建立一个动态代理
  • 接着你要是调用那个接口,本质就是会调用 Feign建立的动态代理,这是核心中的核心
  • Feign的动态代理会根据你在接口上的@RequestMapping等注解,来动态构造出你要请求的服务的地址
  • 最后针对这个地址,发起请求、解析响应

4、Spring Cloud核心组件:Ribbon

说完了Feign,还没完。如今新的问题又来了,若是人家库存服务部署在了5台机器上,以下所示:

  • 192.168.169:9000
  • 192.168.170:9000
  • 192.168.171:9000
  • 192.168.172:9000
  • 192.168.173:9000

这下麻烦了!人家Feign怎么知道该请求哪台机器呢?

  • 这时Spring Cloud Ribbon就派上用场了。Ribbon就是专门解决这个问题的。它的做用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把请求分发到各个机器上
  • Ribbon的负载均衡默认使用的最经典的Round Robin轮询算法。这是啥?简单来讲,就是若是订单服务对库存服务发起10次请求,那就先让你请求第1台机器、而后是第2台机器、第3台机器、第4台机器、第5台机器,接着再来—个循环,第1台机器、第2台机器。。。以此类推。

此外,Ribbon是和Feign以及Eureka紧密协做,完成工做的,具体以下:

  • 首先Ribbon会从 Eureka Client里获取到对应的服务注册表,也就知道了全部的服务都部署在了哪些机器上,在监听哪些端口号。
  • 而后Ribbon就能够使用默认的Round Robin算法,从中选择一台机器
  • Feign就会针对这台机器,构造并发起请求。

对上述整个过程,再来一张图,帮助你们更深入的理解:

 

5、Spring Cloud核心组件:Hystrix

在微服务架构里,一个系统会有不少的服务。以本文的业务场景为例:订单服务在一个业务流程里须要调用三个服务。如今假设订单服务本身最多只有100个线程能够处理请求,而后呢,积分服务不幸的挂了,每次订单服务调用积分服务的时候,都会卡住几秒钟,而后抛出—个超时异常。

我们一块儿来分析一下,这样会致使什么问题?

  1. 若是系统处于高并发的场景下,大量请求涌过来的时候,订单服务的100个线程都会卡在请求积分服务这块。致使订单服务没有一个线程能够处理请求
  2. 而后就会致使别人请求订单服务的时候,发现订单服务也挂了,不响应任何请求了

上面这个,就是微服务架构中恐怖的服务雪崩问题

以下图所示:

如上图,这么多服务互相调用,要是不作任何保护的话,某一个服务挂了,就会引发连锁反应,致使别的服务也挂。好比积分服务挂了,会致使订单服务的线程所有卡在请求积分服务这里,没有一个线程能够工做,瞬间致使订单服务也挂了,别人请求订单服务所有会卡住,没法响应。

可是咱们思考一下,就算积分服务挂了,订单服务也能够不用挂啊!为何?

  • 咱们结合业务来看:支付订单的时候,只要把库存扣减了,而后通知仓库发货就OK了
  • 若是积分服务挂了,大不了等他恢复以后,慢慢人肉手工恢复数据!为啥必定要由于一个积分服务挂了,就直接致使订单服务也挂了呢?不能够接受!

如今问题分析完了,如何解决?

这时就轮到Hystrix闪亮登场了。Hystrix是隔离、熔断以及降级的一个框架。啥意思呢?说白了,Hystrix会搞不少个小小的线程池,好比订单服务请求库存服务是一个线程池,请求仓储服务是一个线程池,请求积分服务是一个线程池。每一个线程池里的线程就仅仅用于请求那个服务。

打个比方:如今很不幸,积分服务挂了,会咋样?

固然会致使订单服务里那个用来调用积分服务的线程都卡死不能工做了啊!但因为订单服务调用库存服务、仓储服务的这两个线程池都是正常工做的,因此这两个服务不会受到任何影响。

这个时候若是别人请求订单服务,订单服务仍是能够正常调用库存服务扣减库存,调用仓储服务通知发货。只不过调用积分服务的时候,每次都会报错。可是若是积分服务都挂了,每次调用都要去卡住几秒钟干啥呢?有意义吗?固然没有!因此咱们直接对积分服务熔断不就得了,好比在5分钟内请求积分服务直接就返回了,不要去走网络请求卡住几秒钟,这个过程,就是所谓的熔断!

那人家又说,兄弟,积分服务挂了你就熔断,好歹你干点儿什么啊!别啥都不干就直接返回啊?没问题,我们就来个降级:每次调用积分服务,你就在数据库里记录一条消息,说给某某用户增长了多少积分,由于积分服务挂了,致使没增长成功!这样等积分服务恢复了,你能够根据这些记录手工加一下积分。这个过程,就是所谓的降级。

为帮助你们更直观的理解,接下来用一张图,梳理一下Hystrix隔离、熔断和降级的全流程:

6、Spring Cloud核心组件:Zuul

说完了Hystrix,接着给你们说说最后一个组件:Zuul,也就是微服务网关。这个组件是负责网络路由的。不懂网络路由?行,那我给你说说,若是没有Zuul的平常工做会怎样?

假设你后台部署了几百个服务,如今有个前端兄弟,人家请求是直接从浏览器那儿发过来的。打个比方:人家要请求一下库存服务,你难道还让人家记着这服务的名字叫作inventory-service?部署在5台机器上?就算人家肯记住这一个,你后台可有几百个服务的名称和地址呢?难不成人家请求一个,就得记住一个?你要这样玩儿,那真是友谊的小船,说翻就翻!

上面这种状况,压根儿是不现实的。因此通常微服务架构中都必然会设计一个网关在里面,像android、ios、pc前端、微信小程序、H5等等,不用去关心后端有几百个服务,就知道有一个网关,全部请求都往网关走,网关会根据请求中的一些特征,将请求转发给后端的各个服务。

并且有一个网关以后,还有不少好处,好比能够作统一的降级、限流、认证受权、安全,等等。

7、总结:

最后再来总结一下,上述几个Spring Cloud核心组件,在微服务架构中,分别扮演的角色:

  • Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,而且Eureka Client还能够反过来从Eureka Server拉取注册表,从而知道其余服务在哪里
  • Ribbon:服务间发起请求的时候,基于Ribbon作负载均衡,从一个服务的多台机器中选择一台
  • Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
  • Hystrix:发起请求是经过Hystrix的线程池来走的,不一样的服务走不一样的线程池,实现了不一样服务调用的隔离,避免了服务雪崩的问题
  • Zuul:若是前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务

以上就是咱们经过一个电商业务场景,阐述了Spring Cloud微服务架构几个核心组件的底层原理。

文字总结还不够直观?没问题!咱们将Spring Cloud的5个核心组件经过一张图串联起来,再来直观的感觉一下其底层的架构原理:


 

五大核心组件讲完了,面试官心中已经知道你对SpringCould的有必定的了解了,但这还不够,你若是讲到这个层面,部分面试官还会继续问,由于你讲解的这些其余面试者也讲过,可能也就你讲的比较细一些,但本质仍是和他们差不了太多,有些公司可能集中招人,负责面试的可能就一个,你想一想他这一天能够面试多少我的,这个时候你就须要继续拓展其余组件,来突出你的不一样了。

Spring Cloud Sleuth(服务链路追踪),Spring Cloud Bus(消息总线),Spring Cloud Config(分布式配置中心)之类的,这里我就不继续写了,给上一个SpringCould专栏(一位大佬写的,挺不错的)你去看看吧,最好能实现动手敲上一套,后面你会发现本身对SpringCould的理解远超其余人。专栏地址是:http://www.javashuo.com/article/p-vpguiuwq-dw.html

CAP 定论

一个分布式系统最多只能同时知足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
C 一致性即更新操做成功并返回客户端完成后,全部节点在同一时间的数据彻底一致。
A 可用性服务一直可用,并且是正常响应时间。
P 分区容错性即分布式系统在遇到某节点或网络分区故障的时候,仍然可以对外提供知足一致性和可用性的服务。

  1. 对于多数大型互联网应用的场景,通常保证知足 P 和 A,舍弃 C(一致性没法保证,退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到形成用户流失的严重程度。如原来同步架构的时候若是没有库存,就立刻告诉客户库存不足没法下单。但在微服务框架下订单和库存多是两个微服务对应两个数据库,用户下单时订单服务是当即生成的,极可能过了一会系统通知你订单被取消掉(最终一致性)。就像抢购“小米手机”同样,几十万人在排队,排了好久告诉你没货了,明天再来吧。
  2. 对于涉及到钱财这样不能有一丝让步的场景,C 必须保证。网络发生故障宁肯中止服务,这是保证 CA,舍弃 P。
  3. 还有一种是保证 CP,舍弃 A。例如网络故障事只读不写。

BASE

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对 CAP 中 AP 的一个扩展

  1. 基本可用:分布式系统在出现故障时,容许损失部分可用功能,保证核心功能可用。
  2. 软状态:容许系统中存在中间状态,这个状态不影响系统可用性,这里指的是 CAP 中的不一致。
  3. 最终一致:最终一致是指通过一段时间后,全部节点数据都将会达到一致。

BASE 解决了 CAP 中理论没有网络延迟,在 BASE 中用软状态和最终一致,保证了延迟后的一致性。BASE 和 ACID 是相反的,它彻底不一样于 ACID 的强一致性模型,而是经过牺牲强一致性来得到可用性,并容许数据在一段时间内是不一致的,但最终达到一致状态。

设计模式

1. 根据目的来分

根据模式是用来完成什么工做来划分,这种方式可分为建立型模式、结构型模式和行为型模式 3 种。

  1. 建立型模式:用于描述“怎样建立对象”,它的主要特色是“将对象的建立与使用分离”。GoF 中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种建立型模式。
  2. 结构型模式:用于描述如何将类或对象按某种布局组成更大的结构,GoF 中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
  3. 行为型模式:用于描述类或对象之间怎样相互协做共同完成单个对象都没法单独完成的任务,以及怎样分配职责。GoF 中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。

2. 根据做用范围来分

根据模式是主要用于类上仍是主要用于对象上来分,这种方式可分为类模式和对象模式两种。

  1. 类模式:用于处理类与子类之间的关系,这些关系经过继承来创建,是静态的,在编译时刻便肯定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。
  2. 对象模式:用于处理对象之间的关系,这些关系能够经过组合或聚合来实现,在运行时刻是能够变化的,更具动态性。GoF 中除了以上 4 种,其余的都是对象模式。

3.设计模式的功能

  • 一、FACTORY 工厂方法:追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不一样,但无论你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就好了。麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开。消费者任什么时候候须要某种产品,只需向工厂请求便可。消费者无须修改就能够接纳新产品。缺点是当产品修改时,工厂类也要作相应的修改。如:如何建立及如何向客户端提供。
  • 二、BUILDER建造者模式:MM最爱听的就是“我爱你”这句话了,见到不一样地方的MM,要可以用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就可以用相应的语言说出“我爱你”这句话了,国外的MM也能够轻松搞掂,这就是个人“我爱你”builder。(这必定比美军在伊拉克用的翻译机好卖) 建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具备不一样的内部表象的产品对象。建造模式使得产品内部表象能够独立的变化,客户没必要知道产品内部组成的细节。建造模式能够强制实行一种分步骤进行的建造过程。
  • 三、FACTORY METHOD抽象工厂:请MM去麦当劳吃汉堡,不一样的MM有不一样的口味,要每一个都记住是一件烦人的事情,我通常采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就好了。 工厂方法模式:核心工厂类再也不负责全部产品的建立,而是将具体建立的工做交给子类去作,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪个产品类应当被实例化这种细节。
  • 四、PROTOTYPE 原型模式:跟MM用QQ聊天,必定要说些深情的话语了,我搜集了好多肉麻的情话,须要时只要copy出来放到QQ里面就好了,这就是个人情话prototype了。(100块钱一份,你要不要) 原始模型模式:经过给出一个原型对象来指明所要建立的对象的类型,而后用复制这个原型对象的方法建立出更多同类型的对象。原始模型模式容许动态的增长或减小产品类,产品类不须要非得有任何事先肯定的等级结构,原始模型模式适用于任何的等级结构。缺点是每个类都必须配备一个克隆方法。
  • 五、SINGLETON 单态模式:俺有6个漂亮的老婆,她们的老公都是我,我就是咱们家里的老公Sigleton,她们只要说道“老公”,都是指的同一我的,那就是我(刚才作了个梦啦,哪有这么好的事) 单例模式:单例模式确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可以使用。 [b:9ceca65206]结构型模式[/b:9ceca65206]
  • 六、ADAPTER 适配器模式:在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于个人朋友kent了,他做为我和Sarah之间的Adapter,让我和Sarah能够相互交谈了(也不知道他会不会耍我) 适配器(变压器)模式:把一个类的接口变换成客户端所期待的另外一种接口,从而使本来因接口缘由不匹配而没法一块儿工做的两个类可以一块儿工做。适配类能够根据参数返还一个合适的实例给客户端。
  • 七、BRIDGE 桥梁模式:早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新作的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新作了个发型怎么说”这种问题,本身用BRIDGE组合一下不就好了 桥梁模式:将抽象化与实现化脱耦,使得两者能够独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使二者能够独立的变化。
  • 八、COMPOSITE合成模式:Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你本身挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有? 合成模式:合成模式将对象组织到树结构中,能够用来描述总体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与总体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。
  • 九、DECORATOR装饰模式:Mary过完轮到Sarly过生日,仍是不要叫她本身挑了,否则这个月伙食费确定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,咱们都是Decorator,最终都在修饰我这我的呀,怎么样,看懂了吗? 装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增长功能,这些功能能够再动态的撤消。1增长由一些基本功能的排列组合而产生的很是大量的功能。
  • 十、FACADE门面模式:我有一个专业的Nikon相机,我就喜欢本身手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸亏相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就好了,一切由相机自动调整,这样MM也能够用这个相机给我拍张照片了。 门面模式:外部与一个子系统的通讯必须经过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每个子系统只有一个门面类,并且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统能够有多个门面类。
  • 十一、FLYWEIGHT享元模式:天天跟MM发短信,手指都累死了,最近买了个新手机,能够把一些经常使用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就能够发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文状况使用。 享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能作到共享的关键是区份内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不一样。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将能够共享的状态和不能够共享的状态从常规类中区分开来,将不能够共享的状态从类里剔除出去。客户端不能够直接建立被共享的对象,而应当使用一个工厂对象负责建立被共享的对象。享元模式大幅度的下降内存中对象的数量。
  • 十二、PROXY代理模式:跟MM在网上聊天,一开头老是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序作为个人Proxy吧,凡是接收到这些话都设置好了本身的回答,接收到其余的话时再通知我回答,怎么样,酷吧。 代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一我的或一个机构表明另外一我的或者一个机构采起行动。某些状况下,客户不想或者不可以直接引用一个对象,代理对象能够在客户和目标对象直接起到中介的做用。客户端分辨不出代理主题对象与真实主题对象。代理模式能够并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不可以建立被代理对象,被代理对象必须有系统的其余角色代为建立并传入。
  • 1三、CHAIN OF RESPONSIBLEITY责任链模式:晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,能够作个人女友吗?若是不肯意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,据说是个老处女呀,快跑! 责任链模式:在责任链模式中,不少对象由每个对象对其下家的引用而接起来造成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪个对象最终处理这个请求,系统能够在不影响客户端的状况下动态的从新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求能够最终不被任何接收端对象所接受。
  • 1四、COMMAND命令模式:俺有一个MM家里管得特别严,无法见面,只好借助于她弟弟在咱们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男友送COMMAND,就数你最小气,才请我吃面。” 命令模式:命令模式把一个请求或者操做封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不一样的对象。命令模式容许请求的一方和发送的一方独立开来,使得请求的一方没必要知道接收请求的一方的接口,更没必要知道请求是怎么被接收,以及操做是否执行,什么时候被执行以及是怎么被执行的。系统支持命令的撤消。
  • 1五、INTERPRETER解释器模式:俺有一个《泡MM真经》,上面有各类泡MM的攻略,好比说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要作一个Interpreter,照着上面的脚本执行就能够了。 解释器模式:给定一个语言后,解释器模式能够定义出其文法的一种表示,并同时提供一个解释器。客户端能够使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象可以解释的任何组合。在解释器模式中须要定义一个表明文法的命令类的等级结构,也就是一系列的组合规则。每个命令对象都有一个解释方法,表明对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。
  • 1六、ITERATOR迭代子模式:我爱上了Mary,不顾一切的向她求婚。 Mary:“想要我跟你结婚,得答应个人条件” 我:“什么条件我都答应,你说吧” Mary:“我看上了那个一克拉的钻石” 我:“我买,我买,还有吗?” Mary:“我看上了湖边的那栋别墅” 我:“我买,我买,还有吗?” Mary:“我看上那辆法拉利跑车” 我脑壳嗡的一声,坐在椅子上,一咬牙:“我买,我买,还有吗?” …… 迭代子模式:迭代子模式能够顺序访问一个汇集中的元素而没必要暴露汇集的内部表象。多个对象聚在一块儿造成的整体称之为汇集,汇集对象是可以包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与汇集自己隔开。迭代子模式简化了汇集的界面。每个汇集对象均可以有一个或一个以上的迭代子对象,每个迭代子的迭代状态能够是彼此独立的。迭代算法能够独立于汇集角色变化。
  • 1七、MEDIATOR调停者模式:四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸好当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺获得了四个MM的电话。 调停者模式:调停者模式包装了一系列对象相互做用的方式,使得这些对象没必要相互明显做用。从而使他们能够松散偶合。当某些对象之间的做用发生改变时,不会当即影响其余的一些对象之间的做用。保证这些做用能够彼此独立的变化。调停者模式将多对多的相互做用转化为一对多的相互做用。调停者模式将对象的行为和协做抽象化,把对象在小尺度的行为上与其余对象的相互做用分开处理。
  • 1八、MEMENTO备忘录模式:同时跟几个MM聊天时,必定要记清楚刚才跟MM说了些什么话,否则MM发现了会不高兴的哦,幸好我有个备忘录,刚才与哪一个MM说了什么话我都拷贝一份放到备忘录里面保存,这样能够随时察看之前的记录啦。 备忘录模式:备忘录对象是一个用来存储另一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而能够在未来合适的时候把这个对象还原到存储起来的状态。
  • 1九、OBSERVER观察者模式:想知道我们公司最新MM情报吗?加入公司的MM情报邮件组就好了,tom负责搜集情报,他发现的新情报不用一个一个通知咱们,直接发布给邮件组,咱们做为订阅者(观察者)就能够及时收到情报啦 观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知全部观察者对象,使他们可以自动更新本身。
  • 20、STATE 状态模式:跟MM交往时,必定要注意她的状态哦,在不一样的状态时她的行为会有不一样,好比你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过能够带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,固然你看电影过程当中表现良好的话,也能够把MM的状态从不讨厌不喜欢变成喜欢哦。 状态模式:状态模式容许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类同样。状态模式把所研究的对象的行为包装在不一样的状态对象里,每个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式须要对每个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
  • 2一、STRATEGY 策略模式:跟不一样类型的MM约会,要用不一样的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了获得MM的芳心,个人追MM锦囊中有好多Strategy哦。 策略模式:策略模式针对一组算法,将每个算法封装到具备共同接口的独立的类中,从而使得它们能够相互替换。策略模式使得算法能够在不影响到客户端的状况下发生变化。策略模把行为和环境分开。环境类负责维持和查询行为类,各类算法在具体的策略类中提供。因为算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
  • 2二、TEMPLATE METHOD模板方法模式:看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每一个步骤针对不一样的状况,都有不同的作法,这就要看你随机应变啦(具体实现); 模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,而后声明一些抽象方法来迫使子类实现剩余的逻辑。不一样的子类能够以不一样的方式实现这些抽象方法,从而对剩余的逻辑有不一样的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。
  • 2三、VISITOR访问者模式:情人节到了,要给每一个MM送一束鲜花和一张卡片,但是每一个MM送的花都要针对她我的的特色,每张卡片也要根据我的的特色来挑,我一我的哪搞得清楚,仍是找花店老板和礼品店老板作一下Visitor,让花店老板根据MM的特色选一束花,让礼品店老板也根据每一个人特色选一张卡,这样就轻松多了; 访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操做。一旦这些操做须要修改的话,接受这个操做的数据结构能够保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和做用于结构上的操做之间的耦合解脱开,使得操做集合能够相对自由的演化。访问者模式使得增长新的操做变的很容易,就是增长一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽量多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式能够跨过几个类的等级结构访问属于不一样的等级结构的成员类。

Redis支持的数据类型以及使用场景,持久化,哨兵机制,缓存击穿,缓存穿透

简单介绍一个redis?

redis是内存中的数据结构存储系统,一个key-value类型的非关系型数据库,可持久化的数据库,相对于关系型数据库(数据主要存在硬盘中),性能高,所以咱们通常用redis来作缓存使用;而且redis支持丰富的数据类型,比较容易解决各类问题,所以redis能够用来做为注册中心,​数据库、缓存和消息中间件。Redis的Value支持5种数据类型,string、hash、list、set、zset(sorted set);

String类型:一个key对应一个value

Hash类型:它的key是string类型,value又是一个map(key-value),适合存储对象。

List类型:按照插入顺序的字符串链表(双向链表),主要命令是LPUSH和RPUSH,可以支持反向查找和遍历

Set类型:用哈希表类型的字符串序列,没有顺序,集合成员是惟一的,没有重复数据,底层主要是由一个value永远为null的hashmap来实现的。

zset类型:和set类型基本一致,不过它会给每一个元素关联一个double类型的分数(score),这样就能够为成员排序,而且插入是有序的。

你还用过其余的缓存吗?这些缓存有什么区别?都在什么场景下去用?

对于缓存了解过redis和memcache

Memcache和redis的区别:

数据支持的类型:redis不只仅支持简单的k/v类型的数据,同时还支持list、set、zset、hash等数据结构的存储;memcache只支持简单的k/v类型的数据,key和value都是string类型

可靠性:memcache不支持数据持久化,断电或重启后数据消失,但其稳定性是有保证的;redis支持数据持久化和数据恢复,容许单点故障,可是同时也会付出性能的代价

性能上:对于存储大数据,memcache的性能要高于redis

应用场景:

Memcache:适合多读少写,大数据量的状况(一些官网的文章信息等)

Redis:适用于对读写效率要求高、数据处理业务复杂、安全性要求较高的系统

案例:分布式系统,存在session之间的共享问题,所以在作单点登陆的时候,咱们利用redis来模拟了session的共享,来存储用户的信息,实现不一样系统的session共享;

对redis的持久化了解不?

redis的持久化方式有两种:

RDB(半持久化方式):按照配置不按期的经过异步的方式、快照的形式直接把内存中的数据持久化到磁盘的一个dump.rdb文件(二进制的临时文件)中,redis默认的持久化方式,它在配置文件(redis.conf)中。

优势:只包含一个文件,将一个单独的文件转移到其余存储媒介上,对于文件备份、灾难恢复而言,比较实用。

缺点:系统一旦在持久化策略以前出现宕机现象,此前没有来得及持久化的数据将会产生丢失

RDB持久化配置:

Redis会将数据集的快照dump到dump.rdb文件中。此外,咱们也能够经过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件以后,咱们搜索save,能够看到下面的配置信息:

save 900 1              #在900秒(15分钟)以后,若是至少有1个key发生变化,则dump内存快照。

save 300 10            #在300秒(5分钟)以后,若是至少有10个key发生变化,则dump内存快照。

save 60 10000        #在60秒(1分钟)以后,若是至少有10000个key发生变化,则dump内存快照。

AOF(全持久化的方式):把每一次数据变化都经过write()函数将你所执行的命令追加到一个appendonly.aof文件里面,Redis默认是不支持这种全持久化方式的,须要在配置文件(redis.conf)中将appendonly no改为appendonly yes

优势:数据安全性高,对日志文件的写入操做采用的是append模式,所以在写入过程当中即便出现宕机问题,也不会破坏日志文件中已经存在的内容;

缺点:对于数量相同的数据集来讲,aof文件一般要比rdb文件大,所以rdb在恢复大数据集时的速度大于AOF;

AOF持久化配置:

在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会都调用fsync刷新到aof文件,很是慢,可是安全;

appendfsync everysec  #每秒钟都调用fsync刷新到aof文件中,很快,可是可能丢失一秒内的数据,推荐使用,兼顾了速度和安全;

appendfsync no          #不会自动同步到磁盘上,须要依靠OS(操做系统)进行刷新,效率快,可是安全性就比较差;

二种持久化方式区别:

AOF在运行效率上每每慢于RDB,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB同样高效;

若是缓存数据安全性要求比较高的话,用aof这种持久化方式(好比项目中的购物车);

若是对于大数据集要求效率高的话,就能够使用默认的。并且这两种持久化方式能够同时使用。  

作过redis的集群吗?大家作集群的时候搭建了几台,都是怎么搭建的?

Redis的数据是存放在内存中的,不适合存储大数据,大数据存储通常公司经常使用hadoop中的Hbase或者MogoDB。redis主要用来处理高并发的,用咱们的项目来讲,电商项目若是并发大的话,一台单独的redis是不能足够支持咱们的并发,这就须要咱们扩展多台设备协同合做,即用到集群。

Redis搭建集群的方式有多种,例如:客户端分片、Twemproxy、Codis等,可是redis3.0以后就支持redis-cluster集群,这种方式采用的是无中心结构,每一个节点保存数据和整个集群的状态,每一个节点都和其余全部节点链接。若是使用的话就用redis-cluster集群。集群这块是公司运维搭建的,具体怎么搭建不是太了解。

咱们项目中redis集群主要搭建了6台,3主(为了保证redis的投票机制)3从(高可用),每一个主服务器都有一个从服务器,做为备份机。全部的节点都经过PING-PONG机制彼此互相链接;客户端与redis集群链接,只须要链接集群中的任何一个节点便可;Redis-cluster中内置了16384个哈希槽,Redis-cluster把全部的物理节点映射到【0-16383】slot上,负责维护。

redis有事务吗?

Redis是有事务的,redis中的事务是一组命令的集合,这组命令要么都执行,要不都不执行,保证一个事务中的命令依次执行而不被其余命令插入。redis的事务是不支持回滚操做的。redis事务的实现,须要用到MULTI(事务的开始)和EXEC(事务的结束)命令 ;

缓存穿透

缓存查询通常都是经过key去查找value,若是不存在对应的value,就要去数据库中查找。若是这个key对应的value在数据库中也不存在,而且对该key并发请求很大,就会对数据库产生很大的压力,这就叫缓存穿透

解决方案:

1.对全部可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。

2.将全部可能存在的数据哈希到一个足够大的bitmap中,一个必定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

3.若是一个查询返回的数据为空(无论是数 据不存在,仍是系统故障),咱们仍然把这个空结果进行缓存,但它的过时时间会很短,最长不超过五分钟。

缓存雪崩

当缓存服务器重启或者大量缓存集中在一段时间内失效,发生大量的缓存穿透,这样在失效的瞬间对数据库的访问压力就比较大,全部的查询都落在数据库上,形成了缓存雪崩。 这个没有完美解决办法,但能够分析用户行为,尽可能让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

解决方案:

1.在缓存失效后,经过加锁或者队列来控制读数据库写缓存的线程数量。好比对某个key只容许一个线程查询数据和写缓存,其余线程等待。
2.能够经过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
3.不一样的key,设置不一样的过时时间,让缓存失效的时间点尽可能均匀
4.作二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,能够访问A2,A1缓存失效时间设置为短时间,A2设置为长期。

redis的安全机制(大家公司redis的安全这方面怎么考虑的?)

漏洞介绍:redis默认状况下,会绑定在bind 0.0.0.0:6379,这样就会将redis的服务暴露到公网上,若是在没有开启认证的状况下,能够致使任意用户在访问目标服务器的状况下,未受权就可访问redis以及读取redis的数据,攻击者就能够在未受权访问redis的状况下能够利用redis的相关方法,成功在redis服务器上写入公钥,进而能够直接使用私钥进行直接登陆目标主机;

解决方案:

  1. 禁止一些高危命令。修改redis.conf文件,用来禁止远程修改DB文件地址,好比 rename-command FLUSHALL "" 、rename-command CONFIG"" 、rename-command EVAL “”等;
  2. 以低权限运行redis服务。为redis服务建立单独的用户和根目录,而且配置禁止登陆;
  3. 为redis添加密码验证。修改redis.conf文件,添加requirepass mypassword;
  4. 禁止外网访问redis。修改redis.conf文件,添加或修改 bind 127.0.0.1,使得redis服务只在当前主机使用;
  5. 作log监控,及时发现攻击;
  1. redis的哨兵机制(redis2.6之后出现的)

哨兵机制:

监控:监控主数据库和从数据库是否正常运行;

提醒:当被监控的某个redis出现问题的时候,哨兵能够经过API向管理员或者其余应用程序发送通知;

自动故障迁移:主数据库出现故障时,能够自动将从数据库转化为主数据库,实现自动切换;

具体的配置步骤参考的网上的文档。要注意的是,若是master主服务器设置了密码,记得在哨兵的配置文件(sentinel.conf)里面配置访问密码

redis中对于生存时间的应用

  Redis中能够使用expire命令设置一个键的生存时间,到时间后redis会自动删除;

  应用场景:

  1. 设置限制的优惠活动的信息;
  2. 一些及时须要更新的数据,积分排行榜;
  3. 手机验证码的时间;
  4. 限制网站访客访问频率;

线程是什么,有几种实现方式,它们之间的区别是什么,线程池实现原理,JUC并发包,ThreadLocal与Lock和Synchronize区别

什么是线程?讲个故事给你听,让你无法去背这个题,地址:http://www.javashuo.com/article/p-sgybkgzc-mm.html

有几种实现方式?

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口
  4. 线程池方式

优缺点

1.继承Thread类

  • 优势 、代码简单 。
  • 缺点 、该类没法集成别的类。

2.实现Runnable接口

  • 优势 、继承其余类。 同一实现该接口的实例能够共享资源。
  • 缺点 、代码复杂

3.实现Callable

  • 优势 、能够得到异步任务的返回值

4.线程池 、实现自动化装配,易于管理,循环利用资源。

代码实现案例:

继承Thread类,并重写里面的run方法
class A extends Thread{
    public void run(){
        for(int i=1;i<=100;i++){
            System.out.println("-----------------"+i);
        }
    }
}
A a = new A();
a.start();

实现Runnable接口,并实现里面的run方法
class B implements Runnable{
    public void run(){
        for(int i=1;i<=100;i++){
            System.out.println("-----------------"+i);
        }
    }
}
B b = new B();
Thread t = new Thread(b);
t.start();
实现Callable
class A implements Callable<String>{
    public String call() throws Exception{
        //...
    }
}
FutureTask<String> ft = new FutureTask<>(new A());
new Thread(ft).start();
线程池
ExcutorService es = Executors.newFixedThreadPool(10);
es.submit(new Runnable(){//任务});
es.submit(new Runnable(){//任务});
...
es.shutdown();

问题扩展

在Java中Lock接口比synchronized块的优点是什么?你须要实现一个高效的缓存,它容许多个用户读,但只容许一个用户写,以此来保持它的完整性,你会怎样去实现它?

总体上来讲Lock是synchronized的扩展版,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition方法)锁操做。另外Lock的实现类基本都支持非公平锁(默认)和公平锁,synchronized只支持非公平锁,固然,在大部分状况下,非公平锁是高效的选择。

线程池的实现原理https://blog.csdn.net/java_wxid/article/details/101844786

JUC并发包:

ThreadLocal与Lock和Synchronize区别

ThreadLocal与Lock和Synchronize区别
ThreadLocal为每个线程都提供了变量的副本,使得每一个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。ThreadLocal采用了“以空间换时间”的方式,为每个线程都提供了一份变量,所以能够同时访问而互不影响。
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。同步机制采用了“以时间换空间”的方式,仅提供一份变量,让不一样的线程排队访问。
若是一个代码块被synchronized关键字修饰,当一个线程获取了对应的锁,并执行该代码块时,其余线程便只能一直等待直至占有锁的线程释放锁。事实上,占有锁的线程释放锁通常会是如下三种状况之一:
占有锁的线程执行完了该代码块,而后释放对锁的占有;
占有锁线程执行发生异常,此时JVM会让线程自动释放锁;
占有锁线程进入 WAITING 状态从而释放锁,例如在该线程中调用wait()方法等。
synchronized 是Java语言的内置特性,能够轻松实现对临界资源的同步互斥访问。那么,为何还会出现Lock呢?试考虑如下三种状况:
Case 1 :
在使用synchronized关键字的情形下,假如占有锁的线程因为要等待IO或者其余缘由(好比调用sleep方法)被阻塞了,可是又没有释放锁,那么其余线程就只能一直等待,别无他法。这会极大影响程序执行效率。所以,就须要有一种机制能够不让等待的线程一直无期限地等待下去(好比只等待必定的时间 (解决方案:tryLock(long time, TimeUnit unit)) 或者 可以响应中断 (解决方案:lockInterruptibly())),这种状况能够经过 Lock 解决。
Case 2 :
咱们知道,当多个线程读写文件时,读操做和写操做会发生冲突现象,写操做和写操做也会发生冲突现象,可是读操做和读操做不会发生冲突现象。可是若是采用synchronized关键字实现同步的话,就会致使一个问题,即当多个线程都只是进行读操做时,也只有一个线程在能够进行读操做,其余线程只能等待锁的释放而没法进行读操做。所以,须要一种机制来使得当多个线程都只是进行读操做时,线程之间不会发生冲突。一样地,Lock也能够解决这种状况 (解决方案:ReentrantReadWriteLock) 。
Case 3 :
咱们能够经过Lock得知线程有没有成功获取到锁 (解决方案:ReentrantLock) ,但这个是synchronized没法办到的。
上面提到的三种情形,咱们均可以经过Lock来解决,但 synchronized 关键字却无能为力。事实上,Lock 是 java.util.concurrent.locks包 下的接口,Lock 实现提供了比 synchronized 关键字 更普遍的锁操做,它能以更优雅的方式处理线程同步问题。也就是说,Lock提供了比synchronized更多的功能。可是要注意如下几点:
1)synchronized是Java的关键字,所以是Java的内置特性,是基于JVM层面实现的。而Lock是一个Java接口,是基于JDK层面实现的,经过这个接口能够实现同步访问;
2)采用synchronized方式不须要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完以后,系统会自动让线程释放对锁的占用;而 Lock则必需要用户去手动释放锁,若是没有主动释放锁,就有可能致使死锁现象。

关于读写锁:http://www.javashuo.com/article/p-dvzzqrip-md.html

分布式事务(不一样系统之间如何保证数据的一致性(A系统写入数据,B系统由于某些缘由没有写入成功,形成数据不一致))

关于分布式事物我看了有一篇博文感受写的很好,这里我就引用他的地址:

http://www.javashuo.com/article/p-vcwshiyv-dk.html

安全性问题(数据篡改(拿到别人的URL,篡改数据(金额)发送给系统))

  • 方法一:对插入的操做进行校验:一个请求的URL传入进来,根据参数找到对应的用户关联表,查询到用户的userid和用户登陆后保存到redis中的userid进行对比。例如:传入参数为(订单id)和(优惠券id),拿(订单id)查询该订单的用户id,拿来和登陆的用户id进行对比,判断是否为本人操做。拿(优惠券id)查询用户表是否领取了该优惠券,该优惠券是否可用。
  • 方法二:前端传入一个加密的信息数据,后端给这个给这个数据解密,判断是否为同一用户。例如:将用户id+项目id+密钥生成一个token,传入后端解密,拿到用户id,项目id,密钥对比是否一致
  • 方法三:权限框架:能够指定某些角色,用户的登陆名称密码正确才能够访问,修改。例如:1.Spring Security  2.apache shiro

索引使用的限制条件,sql优化有哪些,数据同步问题(缓存和数据库),缓存优化

索引使用的限制条件,sql优化有哪些

a,选取最适用的字段:在建立表的时候,为了得到更好的性能,咱们能够将表中字段的宽度设得尽量小。另一
个提升效率的方法是在可能的状况下,应该尽可能把字段设置为NOTNULL,
b,使用链接(JOIN)来代替子查询(Sub-Queries)
c,使用联合(UNION)来代替手动建立的临时表
d,事物:
    a)要么语句块中每条语句都操做成功,要么都失败。换句话说,就是能够保持数据库中数据的一致性和完整
性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操做失败,那么,ROLLBACK命令就能够
把数据库恢复到BEGIN开始以前的状态。
    b) 是当多个用户同时使用相同的数据源时,它能够利用锁定数据库的方法来为用户提供一种安全的访问方
式,这样能够保证用户的操做不被其它的用户所干扰。
e,减小表关联,加入冗余字段
f,使用外键:锁定表的方法能够维护数据的完整性,可是它却不能保证数据的关联性。这个时候咱们就能够使用外键。
g,使用索引
h,优化的查询语句
i,集群
j,读写分离
k,主从复制
l,分表
m,分库
o,适当的时候能够使用存储过程

限制:尽可能用全职索引,最左前缀:查询从索引的最左前列开始而且不跳过索引中的列;索引列上不操做,范围之
后全失效; 不等空值还有OR,索引影响要注意;like以通配符%开头索引失效会变成全表扫描的操做,字符串不
加单引号索引失效

数据同步问题(缓存和数据库),缓存优化

1.下降后端负载:对于高消耗的SQL:join结果集、分组统计结果;对这些结果进行缓存。
2.加速请求响应
3.大量写合并为批量写:如计数器先redis累加再批量写入DB
4.超时剔除:例如expire
5.主动更新:开发控制生命周期(最终一致性,时间间隔比较短)
6.缓存空对象
7.布隆过滤器拦截
8.命令自己的效率:例如sql优化,命令优化
9.网络次数:减小通讯次数
10.下降接入成本:长连/链接池,NIO等。
11.IO访问合并
目的:要减小缓存重建次数、数据尽量一致、减小潜在危险。
解决方案:
1.互斥锁setex,setnx:
若是 set(nx 和 ex) 结果为 true,说明此时没有其余线程重建缓存,那么当前线程执行缓存构建逻辑。
若是 setnx(nx 和 ex) 结果为 false,说明此时已经有其余线程正在执行构建缓存的工做,那么当前线程将休
息指定时间 ( 例如这里是 50 毫秒,取决于构建缓存的速度 ) 后,从新执行函数,直到获取到数据。

2永远不过时:
热点key,无非是并发特别大一级重建缓存时间比较长,若是直接设置过时时间,那么时间到的时候,巨大的访
问量会压迫到数据库上,因此要给热点key的val增长一个逻辑过时时间字段,并发访问的时候,判断这个逻辑
字段的时间值是否大于当前时间,大于了说明要对缓存进行更新了,那么这个时候,依然让全部线程访问老的
缓存,由于缓存并无设置过时,可是另开一个线程对缓存进行重构。等重构成功,即执行了redis set操做
以后,全部的线程就能够访问到重构后的缓存中的新的内容了

从缓存层面来看,确实没有设置过时时间,因此不会出现热点 key 过时后产生的问题,也就是“物理”不过时。
从功能层面来看,为每一个 value 设置一个逻辑过时时间,当发现超过逻辑过时时间后,会使用单独的线程去构建缓存。

一致性问题:
1.先删除缓存,而后在更新数据库,若是删除缓存失败,那就不要更新数据库,若是说删除缓存成功,而更新
数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。
2.先去缓存里看下有没有数据,若是没有,能够先去队列里看是否有相同数据在作更新,发现队列里有一个请
求了,那么就不要放新的操做进去了,用一个while(true)循环去查询缓存,循环个200MS左右再次发送到
队列里去,而后同步等待缓存更新完成。

初始化Bean对象有几个步骤,它的生命周期

以前写过,这里就给一个地址:https://blog.csdn.net/java_wxid/article/details/84391519

。。。。。(待完善中)

 

关于面试答案说明:这里的答案我后面慢慢补,大家先看着,若是以为本身技术能力强的能够在评论下方留言,尽可能精简语言将知识点扩展多些,合适的我会采用

关于背面试题说明:对于上面的面试题其实都是一些比较常见的,高频率的题目,能回答上来的有不少人,我相信你是能够作到的,但你聊的真的足够深刻吗?讲解的真的够全面吗?拿下面第一题来讲,面试官通常都直接问你HashMap实现原理,可是要是换一个问法,好比:影响HashMap性能有哪些因素?HashMap为何存取效率那么高?若是只是死记硬背总有那么几道题达不上来吧,相信大部分面试者就只会讲一些在网上找到的答案,没有扩展,面试官听到你的回答,其实他已经听过不少遍了,讲出花来在面试官耳朵里也就那么回事,你拿什么和别人拉开差距,全部请不要死记硬背。

关于薪资方面说明:即使你真的能讲的很细,很全,有时也并非全部地区,全部时间段都能拿到16k,行情是一直都在变化的,这里我所说的16k,仅在上海地区,而且2019年年末面试所了解到的,其余地区面试状况如何本人并不清楚,年后疫情薪资方面,我看了确实有所下滑,要求很提升了不少,因此不用一直在评论下方说什么,会背的人太多了,拿不了16k;像我学了半年的大部分能回答出来能够拿多少?都是开源的东西,能值16K,你别闹了;我在这里吐槽一句:能拿多少是以本身的能力为标准的,但影响你薪资的,不只仅只是能力,时间,地点,运气等等,各方面因素都有的。(我有个朋友,前二年月薪拿32k呢,如今薪资都降到26了)

关于工做说明:能拿到16k,说明你已经具有了java中级开发的能力了,这一阶段已经不局限于CRUD了,已经能够独立负责一个模块开发,有必定的性能优化能力了,而不是只要面试过关就能够的,你的编码能力,独立开发的能力,对业务理解的能力,和团队沟通的能力要达到相应的水平。

这篇博文仅为参考,背景于2019年年末,上海部分地区。总结:能力多少靠本身,薪资多少看命运。