Java面试——开源框架知识
一、简单讲讲 Tomcat结构,以及其类加载器流程,线程模型等
【1】模块组成结构:Tomcat 的核心组件就 Connector 和 Container,一个Connector+一个Container(Engine)构成一个Service,Service就是对外提供服务的组件,有了Service组件Tomcat就能对外提供服务了,但是光有服务还不行,还需要有环境让你提供服务才行,所以最外层的Server就是为Service提供了生存的土壤。
【3】类加载器流程:Tomcat启动时,会创建以下 4种类加载器:
1)、Bootstrap 引导类加载器:加载JVM启动所需的类,以及标准扩展类(位于jar/lib/ext上)
2)、System 系统类加载器:加载Tomcat启动时的类,比如bootstrap.jar通常在catalina.bat或者catalina.sh中指定。指定位置位于CATALINA_HOME/bin下。
【4】线程模型:支持以下四种线程模型。
【主要介绍,NIO线程模型】:Poller组件在非阻塞的模式下轮询多个客户端连接,不断检测,处理各种事件。例如检测各个连接是否可读,对于可读的客户端连接尝试进行读取和解析消息报文。
二、Tomcat 如何调优,涉及哪些参数
【1】Tomcat调优主要从四个方面考虑:1)、吞吐量。2)、Responsetime。 3)、Cpuload。 4)、MemoryUsage。
【2】参数调优:1)、Tomcat启动参数的优化:Tomcat 的启动参数位于tomcat的安装目录\bin目录下,如果你是Linux操作系统就是catalina.sh文件,如果你是Windows操作系统那么你需要改动的就是catalina.bat文件。
✔ Linux系统中catalina.sh文件中添加如下参数(重要参数随后说明):
✔ Windowns系统中catalina.bat文件中添加如下参数(重要参数随后说明):
◀ -Server(重要):只要Tomcat是运行在生产环境中,这个参数必须添加。因为Tomcat默认是java-client模式运行,添加server后表示以真实的production的模式运行,将拥有更大、更高的并发处理能力,更快、更强的JVM垃圾回收机制,可以获得更多的负载和吞吐量等等。
◀ -Xms -Xmx:既JVM内存设置了,把Xms与Xmx两个值设成一样是最优的做法。(否则当内存=Xmx向Xms变化时,CPU高速运转触发垃圾回收机制,严重时会导致系统‘卡壳’,因此一开始我们就把这两个设成一样,使得Tomcat在启动时就为最大化参数充分利用系统的效率。)
※在设这个最大内存即Xmx值时请先打开一个命令行:能够正常显示JDK的版本信息,说明这个值能够用。
▶ URIEncoding=”UTF-8”:使得tomcat可以解析含有中文名的文件的url。
▶ minSpareThreads:最小备用线程数,tomcat启动时的初始化的线程数。
▶ maxSpareThreads:如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。
▶ connectionTimeout:网络连接超时时间毫秒数。
▶ maxThreads:Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,即最大并发数。
▶ acceptCount:当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection。
▶ maxProcessors与minProcessors:在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。 通常Windows是1000个左右,Linux是2000个左右。
▶ useURIValidationHack:设成"false",可以减少它对一些url的不必要的检查从而减省开销。
▶ enableLookups:设置为"false",主要为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值
▶ disableUploadTimeout:允许Servlet容器,正在执行使用一个较长的连接超时值,以使Servlet有较长的时间来完成它的执行,默认值为false
▶ 给Tomcat配置gzip压缩(HTTP压缩)功能:HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程HTML、CSS、Javascript、Text ,它可以节省40%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP、JSP、ASP、 Servlet、SHTML等输出的网页也能进行压缩,压缩效率惊人。
1)、compression="on" 打开压缩功能
2)、compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认为2KB
3)、noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩
4)、compressableMimeType="text/html,text/xml" 压缩类型
▶ redirectPort: 如果我们走https协议的话,我们将会用到8443端口这个段的配置。
三、讲讲 Spring加载流程
初始化环境—>加载配置文件—>实例化Bean—>调用Bean显示信息
四、Spring AOP的实现原理
AOP(Aspect-OrientedProgramming,面向方面编程):是OOP的补充和完善。OOP引入了封装、继承、多态性等建立一种对象层次结构(从上到下的关系)。当需要为分散的对象引入公共行为的时候(从左到右的关系),OOP就显得无能为力。例如:日志功能。日志代码往往水平的散步所有对象层次中,与对象的核心功能毫无关系。这种代码被称为横切(cross-cutting)代码还有像安全性、异常处理、透明的持续性等都称为横切代码。在OOP设计中,它们导致了大量代码的重复,不利于模块的重用。
AOP 与 OOP相反,利用“横切”技术将影响多个类的公共行为封装到一个可重用模块,称为 Aspect。简单点,就是将那些与业务无关,却被业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
Spring 提供了两种方式生成代理对象:JDKProxy 和 Cglib具体使用哪种方式生成由 AopProxyFactory根据 AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。下面我们来研究一下 Spring如何使用 JDK来生成代理对象,具体的生成代码放在 JdkDynamicAopProxy这个类中:
切面植入:我们都知道 InvocationHandler是 JDK动态代理的核心,生成代理对象的方法,都会委托到 InvocationHandler.invoke()方法。而通过 JdkDynamicAopProxy
五、讲讲 Spring事务的传播属性
【1】PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。(常见的选择)比如ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED,那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。
【2】PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
【3】PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
【4】PROPAGATION_REQUIRES_NEW:支持当前事务,如果当前没有事务,就将当前事务挂起。如 ServiceA.methodA的事务级别为 PROPAGATION_REQUIRED,ServiceB.methodB 的事务级别为 PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB 的时候,ServiceA.methodA 所在的事务就会挂起,ServiceB.methodB 会起一个新的事务,等待ServiceB.methodB 的事务完成以后,A才继续执行。他与 PROPAGATION_REQUIRED的事务区别在于事务的回滚程度了。因为ServiceB.methodB 是新起一个事务,那么就是存在两个不同的事务。如果 ServiceB.methodB已经提交,那么 ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果 ServiceB.methodB失败回滚,如果他抛出的异常被 ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
【5】PROPAGATION_NOT_SUPPORTED:以非事务方式执行当前操作,如果当前存在事务,就把事务挂起来。
【6】PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛异常。
【7】PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,关键是savepoint。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。与PROPAGATION_REQUIRES_NEW的区别是NESTED的事务和他的父事务是相依的,它的提交是要等父事务一块提交。也就是说,如果父事务最后回滚,它也要回滚。
六、Spring 如何管理事务的
Spring 事务管理主要包括3个接口,Spring 事务主要由以下三个共同完成的:
【1】PlatformTransactionManager:事务管理器,主要用于平台相关事务的管理。主要包括三个方法:①、commit:事务提交。②、rollback:事务回滚。③、getTransaction:获取事务状态。
【2】TransacitonDefinition:事务定义信息,用来定义事务相关属性,给事务管理器 PlatformTransactionManager使用这个接口有下面四个主要方法:①、getIsolationLevel:获取隔离级别。②、getPropagationBehavior:获取传播行为。③、getTimeout获取超时时间。④、isReadOnly:是否只读(保存、更新、删除时属性变为false--可读写,查询时为true--只读)事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。
【3】TransationStatus:事务具体运行状态,事务管理过程中,每个时间点事务的状态信息。例如:①、hasSavepoint():返回这个事务内部是否包含一个保存点。②、isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚。③、isNewTransaction():判断当前事务是否是一个新事务。
七、Spring 怎么配置事务(具体说出一些关键的xml 元素)
【1】配置事务的方法有两种:1)、基于 XML的事务配置。2)、基于注解方式的事务配置。
【铺垫】:1)、spring的事务管理是通过Aop的方式来实现。2)、声明式事务是spring对事务管理的最常用的方式,因为这种方式对代码的影响最小,因此也就符合非侵入式的轻量级的容器的概念;
【基于XML的事务配置】:
【基于注解方式的事务配置】 : @Transactional:直接在Java源代码中声明事务的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。
♣ 注解事务,只需要在 XML配置中配置一句就可以了,如下:
♣ 主要在类中定义事务注解 @Transactional,如下:
八、说说你对 Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理?
【Spring的理解】:【1】Spring 是一个开源框架,主要是为简化企业级应用开发而生。可以实现 EJB可以实现的功能,Spring是一个 IOC和 AOP容器框架。
♧ 控制反转(IOC):Spring 容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。
♧ 依赖注入(DI):Spring 使用 Java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。
♧ 面向切面编程(AOP):在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
【2】在 Spring中,所有管理的都是 JavaBean对象,而 BeanFactory和 ApplicationContext就是 Spring框架的那个 IOC容器,现在一般使用 ApplicationContext,其不但包括了 BeanFactory的作用,同时还进行了更多的扩展。
【非单例注入原理】:在大部分情况下,容器中的 bean都是 singleton类型的。如果一个 singleton bean要引用另外一个singleton bean或者一个非 singleton bean要引用另外一个非 singleton,通常情况下将一个 bean定义为另一个 bean的 property值就可以了。不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个 singleton类型 bean A的某个方法时,需要引用另一个非 singleton(prototype)类型的 bean B,对于 bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A 提供一个新的的bean B实例。
【注入原理】:链接
【生命周期】:链接
【循环注入原理】:链接
九、SpringMVC 中 DispatcherServlet初始化过程
【参考博客】:链接
十、Netty 的线程模型,Netty 如何基于 Reactor模型上实现的
【参考博客】:链接
十一、为什么选择 Netty
【参考博客】:链接
十二、什么是 TCP粘包,拆包。解决方式是什么
【参考博客】:链接
十三、Netty的 fashwheeltimer的用法,实现原理,是否出现过调用不够准时,怎么解决
【参考博客】:链接
十四、Netty 的心跳处理在弱网下怎么办
【参考博客】:链接
十五、Netty 的通讯协议是什么样的
【参考博客】:链接
十六、SpringMVC 用到的注解,作用是什么,原理
【参考博客】:链接
十七、SpringBoot 启动机制
【参考博客】:链接