1、说下HashMap底层是数组+链表如何通过key-value的形式存储数据的?
https://blog.csdn.net/weixin_44460333/article/details/86770169
2、线程池的参数?参数的意思?10个线程,5条核心线程,有哪些线程一直在队列中?
一、corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会 被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
二、maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池后,首先会缓存到工作队列(后面会介绍)中,如果工作队列满了,则会创建一个新线程,然后从工作队列中的取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize来指定。
三、keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
四、unit 空间线程存活时间单位
keepAliveTime的计量单位
五、workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
六、threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
七、handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:
①CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
②AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
③DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
④DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
核心线程满了,接下来进队列,队列也满了,创建新线程,直到达到最大线程数,之后再超出,会进入拒绝rejectedExecution
3、Spring 如何解决循环依赖的问题?
Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或zh属性是可以延后设置的(但是构造器必须是在获取引用之前)。
Spring的单例对象的初始化主要分为三步:
①:createBeanInstance:实例化,其实也就是 调用对象的构造方法实例化对象
②:populateBean:填充属性,这一步主要是多bean的依赖属性进行填充
③:initializeBean:调用spring xml中的init() 方法。
从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。
那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。
https://blog.csdn.net/qq_36381855/article/details/79752689
4、说说队列的数据结构?使用场景?
5、说说重入锁ReentrantLock?对于静态方法与非静态方法使用关键字synchronized?
简单来讲,就是增强版synchronized,在jdk1.5的时候,ReentranceLock要比synchronized在性能上增强很多,从jdk1.6开始,由于synchronized做了很多优化,基本和ReentrantLock性能持平,《深入理解jvm虚拟机》上建议1.6以后优先使用synchronized。
a.重入:
对于synchronized来讲,被synchronized锁定的资源(代码块),线程A,只有执行完毕,才能释放锁,对于长时间的同步块来讲,其他线程线程B在很长时间内都得不到执行,如果B想中断自己,在synchronized锁定下,这种情况B不会中断。
ReentranceLock之所以可以重入,是因为,线程在执行的过程可以中断,对于某些等待时间较长的同步块,可以中断,而转去执行其他的操作。
引申:synchronized可能会产生死锁,ReentranceLock可以中断死锁。
b.公平锁
公平锁:公平意思是,按照申请锁的时间,先来先得,有序。
synchronized是非公平锁,优先级高的线程可以优先得到锁。
ReentranceLock默认也是非公平锁,可以通过构造函数实现公平锁
c.多条件
ReentranceLock可以同时绑定多个Condition,
synchronized只能绑定一个Condition,也就是锁定的对象。
synchronized静态同步方法,由于静态方法优先于对象存在,所以锁是该类的字节码对象(.class文件)
synchronized非静态同步方法,锁是该类对象或this;
6、说说什么是微服务?单体服务和微服务的优缺点?
微服务一种系统架构的设计风格,主旨是将原本复杂的系统拆分成多个独立的小型服务,每个服务维护着自身的业务逻辑、数据处理以及部署,服务和服务之间通过简单的通信协议进行通信(比如说Restful API),不要求每个微服务使用同一种变成语言编写。
传统的单体应用:
面临的问题
1项目过度复杂:比如原本一个很简单的单体应用,经过不断的拓展,功能越来越多,经手的人也在不断的变化,可能到最后已经变成一个非常复杂的大系统了,而且这种复杂情况还会越来越严重。
2开发速度缓慢
因为单体应用复杂了之后,项目会越来越臃肿而且庞大,每一次编译构建运行测试,都要花费大量的时间和精力,而且如果测试有问题,又要重新来一次,有种牵一发而动全身的操作。
3不易拓展
比如部署的时候需要的服务器性能要满足所有功能的要求,比如内存,CPU等
4技术栈不易拓展
项目开始的时候一旦选择了某一种技术栈来开发项目,那么以后就很难在技术栈上做切换。有时候因为某一个功能需要改新的技术去做,那么这是很难去重新改造这个模块的。
曾经的优势
1开发简单,一个IDE可以很快速的就构建出一个单体应用
2测试简单
3部署简单,一个tomcat安装之后把应用放上去就可以了
4集群化部署也很容易,多个tomcat + 一个Nginx就可以搭建好集群环境了
微服务的优势
1大项目可以持续交付
微服务将一个大项目拆分成很多个互相独立的服务,每一个服务都可以由一个团队去完成,并且配备自己的开发和部署,各个团队开发的微服务相互独立,互不干扰。每一个小项目都可以非常方便的进行测试部署,不会牵一发而动全身。
2易于维护
一个传统的单体项目,刚接手的时候不一定能很快的理清头绪,但是微服务由于比较小巧,一个微服务只负责一个功能,能很快的理出头绪上手开发。并且微服务的规模都比较小,项目启动和测试速度都会比较快。
3服务可以可以独立拓展
独立拓展可以让我们充分的使用硬件资源。单体应用所有的功能都写在一起,有的模块可能需要内存更大,有的模块需要CPU的运算更大,部署的时候,我们只能选择CPU更强内存更大的机器,而如果是微服务架构,不同的系统独立部署,压力大的时候进行独立的集群化部署,这些操作都不会影响到其他的服务,非常方便灵活。
4更强的兼容性
由于每一个微服务都是独立运行的,处理的当,我们在微服务架构中可以实现更好的故障隔离,当一个微服务发生问题时,不会影响到其他的功能服务。
5可以灵活的采用最新的技术
因为微服务都是独立运行的,单个微服务的技术升级非常简单和容易,
微服务的弊端
1服务的拆分
2分布式系统带来的挑战
主键如何产生、如何熔断、分布式事务如何处理、数据如何同步等一系列问题
3多个研发团队的协调管理
不同的团队研发不同的微服务,那么就需要协调多个团队共同配合,才能做好微服务的开发工作,这堆项目管理提出了挑战。
7、dubbo都有哪些角色组成?Container的作用?如何理解服务治理?
服务容器 Container 负责启动,加载,运行服务提供者。
服务提供者Provider在启动时,向注册中心注册自己提供的服务。
服务消费者Consumer在启动时,向注册中心订阅自己所需的服务。
注册中心Registry返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者Consumer,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务治理:集群、路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等手动配置
8、@SpringBootApplication注解是由哪些注解组成的?
@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan
9、SpringBoot和SpringCloud的不同?
SpringBoot是一个框架,一种全新的编程规范,他的产生简化了框架的使用,所谓简化是指简化了Spring众多框架中所需的大量且繁琐的配置文件,所以 SpringBoot是一个服务于框架的框架,服务范围是简化配置文件。(starter快速整合三方框架、去xml配置注解开发、内置Tomcat和netty服务)
Spring Cloud是一个微服务框架,相比Dubbo等RPC框架, Spring Cloud提供的全套的分布式系统解决方案
SpringCloud 的核心组件:服务发现——Netflix Eureka;客服端负载均衡——Netflix Ribbon;断路器——Netflix Hystrix;服务网关——Netflix Zuul;分布式配置——Spring Cloud Config
9、说说SpringBoot内置netty服务?
10、Spring有哪些主要模块?
Spring有七大功能模块,分别是Spring Core,AOP,ORM,DAO,MVC,WEB,Context。
Spring Core:Core是Spring的核心类库,Spring的所有功能都依赖于该类库,Core主要实现IOC功能,Sprign的所有功能都是借助IOC实现的。
AOP:AOP模块是Spring的AOP库,提供了AOP(拦截器)机制,并提供常用的拦截器,供用户自定义和配置。
ORM:Spring 的ORM模块提供对常用的ORM框架的管理和辅助支持,Spring支持常用的Hibernate,ibtas,jdao等框架的支持,Spring本身并不对ORM进行实现,仅对常见的ORM框架进行封装,并对其进行管理
DAO模块:Spring 提供对JDBC的支持,对JDBC进行封装,允许JDBC使用Spring资源,并能统一管理JDBC事物,并不对JDBC进行实现。(执行sql语句)
WEB模块:WEB模块提供对常见框架如Struts1,WEBWORK(Struts 2),JSF的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器。
Context模块:提供框架式的Bean访问方式,其他程序可以通过Context访问Spring的Bean资源,相当于资源注入。
MVC模块:为Spring提供了一套轻量级的MVC实现,在Spring的开发中,我们既可以用Struts也可以用Spring自己的MVC框架,相对于Struts,Spring自己的MVC框架更加简洁和方便
11、Spring 常用的注入方式有哪些?
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入
12、Spring事务实现方式有哪些?
(1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
(2)基于 TransactionProxyFactoryBean的声明式事务管理
(3)基于 @Transactional 的声明式事务管理
(4)基于Aspectj AOP配置事务
13、spring切面和切点的定义?如何使用的?
- Before——在方法调用之前调用通知
- After——在方法完成之后调用通知,无论方法执行成功与否
- After-returning——在方法执行成功之后调用通知
- After-throwing——在方法抛出异常后进行通知
- Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
使用:
package com.yidongxueyuan.spring.pojo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.springframework.stereotype.Component;
@Component("myAspectAnto")
//告知spring 该类是一个切面类:
@Aspect
public class MyAspectAnto {
@Pointcut(value="execution ( * com.yidongxueyuan.spring.dao.impl.*.*(..))")
private void point1() {}
/*@Before(value="point1()")
public void checkPri() {
System.out.println("权限的校验");
}
@AfterReturning(value="point1()")
public void logger() {
System.out.println("日志的记录: ");
}
@AfterThrowing(value="point1()",throwing="e")
public void afterThrowing(Throwable e) {
System.out.println("异常抛出通知。。。"+e.getMessage());
}
@After(value="point1()")
public void after() {
System.out.println("最终通知。。。");
}*/
//环绕通知;
@Around(value="point1()")
public Object around(ProceedingJoinPoint joinPoint) {
Object obj=null;
try {
System.out.println("前置通知1。。。");
obj = joinPoint.proceed();
System.out.println("后置通知1。。。");
} catch (Throwable e) {
System.out.println("异常抛出通知1。。。");
e.printStackTrace();
} finally {
System.out.println("最终通知1");
}
return obj;
}
}
14、SpringMVC工作流程描述?
1. 用户向服务器发送请求,请求被SpringMVC 前端控制DispatcherServlet捕获;
2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
7. ViewResolver 结合Model和View,来渲染视图
8. 将渲染结果返回给客户端。
15、springMVC的注解?rest风格
@Controller:
用于定义控制器类
@ResponseBody:
表示方法的返回结果直接写入HTTP response body中
@PathVariable:
获取路径参数
@RquestParam:
用在方法的参数前面
@RequestBody
@RestController:
是@Controller和@ResponseBody的合集
@RequestMapping:
提供路由信息,负责URL到Controller中的具体函数的映射
@GetMapping:
是@RequestMapping(method = RequestMethod.GET)的缩写。不支持@RequestMapping的自定义属性。
@PostMapping:
是@RequestMapping(method = RequestMethod.POST)的缩写。不支持@RequestMapping的自定义属性。
@ControllerAdvice:统一处理异常。
@ExceptionHandler:用在方法上表示遇到这个异常就执行以下方法。
Controller层的全局异常统一处理
@RequestMapping("/restfulTest")
@Controller
public class RestTestHandler {
private static final String SUCCESS="success";
/**
* 1. 使用 @RequestMapping 注解来映射请求的 URL
* 2. 返回值会通过视图解析器解析为实际的物理视图, 对于 InternalResourceViewResolver 视图
解析器, 会做如下的解析:
* 通过 prefix + returnVal + 后缀 这样的方式得到实际的物理视图, 然会做转发操作
*
* /WEB-INF/views/success.jsp
*
* @return
*/
@RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE)
public String testDelete(@PathVariable Integer id){
System.out.println(" TestRest DELETE:"+id);
return SUCCESS;
}
@RequestMapping(value="/testRest/{id}",method=RequestMethod.PUT)
public String testPut(@PathVariable Integer id){
System.out.println(" TestRest PUT:"+id);
return SUCCESS;
}
@RequestMapping(value="/testRest",method=RequestMethod.POST)
public String testPost(){
System.out.println(" TestRest POST:");
return SUCCESS;
}
@RequestMapping(value="/testRest/{id}",method=RequestMethod.GET)
public String testRest(@PathVariable Integer id){
System.out.println(" TestRest Get:"+id);
return SUCCESS;
}
}
16、Spring自动装配?
一、@Autowired
二、@Resource
三、@Inject
1.@Autowired:自动注入
(1)、默认优先按照类型去容器中寻找相应的组件
applicationContext.getBean(BookDao.class);
(2)、如果找到多个类型相同的组件,再将属性的名称作为组件的id去容器中寻找
applicationContext.getBean("bookDao);
(3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
(4)、自动装配默认属性要装好,否则会报错
可以使用@Autowired(required=false);
(5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean
2.@Resuorce:JSR250
(1)、可以和@Autowired一样进行自动装配
public class BookService{
@Resource
private BookDao bookDao
}
(2)、默认按照组件名称进行装配,设置name="bookDao2",可以指定名称
(3)、不支持@Primary以及@Autowired(required=false)
3.@Inject:JSR330
(1)在maven中查找依赖javax.inject,添加依赖进入工程
(2)能支持@Autowired的各种特性,但需要添加依赖,不支持required=false
4.总结
(1)@Autowired是Spring定义的,其余两个都是JAVA规范
(2)完成解析自动装配的是实现了BeanPostProcesser的AutowiredAnnotationBeanPostProcesser
17、@Autowired 与@Resource的区别?
@Resource的作用相当于@Autowired,只不过@Autowired按类型(byType)自动注入,而@Resource默认按名称(byName)自动注入。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Resource装配顺序
1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
18、spring security的了解?
https://blog.csdn.net/qq_32734365/article/details/81413236
19、Shiro的使用?
Shiro的 SecurityManager
https://blog.csdn.net/mxw2552261/article/details/79674871